Disadvantages or “How I stopped loving Angular”

At first I wasn’t going to write another hater post like Angular 2 is terrible.

However, despite above post being written for quite an old Angular version already, it’s spot on most of the points. I might say that writer might have been too soft sometimes.

One point, which I don’t completely agree on, is about RxJS, simply because it’s so powerful:

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.

In reality, thanks to RxJS, if you start to think of any data as stream it tends to be very easy to manipulate.

Observables can be very useful sometimes. Yet the sad truth is that we will not see a native 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).

Moreover, even with all of the great features Rx gives us — making it framework core is not the best approach.

I think that people like me (who really like Rx) would’ve easily integrated it themselves if needed, instead of forcing it as default. This wasn’t a problem for me, but a lot of people I know have a hard time understanding Observables, Subjects, etc. All of that on top of general Angular complexity seem to be very harsh on framework newcomers (most of those just cast HTTP resulted Observable to Promise).

Another point, which I don’t agree on, is TypeScript — it’s really an amazing language/tool, but that’s below.

Post above is highly recommended to check, especially if you already use Angular or just planning to

Nevertheless, I’ll write some of my own thoughts on it, not pointed out in that post.

TypeScript in Angular

The most painful disappointment for me — is what happened with TypeScript usage in Angular.

Some of the bad examples below.

Poor API design

One of the main problems of TypeScript usage in Angular I consider API design. TypeScript itself is ideal for making most stable, strict code without the possibility to make the wrong move. It’s basically created for making public API’s, yet Angular team did all they can not to use this as an advantage.

Examples:

HttpParams

For some reason Angular team decided to make HttpParams immutable. Don't get me wrong here, immutability is really great, but only if it's a general approach. If you think that most of the classes in Angular are immutable - you are wrong.

So this code for example:

let params = new HttpParams();

params.set('param', param);

params.set('anotherParam', anotherParam);

...

this.http.get('test', {params: params});

Will not add any parameters to the request. Why? There are no TypeScript or Angular errors or any kind of warnings. However, there are no parameters for your GET.

Only opening the class itself in TypeScript you can find the comment (with a typo btw):

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

Which is, of course, non-intuitive at all.

And here’s all of the info in the docs:

http

.post('/api/items/add', body, {

params: new HttpParams().set('id', '3'),

})

.subscribe();

Router API

I have many complaints for the new Angular router, but API is the main concern.

No more named routes

For some strange reason Angular team decided to remove support for the named routes. It’s basically a string name for the route, to be later easily used without bothering with the url.

In our AngularJS app, this was essential for a few reasons:

easy to use for programmatic redirection in controllers

able to use enum for routes, to search and refactor the whole application easily ( Alt+F7 instead of global search by string)

for routes, to search and refactor the whole application easily ( instead of global search by string) saved us a whole lot of pain, when after having a hundred of existing routes, there arise a new root route requirement (virtual) — just a matter of changing one route config file, not breaking any existing URL’s, thanks to the route names

I don’t know why is this (much needed) feature was removed, but some people had to implement it themselves.

Events

Now to work with route parameters we have to subscribe to the router events. Ok that’s fine, but you only can subscribe to all of them, even if you only need one. And you’ll have to check using instanceof (which is again a new approach, different from most of other places):

