Angularを愛するのをやめた方法

エントリー


私は長年AngularJSで働いていましたが、今日まで本番環境で使用しています。 歴史的に開発されたアーキテクチャのために理想とは言えないという事実にもかかわらず、JSフレームワークだけでなくWeb全体の進化のマイルストーンになったと主張する人は誰もいません。


ヤードでは2017年であり、新しい製品/プロジェクトごとに、開発用のフレームワークを選択するという疑問が生じます。 長い間、私は新しいAngular 2/4(以下、単にAngularと呼ぶ)が今後数年先の企業開発の主流になると確信していました。


今日、私自身は次のプロジェクトで使用することを拒否しています。


免責事項 :この記事はあくまで主観的なものですが、これは私の個人的な見解であり、企業レベルのアプリケーションの開発に関するものです。


Angularjs



長年の進化により、フレームワークの欠点のほとんどが解消され、図書館は非常に安定した状態になり、コミュニティの規模は無限になりつつあります。 おそらく、最初の格納庫では、何百もの既存のアプリケーションを壊さずに大幅に改善できるものはほとんどないと言えるでしょう。


多大な作業とフレームワークの全体的な高速化(特に1.5以降)にもかかわらず、
その主な欠点は、私はまだ速度を呼び出します。 もちろん、これは長年にわたって、何らかの形で下位互換性がまだサポートされていることを考えると、許されます。


角度



そして最後に、Angularはゼロから書き直され、将来の多くのWebアプリケーションの主要なものになりました。


もちろん、これまでの道のりは長く、重大な変更に満ちていましたが、現在、Angular 4は安定しており、完全に生産準備が整っています。


新しいAngularが私たちに与えた最もクールなものの1つは、TypeScriptの宣伝です。
個人的には、彼が私のお気に入りのフレームワークの主要なフレームワークになる前から彼を知っており、働いていましたが、Angularのおかげで多くの人が彼について知りました。


TypeScript



TypeScriptについて詳しくは説明しません。 これは別の記事のトピックです。
すでに必要以上に彼について書かれています。 しかし、エンタープライズ開発の場合、TypeScriptには大きな利点があります。 最も静的なタイピングとスコープで始まり、IE9でもES7 / 8サポートで終わります。


TypeScriptを使用する主な利点は、豊富なツールキットと優れたIDEサポートです。 私たちの経験では、TSを使用した単体テストの記述は大幅に少なくなります。


Vue



この記事を読んでいる場合、95%の確率で既にそれが何であるかを知っています。


しかし、まだ知らない5%の人々にとって、Vue.jsは非常に軽量な(ただし機能が非常に豊富な)フレームワークであり、AngularJSとReactの両方から多くの優れた機能が組み込まれています。


実際、Reactに似ていますが、テンプレートはAngularJS(HTML + Moustache)とほとんど同じです。


実際には、AngularJSとは著しく異なりますが、表面的な意味では、
ReactまたはAngularJSの経験があれば、その仕組みを理解するのは簡単です。


背景


これはAngularJSの大きなプロジェクトでした


最近実動で出てきた私の最後のプロジェクトは、AngularJS 1.5-1.6で書きました。


Angularの安定したバージョンが長い間利用可能であったにもかかわらず、多くの理由で(政治的というより技術的ではない)移行しないことに決めました。
しかし、最初から使用していた最新のWebの機能の1つはTypeScriptです。


このプロジェクトのコンポーネントの例を次に示します。


import {Component} from "shared-front/app/decorators";
import FileService, {ContentType, IFile} from "../file.service";
import AlertService from "shared/alert.service";

@Component({
    template: require("./file.component.html"),
    bindings: {
        item: "<",
    },
})
export default class FileComponent {
    public static $inject = ["fileService"];
    public item: IFile;

    constructor(private fileService: FileService, private alertService: AlertService) {
    }

    public isVideo() {
        return this.item.contentKeyType === ContentType.VIDEO;
    }

    public downloadFile() {
        this.fileService.download(this.getFileDownloadUrl()).then(() => {
            this.alertService.success();
        });
    }

    private getFileDownloadUrl() {
        return `url-for-download${this.item.text}`;
    }
}

, , TS.
Unit-, 2.


AngularJS , React, , .


