For many years I have been working with AngularJS and to this day I use it in production. Despite the fact that itβs impossible to call it ideal, because of its historically developed architecture, no one will argue with the fact that it became just a milestone in the evolution of not only JS frameworks, but also the web as a whole.
In the yard of 2017, and for each new product / project, the question of choosing a framework for development arises. For a long time I was sure that the new Angular 2/4 (hereinafter simply Angular) would become the main trend of enterprise development for several years ahead and had no doubt that I would only work with it.
Today I myself refuse to use it in my next project.
Disclaimer : this article is strictly subjective, but this is my personal view of what is happening and concerns the development of enterprise-level applications.
During the years of evolution, most of the flaws in the framework have been eliminated, libraries have been brought to a very stable state, and the size of the community tends to infinity. Perhaps we can say that in the first angular, there is little that can be greatly improved without breaking hundreds of existing applications.
Despite the colossal work and the general acceleration of the framework (especially after 1.5),
its main drawback, I would still call the speed of work. Of course, this is forgivable, given that after so many years, backward compatibility is still maintained at the very least.
And finally Angular was rewritten from scratch in order to become the main for many future web applications.
Of course, the path to this was long and full of Breaking Changes, but today Angular 4 is stable and positioned as fully production-ready.
One of the coolest things that the new Angular has given us is TypeScript promotion.
Personally, I was familiar with it and worked even before it became the main one for my favorite framework, but many people learned about it thanks to Angular.
I will not dwell on TypeScript, because This is a topic for a separate article.
Yes, and it is written about him more than necessary. But for TypeScript enterprise development, there are a lot of advantages. Starting with the most static typing and scopes and ending with support for ES7 / 8, even for IE9.
The main advantage of working with TypeScript is its rich toolkit and excellent IDE support. In our experience, unit tests with TS have to write significantly less.
If you are reading this article, then with a 95% probability you already know what it is.
But for those 5% who still do not know - Vue.js is an extremely lightweight (but very rich in functionality) framework that incorporates many good things from both AngularJS and React.
In fact, it looks more like React, but the templates are almost identical to AngularJS (HTML + Mustache).
In reality, it differs from AngularJS very strikingly, but in the superficial sense,
understand how it works, if you already had experience with React or AngularJS, it will be easy.
My last project, which was recently released in production, we wrote on AngularJS 1.5-1.6.
Despite the existence of a stable version of Angular for a long time, we decided not to migrate to it for several reasons (not so much technical as political ones).
But we used one of the features of the modern web from the very beginning - this is TypeScript.
Here is an example of our component from this project:
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 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 4 Angular CLI
CLI , // .
, Angular. .
, AngularJS, -. (), , . .
CLI " ", React (create-react-app) Vue (vue-cli). , , .
, , ,
. , .
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, .
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 , . , API, Angular , .
:
- 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();
, 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? . .
, API β .
. , , , , . 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 .
, ,
TypeScript , #
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
},
, -.
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
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>
, .
Binding | Example |
---|---|
Properties | <input [value]="firstName"> |
Events | <button (click)="buy($event)"> |
Two-way | <input [(ng-model)]="userName"> |
Visualize a banana in a box to remember that the parentheses go inside the brackets.
Angular , ,
, .
β Vue. , , 6 , .. .
Angular View encapsulation.
Shadow DOM .
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 .
, , 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
. ( ).
,
.
:
don't use default exports :)
Just place both export types and it works
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 {}
, .
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)
.
, .
- , .
, β UI . , β -. , UI framework .
, , UI ,
.
UI Angular: https://angular.io/resources ( UI components).
.
, Angular Material 2 , Angular .
, Angular Material 2 , , , .. , , . multiple-select, .
.
Feature | Status |
---|---|
tree | In-progress |
stepper | In-progress, planned Q3 2017 |
sticky-header | In-progress, planned Q3 2017 |
virtual-repeat | Not started, planned Q4 2017 |
fab speed-dial | Not started, not planned |
fab toolbar | Not started, not planned |
bottom-sheet | Not started, not planned |
bottom-nav | Not started, not planned |
, Bootstrap
ng2-bootstrap () ngx-bootstrap. , CSS, ( modal, datepicker typeahead).
. ( Tree Table!).
PrimeFaces .. JSF, . PrimeNG ( ). , , .
.
, ,
, PrimeNG .
β ( ) Clarity vmware.
UI , CSS .
bootstrap. / .
, datepicker' select2- .
: DatePicker, Select 2.0 ( , , ).
, "Clarity Design System" β Angular
( enterprise ). VMware .
, .
, Angular UI .
?
Angular VMware. enterprise? .
, .
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,
.
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/337578/
All Articles