this.router.events.subscribe(event => {

if(event instanceof NavigationStart) {

...

}

}

Query params

If router events may look ok for some people, the idea of making query params an Observable looks very strange to me:

this.activatedRoute.queryParams.subscribe(params => {

const val1 = params['key'];

const valN = params['keyN'];

});

Navigation

Another strange decision was to make all of the router interaction based on the commands, even an Array of them. So most popular and simple routes will look like this:

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

...

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

Why is this bad?

Because in this case commands have a signature of any[] . For those who are not familiar with TypeScript - this basically disables its type-checking features telling the compiler there can be anything here.

That is when the routing is the most loosely coupled part in Angular.

For example, in our AngularJS app, we tried to make routes typed as much as possible, even using enums instead of simple strings. This greatly helps finding routes in a big codebase and highly helps with refactoring. Therefore, in the case above instead of global search for ‘some’ string you can just press Shift + F6 .

Again, Angular doesn’t use this great TypeScript advantage.

Lazy Load

This section can be another post, but I would like to point out that any TypeScript features are ignored of module name is specified like a string after #

{

path: 'admin',

loadChildren: 'app/admin/admin.module#AdminModule',

},

Forms API

First of all — there are two form types in angular: template-driven and reactive.

Of course, they work completely differently.

Nevertheless, my main concern is about reactive forms API :

// What is the empty parameter and why is it needed?

// Why name is an array with a validator??

this.heroForm = this.fb.group({

name: ['', Validators.required ],

street: ''

});

or some examples from the docs:

this.heroForm = this.fb.group({

// Empty field is actually a FormControl called "name"?

name: '',

...

// ??

secretLairs: this.fb.array([])

});

Not even mentioning stuff about combining both form types — you can’t just use attribute binding like [disabled] for reactive forms…

These are just some of the examples of poor API design, there are much more, but I think it’s ok for this section

__metadata

Sadly, TypeScript usage in Angular is heavily based on Decorator usage.

Decorators are great, but in runtime there’s a great part missing in the current web (ES7 quite is far) — __metadata . It is disabled by default and only emitted if there's a compiler option specified.

__metadata just stores the information about class/type/method/parameters etc., marked with the decorator. This is needed to get this information in runtime. Think of reflection in Java-like languages.

Without metadata, you can still use decorators — at compile time, but there’s not much profit in this case.

Nevertheless, in our AngularJS app we’ve used this decorators, like @Component :

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

It just wraps our TypeScript classes in AngularJS component objects and makes them controllers.

However, in Angular, despite being this language feature still experimental, it became framework core. This means that you’ll have to use reflect-metadata in absolutely any case. Very arguable decision.

Abstractions

Amount of internal classes, abstractions and a lot of TypeScript-related tricks, do not help with community adoption and may even make a bad impression about TypeScript itself.

The main example of such problems is the Dependency Injection in Angular.

The concept is great, especially for unit testing. However, in reality it seems that there’s not much use of making something Java-similar on the frontend. We’ve actively used this in our AngularJS application for a long time, but after working some time with Vue component testing, I’ve really started doubting DI usefulness.

That goes for Angular modules too. In past I’ve thought that they are very great in making the application modular, IoC and all that. It seemed, for example, very useful for Lazy Loading. In reality — whole minified Vue.js is about 60KB, and can be decoupled without any problems and modules at all. So do we really need all of that module boilerplate?

Back to DI. In Angular most of the common dependencies, like services, DI will look quite simple, with constructor:

constructor(heroService: HeroService) {

this.heroes = heroService.getHeroes();

}

But this will only work for TypeScript classes, so if you want to add a constant, you’ll have to use @Inject :

constructor(@Inject(APP_CONFIG) config: AppConfig) {

this.title = config.title;

}

Ah, services which you inject, must be annotated with @Injectable() .

But not every service, only those who have own dependencies, otherwise you can omit the decorator.

Why not make them mandatory if it’s recommended to always do anyway:

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.

Another great quote from official docs about parentheses:

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

Overall, this leaves the impression that TypeScript is definetely used wrong in Angular.

Again, I will point out that the language itself really helps in development.

Template syntax

Template syntax is the main complaint to the Angular by the most developers. And it is very objective point.

Example of many different directives with different approaches/usages:

<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>

Initially developers positioned the new syntax as an escape from directive overflow in AngularJS.

The promise was about only having two things: [] and ().

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

Unfortunately, in reality, the amount of directives is not much less than AngularJS’s.

In addition, here’s the great example about two-way binding syntax from official docs:

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

Documentation

I don’t really see a point of describing Angular documentation. It’s so amiss that you just have to read it to understand that it is not ok for such an enterprise framework.

It’s also worse than AngularJS docs as there’s no more a version selector — only major versions like 2, 4 and 5 are there. And there ARE a lot of breaking changes between.

On the contrary — Vue docs. Not only they are written in much cleaner and verbose way, they are also available in 6 languages like russian.

RxJS

In addition, Angular docs don’t have any sensible page with information on Rx and Observable or how to work with it. There's no links to the official RxJS docs too. Even though Rx is a core part of the framework and the creation of Observable is different:

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

View encapsulation

Angular has this concept of View encapsulation.

It’s basically Shadow DOM emulation or the usage of the native support of it.

The Shadow DOM itself is great and potentially lets you even use different CSS frameworks on one page, in different components, without style overlap.

However, native support is quite low today.

Therefore, by default Angular emulates the Shadow DOM.

Here’s a simple CSS example for component:

.first {

background-color: red;

}

.first .second {

background-color: green;

}

.first .second .third {

background-color: blue;

}

Angular will process it to this:

.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;

}

Dunno why it’s done like that with adding custom attribute to every rule.

Vue has the same possibility, but this looks much cleaner:

.first[data-v-50646cd8] {

background-color: red;

}

.first .second[data-v-50646cd8] {

background-color: green;

}

.first .second .third[data-v-50646cd8] {

background-color: blue;

}

Not even mentioning, that Vue doesn’t force this by default and enabling it is as simple as adding a scoped to your style tag.