, AngularJS. , .


— Angular


, Angular 2 ( 4) .


, , . , alpha-RC 0. .


, , .


Angular:


import {Component} from '@angular/core';

import FileService, {ContentType, IFile} from "../file.service";
import AlertService from "shared/alert.service";

@Component({
  selector: 'app-file',
  templateUrl: './file.component.html',
  styleUrls: ['./file.component.scss']
})
export class FileComponent {

    Input() item: IFile;

    constructor(private fileService: FileService, private alertService: AlertService) {
    }

    public isVideo() {
        return this.item.contentKeyType === ContentType.VIDEO;
    }

    public downloadFile() {
        this.fileService.download(this.getFileDownloadUrl()).subscribe(() => {
            this.alertService.success();
        });
    }

    private getFileDownloadUrl() {
        return `url-for-download${this.item.text}`;
    }
}

, .



Angular CLI — AngularJS



, Angular 4 Angular CLI


CLI , // .
, Angular. .


, AngularJS, -. (), , . .


CLI " ", React (create-react-app) Vue (vue-cli). , , .


" Angular"


Angular 2 is terrible ( ).


, , ,
. , .


RxJS, .. .


An Ajax request is singular, and running methods like Observable.prototype.map when there will only ever be one value in the pipe makes no semantic sense. Promises on the other hand represent a value that has yet to be fulfilled, which is exactly what a HTTP request gives you. I spent hours forcing Observables to behave before giving up using Observable.prototype.toPromise to transform the Observable back to a Promise and simply using Promise.all, which works much better than anything Rx.js offers.

, RxJS, , Observable, .


, Object.observe :


After much discussion with the parties involved, I plan to withdraw the Object.observe proposal from TC39 (where it currently sits at stage 2 in the ES spec process), and hope to remove support from V8 by the end of the year (the feature is used on 0.0169% of Chrome pageviews, according to chromestatus.com).

, Rx — . , , .


TypeScript', , .


, Angular


( , ), .


TypeScript Angular


— , TypeScript' Angular.


.


API


TypeScript Angular API.
TypeScript , . , API, Angular , .


:


HttpParams

- Angular HttpParams .
, , Angular , .


:


let params = new HttpParams();
params.set('param', param);
params.set('anotherParam', anotherParam);
...
this.http.get('test', {params: params});

. ?
, TypeScript Angular .


TypeScript


This class is immuatable — all mutation operations return a new instance.

, .


:


http
  .post('/api/items/add', body, {
    params: new HttpParams().set('id', '3'),
  })
  .subscribe();

RxJS operator import

, Angular Observable , .


RxJS. , Rx , Observable :


// rx.js
Rx.Observable.create();
vs
// Angular
new Observable()

, Rx + TypeScript + Angular.


RxJS , do:


observable.do(event => {...})

, .


, :


ERROR TypeError: observable.do is not a function

( ) :


import 'rxjs/add/operator/do';

, TypeScript? . .


Router API

, API — .


Events

. , , , , . instanceof ( , ):


this.router.events.subscribe(event => {
  if(event instanceof NavigationStart) {
    ...
  }
}


— . - :


this.router.navigate(['/some']);
...
this.router.navigate(['/other']);

?


any[]. TypeScript — .


, — Angular.


AngularJS , enum. , 'some'.


, Angular TypeScript .


Lazy Load

, ,
TypeScript , #


{
  path: 'admin',
  loadChildren: 'app/admin/admin.module#AdminModule',
},

Forms API

— Angular : .


, -.


API reactive forms:


//     ?
//  name   c ??
this.heroForm = this.fb.group({
  name: ['', Validators.required ],
});


//     ??
this.heroForm = this.fb.group({
  name: '', // <--- the FormControl called "name"
});


this.complexForm = fb.group({   
  //   compose ?
  //    null ??
  'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])],
  'gender' : [null, Validators.required],
})

— [disabled] ...


, , API


__metadata


TypeScript' Angular .


— , , __metadata.


__metadata ///, , .


— , .


, AngularJS , @Component:


export const Component = (options: ng.IComponentOptions = {}) => controller => angular.extend(options, {controller});

TypeScript AngularJS .


Angular, , ,
reflect-metadata . .



, TypeScript,
. TS .


Dependency Injection Angular.


, unit . , Java- . , AngularJS , Vue , DI.


Angular , , DI , :


constructor(heroService: HeroService) {
  this.heroes = heroService.getHeroes();
}

TypeScript , , @Inject:


constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}