Please also note, that Vue (vue-cli webpack) lets you easily switch between CSS/SASS/SCSS in the same way, but Angular CLI requires running stuff like ng set defaults.styleExt scss . Not sure why, because under the hood it uses the same webpack, and could've used both extensions without any problems (webpack doesn't really care about the extension, it's just a matter of additional step in its internal pipeline).

Nevertheless, that’s not the main problem, real one we’ve encountered when started using external components.

In our case, we’ve used one of the most popular UI framewokrs — PrimeNG, which use such selectors sometimes:

body .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {

font-size: 1.1em;

}

These selectors by definition already have a higher priority, than component styles, which uses this external element.

To fix this you’ll have to write something like this:

body :host >>> .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {

font-size: 2em;

}

Sometimes it’s only doable via the fabled !important .

Of course, all of this is related to PrimeNG, but that’s the problem you’ll have too if you will actually start using Angular.

Few words on stability

In the example above we’ve used >>> - as the /deep/ it's an alias for the shadow-piercing selector. It allows us to "ignore" Shadow DOM for some rules and may be invaluable for some of the external components.

In the one of the recent versions of Angular, developer team decided to deprecate both /deep/ and >>> , as per new standard.

No build errors or warnings were emitted, only the selectors stopped working. As it turned out, now only ::ng-deep works - the analogue of the shadow-piercing selector in the Angular universe.

The update wasn’t the major version one (4.2.6 -> 4.3.0, special thanks to NPM for versions with ^ ), just one day a lot of our styling/layouts broke for some reason.

Of course, not everyone on our team reads Angular ChangeLog every day, and may have missed the latest standard/trend too. Therefore, at first we searched for a problem in our own styles — we’ve had to spend quite some time and brains to find and fix this issue.

Moreover, soon ::ng-deep will stop working too. I don't really know how to fix the styles of not-so-good frameworks, like the mentioned PrimeNG, without it.

Our conclusion on this: default setting — Shadow DOM emulation, creates more problems than it solves.

Custom HTML parser

This can also be a separate post, but making it short: Angular really wrote its own HTML parser. Most likely, the main case for this was the case-sensitivity.

There’s no reason to wage another holy war about Angular not being standard, but a lot of people thinks that this is a strange idea, because for AngularJS simple HTML (case-insensitive) was more than enough.

With AngularJS, there could be a situation like this: you’ve created some <my-component/> and didn't create a test for it. After some time the module with component was removed/refactored/etc.

Anyway — <my-component/> isn't displayed now at all.

Now the parser determines the unknown tags and throws an error breaking the whole build.

But now any external/web component requires either disabling this checking at all, or enabling of the CUSTOM_ELEMENTS_SCHEMA which allows anything with - in it.

You can check the sources yourself:

...

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'

...

Key part here — all of these errors happen in browser console at runtime, no it will not fail your webpack build, but you will not see anything aside from the white screen. That’s because by default JIT compiler is used.

This can be fixed by precompiling the templates via another, AOT compiler, Just run with --aot flag and that's it. But no, there's a bad part here too: this badly works with ng serve and slows down the compile time even more (which is very slow to start with, many times slower than Vue). Seems that's why it isn't enabled by default (which it should be).

Existence of two differently working compilers may sound dangerous, and really causes problems all the time.

We had many different AOT-related errors, including some that are still open. Like you {can’t use default exports](https://github.com/angular/angular/issues/11402):

Also, note the elegant solutions:

don’t use default exports :)

or

Just place both export types and it works

Or a lot of problems like this

AOT is not always able to understand closures, so the code below will cause strange compiler errors:

@NgModule({

providers: [

{provide: SomeSymbol, useFactor: (i) => i.get('someSymbol'), deps: ['$injector']}

]

})

export class MyModule {}

So you’ll have to write the code in more primitive and compiler-friendly manner:

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



@NgModule({

providers: [

{provide: SomeSymbol, useFactor: factoryForSomeSymbol, deps: ['$injector']}

]

})

export class MyModule {}

In addition, we’ve noted that the errors in the templates are usually non-informative at all, which is a shame, since it’s harder to search for those in HTML and IDE support isn’t the best.

Zone.js

Another great concept, which is there in new Angular, is Zone.js. It allows monitoring the execution context for the asynchronous actions. However, these stack-traces are super big and are very hard to understand, especially for newcomers. Here’s an example:

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)

Some people do like it, but in our project, we didn’t yet get much value from it. I hope this will later help in production, but not sure.

Moreover, while it’s still long way to production — we have to develop, and the amount of errors in console is usually more than one, so the search for the potential issue is always complicated due to such stack-traces.

Not even mentioning, that sometimes, Chrome console isn’t enough for all of them at all. In addition, the cause of the problem may be somewhere far above in the console, if it happens a lot.