, @Injectable().


, , , — .


Consider adding @Injectable() to every service class, even those that don't have dependencies and, therefore, do not technically require it.
Here's why:

Future proofing: No need to remember @Injectable() when you add a dependency later.

Consistency: All services follow the same rules, and you don't have to wonder why a decorator is missing.

, ?


:


Always write @Injectable(), not just @Injectable. The application will fail mysteriously if you forget the parentheses.

, , TypeScript Angular .


, .



— Angular. .


, :


<div [ngStyle]="{'color': color, 'font-size': size, 'font-weight': 'bold'}">
  style using ngStyle
</div>

<input [(ngModel)]="color" />

<button (click)="size = size + 1">+</button>

<div [class.required]="isReq">Hello Wordl!</div>  
<div [className]="'blue'">CSS class using property syntax, this text is blue</div>
<div [ngClass]="{'small-text': true, 'red': true}">object of classes</div>
<div [ngClass]="['bold-text', 'green']">array of classes</div>
<div [ngClass]="'italic-text blue'">string of classes</div>

, .


, [] ().


BindingExample
Properties<input [value]="firstName">
Events<button (click)="buy($event)">
Two-way<input [(ng-model)]="userName">

AngularJS.



, two-way binding
:


Visualize a banana in a box to remember that the parentheses go inside the brackets.


Angular , ,
, .


Vue. , , 6 , .. .


View encapsulation


Angular View encapsulation.


Shadow DOM .


Shadow DOM — CSS .
.


Shadow DOM.


CSS :


.first {
  background-color: red;
}
.first .second {
  background-color: green;
}
.first .second .third {
  background-color: blue;
}

Angular :


.first[_ngcontent-c1] {
  background-color: red;
}
.first[_ngcontent-c1]   .second[_ngcontent-c1] {
  background-color: green;
}
.first[_ngcontent-c1]   .second[_ngcontent-c1]   .third[_ngcontent-c1] {
  background-color: blue;
}

.


Vue , :


.first[data-v-50646cd8] {
  background-color: red;
}
.first .second[data-v-50646cd8] {
  background-color: green;
}
.first .second .third[data-v-50646cd8] {
  background-color: blue;
}

, Vue scoped .


, Vue (vue-cli webpack) SASS/SCSS, Angular CLI ng set defaults.styleExt scss. , webpack.


, , .


UI — PrimeNG, :


body .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {
    font-size: 1.1em;
}

, .


:


body :host >>> .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {
  font-size: 2em;
}

!important.


PrimeNG , , Angular.



>>>/deep/ shadow-piercing .


"" Shadow DOM .


Angular ,
, /deep/ >>>.


, .
, ::ng-deep — shadow-piercing Angular .


(4.2.6 -> 4.3.0), ( NPM ^).


, ChangeLog Angular 4, . — .


::ng-deep . , PrimeNG, .


: — Shadow DOM .


HTML


, , Angular HTML . .


Angular , , AngularJS HTML () .


AngularJS : <my-component/> .
//.


— .


.


,
CUSTOM_ELEMENTS_SCHEMA -



...
const TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
  'base': new HtmlTagDefinition({isVoid: true}),
  'meta': new HtmlTagDefinition({isVoid: true}),
  'area': new HtmlTagDefinition({isVoid: true}),
  'embed': new HtmlTagDefinition({isVoid: true}),
  'link': new HtmlTagDefinition({isVoid: true}),
  'img': new HtmlTagDefinition({isVoid: true}),
  'input': new HtmlTagDefinition({isVoid: true}),
  'param': new HtmlTagDefinition({isVoid: true}),
  'hr': new HtmlTagDefinition({isVoid: true}),
  'br': new HtmlTagDefinition({isVoid: true}),
  'source': new HtmlTagDefinition({isVoid: true}),
  'track': new HtmlTagDefinition({isVoid: true}),
  'wbr': new HtmlTagDefinition({isVoid: true}),
  'p': new HtmlTagDefinition({
    closedByChildren: [
      'address', 'article', 'aside', 'blockquote', 'div', 'dl',      'fieldset', 'footer', 'form',
      'h1',      'h2',      'h3',    'h4',         'h5',  'h6',      'header',   'hgroup', 'hr',
      'main',    'nav',     'ol',    'p',          'pre', 'section', 'table',    'ul'
    ],
    closedByParent: true
  }),