UI frameworks

Another section, which is actually not the point about framework itself, is the very low amount of existing UI components/frameworks. Sadly, I think that writing most of the UI components from scratch — is impossible for most of the web developers/small teams. It’s usually sane (and much simpler, faster) to get an existing UI framework instead of creating own trees and grids.

And yes, I know that it is not a good way to select a front-end framework by the UI components, but most of the time it is vital for the real development.

Here’s the list of the UI frameworks for Angular: https://angular.io/resources (UI components section).

Let’s look at the most popular free and open-source ones.

Angular Material 2

Most of the big hopes I had were for Angular Material 2 due to it’s being developed by the Angular team and would definitely match the Google guidelines.

Sadly, even though it exists for quite some time already, component set is rather small.

At the time when I’ve started writing this post — there weren’t any grids (there were even less components when we were selecting the UI framework for our project) . And not a long time ago it’s finally arrived. Yet the functionality is quite basic anyway.

I think, that Angular Material 2 will only be ok for smaller or, best case, medium-sized projects, because even now there’s no trees, for example. In addition, most of the time you need stuff like multiple-select, which there is none either.

Very scarce documentation and low amount of examples is worth noting too.

Last point is the sad feature plan.

stepper - In-progress, planned Q3 2017, sticky-header - In-progress, planned Q3 2017, virtual-repeat - Not started, fab speed-dial - not planned, fab toolbar-not planned etc.

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

There’s also a Covalent UI — but I really recommend you to stay away from it, given that it’s just an extension for the Angular Material. And not anywhere near as stable. There are also 2 different grids now — one official form Angular, and another from Covalent (I don’t think this is good at all).

Bootstrap

For the same reasons as the above, I will not describe Bootstrap-based frameworks like ng2-bootstrap (little better) and ngx-bootstrap. They are not bad at all, but the simplest stuff you can do yourself with Bootstrap CSS, and there are no complex components here anyway (though some devs may be ok with just modal, datepicker and a typeahead).

Same goes for the Semantic which I personally really like, but it is still quite basic.

Prime Faces

The most popular framework today and the one with the most complex components is PrimeNG. There are grids, trees and even Tree Table!

At first, I was really skeptical regarding PrimeFaces, because I’ve had an old JSF experience with them and a lot of painful memories. They even look the same (not very modern for the free themes you can get). However, at the time of starting our project, there weren’t any good alternatives, so I would still say a Thank You to the development team. They’ve created a vast toolset in a short term, which generally does the job.

Nevertheless, the amount of problems we’ve had with it is very huge.

Documentation is quite useless sometimes. Forgot to add some module — something just broke without any kind of error. There’s a lot of debugging required, which leads to the sources that have no comments.

Overall, despite having so many components, I wouldn’t recommend using PrimeNG as a UI component base for your project.

Clarity

The ray of hope here — is young (created less than a year ago) library Clarity from vmware.

Component set is great, documentation is clean and it just looks nice.

Framework not only has it’s own UI components, but a CSS guidelines too. Something like an own Bootstrap (some styles like grid/buttons/etc. are somewhat similar). Thanks to that there a consistent and quite nice/minimalistic component look.

Grids are very functional and stable, and the sources speak for themselves (wow, writing of the unit tests is allowed here?).

However, for now forms are very basic, there’s no datepicker or select2-like component yet. It’s being worked on right now: DatePicker, Select 2.0 (as always design is slick, and even if it’s progressing slowly, we surely be glad with the result).

I would say, that “Clarity Design System” — is the only cause I still believe in Angular. It’s also the only framework that wouldn’t be a shame to use for enterprise development. After all, VMware is the serious maintainer and there’s a hope for a bright future.

We’ve just started using it, and will probably run into a lot of problems, but for today we’re fully satisfied with it and it works just fine.

But there is the only one

Yes, I think that there’s only one worthy Angular framework today. Question: What does this mean?

Answer: Development of such frameworks for Angular is only possible with big companies like the mentioned VMware. It requires a lot of skills, time and resources. Do you really need this kind of enterprise? Please think about it.

Now let’s look at what’s going on with one of the main competitors (much younger btw).

Vue UI frameworks

For the reference, here’s the list of existing Vue.js frameworks with existing grid components:

Element (~15k stars), Vue Material (much younger than Angular Material 2, yet already much more powerful), Vuetify (Material again, and many components already too), Quasar, Chinese-only frameworks should be mentioned too: iView and Muse-UI (iView looks very nice, but the docs aren’t good at all).

This is a simple, yet obvious, example, that creation of such components is much easier with Vue. This even allows you to select one of the many components, instead of hoping for the one, which is supported by some huge dev team.