...
  'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
  'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
  'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
  'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
  'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
  'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
  'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
  'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
  'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc'
  ...

,
, webpack , .
JIT .


, AOT .
--aot, , ng serve . ( ).


,
.


AOT , ,


:


don't use default exports :)

Just place both export types and it works

(AOT )


AOT:


@NgModule({
  providers: [
    {provide: SomeSymbol, useFactor: (i) => i.get('someSymbol'), deps: ['$injector']}
  ]
})
export class MyModule {}

:


export factoryForSomeSymbol = (i) => i.get('someSymbol');

@NgModule({
  providers: [
    {provide: SomeSymbol, useFactor: factoryForSomeSymbol, deps: ['$injector']}
  ]
})
export class MyModule {}

, .


Zone.js


Angular Zone.js.
, . :


core.es5.js:1020 ERROR Error: Uncaught (in promise): Error: No clusteredNodeId supplied to updateClusteredNode.
Error: No clusteredNodeId supplied to updateClusteredNode.
    at ClusterEngine.updateClusteredNode (vis.js:47364)
    at VisGraphDataService.webpackJsonp.../../../../../src/app/services/vis-graph-data.service.ts.VisGraphDataService.updateNetwork (vis-graph-data.service.ts:84)
    at vis-graph-display.service.ts:63
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)
    at ClusterEngine.updateClusteredNode (vis.js:47364)
    at VisGraphDataService.webpackJsonp.../../../../../src/app/services/vis-graph-data.service.ts.VisGraphDataService.updateNetwork (vis-graph-data.service.ts:84)
    at vis-graph-display.service.ts:63
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)
    at resolvePromise (zone.js:770)
    at zone.js:696
    at zone.js:712
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)

Zone.js , .


.


, .


- , .



UI frameworks


, — UI . , — -. , UI framework .


, , UI ,
.


UI Angular: https://angular.io/resources ( UI components).


.


Angular Material 2



, Angular Material 2 , Angular .


, , .


, , — . - . .


, Angular Material 2 , , , .. , , . multiple-select, .


.


.


FeatureStatus
treeIn-progress
stepperIn-progress, planned Q3 2017
sticky-headerIn-progress, planned Q3 2017
virtual-repeatNot started, planned Q4 2017
fab speed-dialNot started, not planned
fab toolbarNot started, not planned
bottom-sheetNot started, not planned
bottom-navNot started, not planned

Bootstrap



, Bootstrap
ng2-bootstrap () ngx-bootstrap. , CSS, ( modal, datepicker typeahead).


Prime Faces



. ( Tree Table!).


PrimeFaces .. JSF, . PrimeNG ( ). , , .


.


. — - . , .


, ,
, PrimeNG .


Clarity



— ( ) Clarity vmware.


, , .


UI , CSS .
bootstrap. / .


, ( , , ?).


, datepicker' select2- .
: DatePicker, Select 2.0 ( , , ).


, "Clarity Design System" — Angular
( enterprise ). VMware .


, .



, Angular UI .
?


Angular VMware. enterprise? .


, .


Vue UI frameworks



Vue.js :


Element (~15k stars), Vue Material ( Angular Material 2 ), Vuetify ( Material ), Quasar, iView Muse-UI (iView , ).


, , , Vue .
,
, .


?


Clarity , Angular . , Angular, .


, . Vue.js.


webpack vue-cli . , all-in-one,
Vue , Angular.


, UI framework' .


— TypeScript,
.


Microsoft . webpack .


React? AngularJS Vue,
v-if, v-model v-for .


, , Aurelia ,
Vue .


, - Angular community - , enterprise framework', . , .


, 4 Angular, .
Vue...


: https://medium.com/@igogrek/how-i-stopped-loving-angular-c2935f7378c4



Source: https://habr.com/ru/post/J337578/


All Articles