News for hackers. Stuff that matters.
Re:Angular 11 by geewee on Thursday November 12, 2020 @07:34AM (#25069131)
I've recently had to use Angular for some time after using React for years. It's.. been unpleasant. A few of my pain points.

- Observables. I'm actually okay with observables, but it seems like Angular has tried to ride on both horses when it comes to observables. E.g. the `HttpClient` returns an Observable, but you can't get your input as Observables, so you have to do weird lifecycle workarounds to get observables re-computed as your input changes

- No fragments. Each component always creates it's own HTML tag. This makes stuff like having components inside tables really difficult to get right.

- The reactive forms has been untyped for years. It feels weird to ship with a forms module that seems to work pretty well, heavily invest on typescript, and then the forms are simply `any` - I mean what the hell?

- The input/output model is unfortunately much worse than it is in React. It doesn't seem like it, but the moment you want to create a HOC, or you want to wrap a component in another component, you find out that you have to manually specify all the input/output of the component you have to wrap. Nesting components is so inconvenient that your components become much larger than you want them to.

Re:Angular 11 by llimos on Thursday November 12, 2020 @07:59AM (#25069345)
My biggest problem with it is it's basically a new JS-like language written on top of JS. You just don't write idiomatic JS (or even TS) in Angular.

In templates is the biggest example - where you can use some JS constructs but not others, and have to learn a new set of directives instead of using if/for/while etc.

But also things like modules being an empty class that's only there so you have somewhere to put your annotations.

Not to mention needing to bundle a compiler, unless you use AOC, and the debugging is a nightmare (or at least used to be, I don't know if they've fixed it recently.) When your stacktrace is full of zone.js calls that are doing weird things because they monkey-patched Promise, you get the feeling that it's just not Javascript.

Some might say that JSX in React is similarly not JS. I respectfully disagree. The syntactic sugar is so light you could easily translate it yourself, by hand. Try doing that with Angular code!

And the trouble is that, as a new language that you have to learn, it is significantly inferior to JS/TS. So yes, give me React any day, where I can use real Javascript and not have to feel like the framework is fighting the language it's written in.

Re:Angular 11 by mgechev on Thursday November 12, 2020 @08:14AM (#25069467)
Angular team member here.

>In templates is the biggest example - where you can use some JS constructs but not others, and have to learn a new set of directives instead of using if/for/while etc.

Templates, just like JSX have their own trade-offs. They are statically analyzable, so relatively easy to optimize by our tooling. Having HTML templates allows us to generate efficient JavaScript instructions for rendering and change detection.

This has almost zero overhead on runtime and lets us perform change detection as fast as possible (we know exactly what has changed in the view).

>Not to mention needing to bundle a compiler, unless you use AOC

You don't need to bundle a compiler in Angular. The compilation happens at build time. After that we perform a lot of optimizations (having JavaScript instructions instead of HTML templates allows us to tree-shake more efficiently).

>debugging is a nightmare

Browsers hide the part of the stack trace that's coming from node modules, others let you blackbox non relevant scripts. To ensure smoother debugging experience for folks, we've been also working on providing better debugging guides and development tooling. We'll be rolling this out in 2021.

>And the trouble is that, as a new language that you have to learn, it is significantly inferior to JS/TS. So yes, give me React any day, where I can use real Javascript and not have to feel like the framework is fighting the language it's written in.

As I said above, it's all about trade offs. JSX is very expressive, but sets its own limitations. It locks you to a specific paradigm for incremental computations, which is not necessarily the most performant one. Templates are less expressive. At the same time, they are easy to optimize ahead of time. Neither of both is JavaScript, as described by ECMA-262.

Comparing templates and JSX is definitely an interesting topic that's hard to cover in a comment on HN :)

Re:Angular 11 by llimos on Thursday November 12, 2020 @09:58AM (#25070528)
@mgechev Thank you, I appreciate you replying to someone who's clearly not the biggest fan :)

My main point isn't so much the individual difficulties like debugging, yes you can build something on top to fix it. It's that these are symptoms of not respecting the idioms of the language the framework is built in. This leads to everything working fine if you do it exactly by the book, but when you start deviating slightly from the standard Angular way of doing things, the cracks start to appear.

This wouldn't happen if it was written to work with the language rather than against it. There's no good reason why me setting my own setTimeout() without going through Angular's should break things, but it does (and Angular reports my app as 'unstable'). If I have a getter method on my component that gets called thousands of times a second by Angular to see if something changed, it's another symptom that the framework wasn't written to fit neatly together with the language.

The fact that you can add yet more code and tooling to handle these issues as they come up does not alter the fundamental problem.

I'm really interested to know how many JS or DOM builtins Angular modifies, wraps, edits the prototype, or replaces in order to work. I know it modifies Promise, I think setTimeout too, and probably also DOMElement. @mgechev, would you have that information?

Re:Angular 11 by brundolf on Thursday November 12, 2020 @11:31AM (#25071540)
In my experience there are two different ways to go about creating an abstraction layer for others to use: a) you respect the language/standards/idioms and just add new building-blocks to the pile, or b) you use the language as a mere "target environment" and build your own, self-contained walled-garden on top of that environment. This is a conscious choice, and each of these approaches has tradeoffs and costs. I wrote a blog post about it, actually: https://www.brandonsmith.ninja/blog/libraries-not-frameworks

I personally tend to agree with you, but I think there are use-cases where the Angular philosophy can be the right decision. The key differentiators for me are a) does this walled garden provide real benefits over the more open model? b) does the organization backing this project have the resources to re-implement and maintain standard library functionality, build/editing tooling, debuggers, documentation, etc? There is a very high cost to going this route, because so many things move in-house. In Angular's case, backed by Google, the answer to question b is probably yes. The answer to question a is less clear. It sounds like there are performance advantages, and certainly there's a cohesive dev experience. For some companies, this tradeoff may be worth it. Personally I wouldn't want to use Angular, but I respect their decisions for what they are.

Aside: I wrote that blog post after being faced with a framework that's very similar to Angular, except it's backed by a very small team unaffiliated with a major tech company and is barely being maintained at this point. That's pretty much the worst-case scenario; with a more open "library" you'd have a much easier time coping with a loss of official support. But Angular is unlikely to ever find itself in that kind of situation.

Re:Angular 11 by llimos on Thursday November 12, 2020 @11:50AM (#25071698)
> Angular is unlikely to ever find itself in that kind of situation.

AngularJS (v1) found itself in exactly that situation! But it's true that it doesn't look like new-Angular is going that way (at least not yet).

Re:Angular 11 by brundolf on Thursday November 12, 2020 @11:56AM (#25071761)
> AngularJS (v1) found itself in exactly that situation!

Sort of. Angular 2 came out in 2016, and Angular 1 will continue to get long-term support through 2021. That's a pretty generous window, and there's also a clear and well-documented migration path. Overall it's a dramatically better situation than the one I found myself dealing with.

Re:Angular 11 by mgechev on Thursday November 12, 2020 @10:45AM (#25071042)
Of course! I love chatting about incremental computations and approaches in different frameworks :)

You're getting warnings at devmode (i.e. during ng serve) that you've modified a binding after it was checked. This is not related to the way we trigger change detection. The simplest example is:

  @Component({
    template: '{{ foo }}'
  })
  class AppComponent {
    _foo = 0;
    get foo() {
      return this._foo++;
    }
  }
This is clearly unstable because every time we get the value of foo, we also increment _foo. What Angular does in devmode is to ensure developers are not hitting this issue.

This is not related to setTimeout and the APIs we patch with zone.js. The check just safeguards us to not get into such cyclic binding scenarios. We'll be working on better explaining the error and the message in future releases.

DOMElement is untouched, we only patch APIs so that we can plug into the microtask queue and trigger change detection when it's empty to ensure consistency between the model and the view.

In fact, one of the projects high on our priority list is to make Zone.js optional and provide alternative, ergonomic APIs to trigger the change detection and specify local component state. I am saying ergonomic APIs because this is already possible, just not ideal since we have to specify a noop zone when bootstrapping the app. Optional, because many developers love the current behavior and we want to keep it available.

And just, let me spent one more paragraph in explaining how the change detection works. Just want to show why is it that fast and with such a low memory overhead.

Each component has a template that we translate to JavaScript instructions. For example:

  <span>{{ foo }}</span>
We will translate to something like (I'm writing the comment from my phone, some instruction names could be different in the CLI output):

  if (mode & creation) {
    renderElement('span');
    interpolation(ctx.foo);
  } else if (mode & update) {
    interpolation(ctx.foo);
  }
That's a leaf component, but we can imagine how it works in more complicated hierarchies.

So when we trigger change detection we just invoke the template functions for the components in update mode. We update only the bindings that have changed, without allocating any additional data structures to do that.

Whether the framework is written to fit the language is mostly a subjective opinion.

Angular doesn't go against the standard anywhere and does not introduce extra syntax on top of TypeScript. It also doesn't add new semantic to already existing constructs.

In fact, to keep compliant is from a high priority to us. We constantly interact with different standardization bodies and keep up to date with standards.

Re:Angular 11 by llimos on Thursday November 12, 2020 @11:45AM (#25071655)
Thank you for the detailed explanation.

Yes, the basic template update mechanism is fine, and it does seem to be fast. I was talking more about the change detection, how it knows that foo has changed in the first place, particularly if it is a nested object or a getter that leads to one. I have had cases where Angular calls the getter constantly (proved by console.log).

I agree that there isn't another obvious way to figure out something changed, but that's the point - since JavaScript doesn't have a way to notify on changed properties, for the framework to make that the paradigm is an interesting choice. So it's great news that you're moving away from that and from zone.js.

This is where React's setState is much easier to reason about. I call a function, I know that I told React that something changed, so the update isn't a surprise. While setting a property does not usually cause anything to happen in Javascript, so I have trouble getting my head around how it works and what Angular is doing behind the scenes, and realizing that it must be polling, which isn't usually a good pattern.

> In fact, to keep compliant is from a high priority to us. We constantly interact with different standardization bodies and keep up to date with standards.

Which is definitely a good thing, but also an indication that it's not actually Javascript, just something that tries to be compatible with Javascript.

Anyway, thank you very much for responding! I also understand that Angular has a lot of history to work with and you're doing a good job with the paradigms you have.

Re:Angular 11 by pas on Friday November 13, 2020 @06:52AM (#25081318)
Hm, so if you get modifications thousands of times a second, then ... something is wrong. I'm tempted to say you're "holding it wrong". Meaning that on one hand you do something that goes against how it ought to be, but also Angular despite all its mighty compiler/optimizer/typing magic doesn't stop you from doing this and doesn't help you to fix this.

As a rule of thumb naive change detection is fine for a small app, but if you want performance, just opt out of the default and manage it yourself: https://angular.io/api/core/ChangeDetectorRef#usage-notes (and probably you have a component that spams changes, where you should completely opt out: https://gist.github.com/jhades/269ee42b83937418e0dbe00e49413... )

Also, I don't think they're moving away from zone.js. They are simply providing an alternative for the more minimalistic minded folks, who like to put their own stack together.

> While setting a property does not usually cause anything to happen in Javascript, so I have trouble getting my head around how it works and what Angular is doing behind the scenes, and realizing that it must be polling, which isn't usually a good pattern.

... um. It's a bit ironic that you say that Angular is going against JS, but maybe you forgot about the wonders of Object.defineProperty which allows the unflinching true hackers to virtually override assignment operation itself!

But Angular doesn't work that way, saner heads prevailed apparently. Hence the use of TS annotations, like the @Input property, which basically wraps the field to trigger change detection. And zone.js, which ... well, basically it does work that way. (It simply replaces global objects with a wrapped version. It wraps timers and I/O, as in click, type, touch, fetch, and websocket events, and who knows what.) So no polling, but yes monkey patching.

Re:Angular 11 by jimbob45 on Thursday November 12, 2020 @11:23AM (#25071439)
I was formulating this exact argument in my head as I scrolled down the comments. Thankfully, you wrote it out more eloquently than I was going to.

Nothing in Angular is first-class because nothing in Angular is endemic to JS. Some are not bothered by this. Personally, I get frustrated without first-class features because I don’t feel like I can put my back up to any part of the language and be 100% sure of how it’s going to work/compile. Even worse, the hybridized language becomes a nightmare to debug since everything has to go through the Angular pseudo-VM.

Re:Angular 11 by azangru on Thursday November 12, 2020 @08:35AM (#25069657)
> So yes, give me React any day, where I can use real Javascript and not have to feel like the framework is fighting the language it's written in.

You might find that something like lit-html is even more like vanilla js, while being, I believe, statically analyzable.

Re:Angular 11 by IggleSniggle on Thursday November 12, 2020 @04:38PM (#25075435)
I've been really interested in uhtml recently.
Re:Angular 11 by mgechev on Thursday November 12, 2020 @08:22AM (#25069539)
Angular team member :)

"Inputs as observables" is probably the most discussed feature request on our issue tracker. I agree that there are strong arguments for going in this direction, but we generally try to keep `@angular/core` agnostic in this sense and not couple it to RxJS (EventEmitter is the only dependent abstraction right now). There are different reasons for that, but I'd say the two main arguments are: out of order component loading and ergonomics.

There are a lot of developers who are comfortable with RxJS and enjoy how expressive it is. Simultaneously, we have a less vocal majority of either beginners or people who oppose such expressive APIs. Both of these groups use Angular, and we need to keep the framework ergonomic for them.

That's why we have community libraries such as `ngrx`, which we consider as reactive extensions of @angular/core.

>- The reactive forms has been untyped for years. It feels weird to ship with a forms module that seems to work pretty well, heavily invest on typescript, and then the forms are simply `any` - I mean what the hell?

We developed reactive forms when TypeScript was still not as powerful as it is today. For the past years, however, the language evolved and it has very strict and expressive type system.

We really want to make reactive forms much more struct :) Our team has been looking actively into that, but it's not trivial to fix the problem without introducing breaking changes in the public API surface. That's something on our radar, and we've been doing a lot of exploration, but given the adoption of Angular, we want to be careful and land it without breaking the ecosystem.

In the meantime, we have community modules that provide much stricter forms implementation.

Regarding fragments and the input/output model, we haven't received much of your feedback, but that's something I'll bring up to the team.

Re:Angular 11 by geewee on Thursday November 12, 2020 @01:44PM (#25073226)
Hey thanks for reaching out.

Re: inputs as observables: Yeah I've followed the Github issues and while I get the arguments, it still seems weird that the value prop of Angular seems to be "batteries included", that the batteries then disagree with eachother.

I totally get that you have two camps of people using the framework, but right now it feels like the state Angular is in, leaves both camps satisfied. The people who like RxJs can't utilize it fully, and the people who don't still have to deal with it.

Re: forms: It makes sense that the forms are hard to type without breaking stuff. Stuff being third-party is probably one of the reasons the React eco-system has been able to move faster, but also with more breakage. That's probably just a consequence of the different ways to govern the project.

Re: fragments/host elements: There's a two year old issue here with 200 or so upvotes: https://github.com/angular/angular/issues/18877 I'll admit that this is probably not the biggest issue - I've only encountered it because I wanted to have sub-components within tables and that can mess up the formatting.

Re: input/output model: It probably makes sense that you haven't heard too much about it. It's probably not something you'd necessarily think about if you just developed angular and haven't tried other composition models.

Re:Angular 11 by dsun179 on Thursday November 12, 2020 @11:14AM (#25071344)
Thank you for the insights. Wouldnt it be possible to add an @AsyncInput decorator, without ever touching the ng/core? Same could be done for @AsyncOutput, with observables instead of event-emitters.
Re:Angular 11 by geewee on Thursday November 12, 2020 @01:57PM (#25073421)
I would love this so much.
Re:Angular 11 by miohtama on Thursday November 12, 2020 @08:59AM (#25069866)
Nice to see Angular authors here! As an another question, what are generally the legacy features holding you back you would want to change if you could do again everything with modern TypeScript, CSS and tooling?
Re:Angular 11 by mgechev on Thursday November 12, 2020 @10:22AM (#25070794)
What's tricky to make forms stricter are proper typings for dynamically added form controls. We don't know what their types are since developers can add them at runtime.

It's not impossible to make forms stricter, it's just a matter of planning how to make it in the least breaking way. Definitely from a very high priority for us.

Re:Angular 11 by liminal on Thursday November 12, 2020 @03:59PM (#25074951)
Inputs should really handle observables - Perhaps with a separate syntax or something. You can't really use Angular without learning rxjs, and once you have the fact that component outputs use them, but inputs don't sticks out like a sore thumb.
Re:Angular 11 by renke1 on Thursday November 12, 2020 @07:45AM (#25069214)
I wholeheartedly agree with basically everything you said.

Reactive Forms are really great in general, but not being strongly typed is really a shame. I am aware though that it's not easy to actually make it strongly typed for various reasons.

For a component-based framework, it's way too tedious in Angular to actually create components. Too often I create big components because I can't bring myself to split it into smaller components. In React it is so natural to create components you can do it even while you develop a feature (without it being overhead). I hardly do that in Angular because it's just too much work.

Re:Angular 11 by geewee on Thursday November 12, 2020 @01:45PM (#25073244)
Yes I think the Reactive Forms work really well for the most part, and the type-safety is such a shame.

I agree 100% with the big components. I can literally feel myself thinking "I should split this up.. but it's so much work" - where there's just much less friction doing that in React.

Re:Angular 11 by spyke112 on Thursday November 12, 2020 @08:15AM (#25069473)
Regarding observables, specifically the ones from HttpClient, i always just do a .toPromise(), and use async/await. My rule of thumb is that if i expect a stream of events use observables, but if it's a one of call, like a http request really is, then i use promises. If i need to memoize the data from the request, i do it in a service, and call through that.
Re:Angular 11 by modernlearner on Thursday November 12, 2020 @11:18AM (#25071383)
Just ran into this when building a component to display and filter data. It was starting to turn into a monster when trying to use Rx.js and in the end, the code was simpler to understand with multiple for loops and forEach and map/filter calls.

I ended up with a service that doesn't rely on observables.

Re:Angular 11 by nobleach on Thursday November 12, 2020 @06:36PM (#25076828)
Not to mention, easier to test. I had a branch of my code where I had finally figured out how to reliably test the stream/observable variant, but it was just an ugly mess.
Re:Angular 11 by davedx on Thursday November 12, 2020 @07:52AM (#25069276)
Yeah, typing in angular is kind of weird. Think Angular 9 was when templates got real type checking, but reading the documents it's still not clear to me what's enabled by default. Whereas with my react projects, if I use TypeScript and use the right file extensions, then everything is type checked, end of story.

I also didn't like using observables with Angular to fetch/send data to API's. Not everything is an event stream, why can't we use the right tool for the job?

Re:Angular 11 by mgechev on Thursday November 12, 2020 @08:01AM (#25069365)
Angular team member here.

Strict type checking is often reported as confusing for folks getting started with Angular and web development. That's why by default our type checking is not the strictest we support.

To make sure advanced developers have better compile-time type checking and editor support, we introduced strict mode. When you create a new workspace the CLI asks you if you want to get it enabled.

In the future, incrementally, we'll be enabling strict settings for everyone.

Re:Angular 11 by davedx on Thursday November 12, 2020 @10:26AM (#25070848)
Thanks for commenting!

It still seems strange to me, if you start a new Angular project then you know you're writing TypeScript, no ifs or buts - and that means if you write some code that doesn't compile, you'll have to fix it before it runs. I don't understand why that's seen as different in the template layer?

Great that it's there at all though, last time I developed with Angular templates didn't have type checking at all, and it kind of hurt.

Re:Angular 11 by mgechev on Thursday November 12, 2020 @10:56AM (#25071156)
Sorry for the miscommunication. I meant type checking in general, not in Angular templates in particular.

To get the same strictness as in TypeScript, you can enable `strictTemplates` in tsconfig.json. Here's our guide which goes into more details https://angular.io/guide/template-typecheck.

Re:Angular 11 by dmm on Thursday November 12, 2020 @08:07AM (#25069407)
> I also didn't like using observables with Angular to fetch/send data to API's. Not everything is an event stream, why can't we use the right tool for the job?

I think the idea is to provide a single async abstraction so everything can be used with rxjs operators. I've taken advantage of this several times with something like "combineLatest" to return the result of a http call and an event stream.

You can also call "toPromise" in rxjs to turn an observable into a then-able promise.

Re:Angular 11 by stupidcar on Thursday November 12, 2020 @07:51AM (#25069271)
They are finally making reactive forms strongly typed, at least[1].

On the Observables point, the Ngrx folks have created the @ngrx/component[2] and @ngrx/component-state projects[3] to try to build true reactivity into the component model. It's a shame this effort is happening outside of the main project, but it's something.

[1] https://angular.io/guide/roadmap#better-developer-ergonomics... [2] https://ngrx.io/guide/component [3] https://ngrx.io/guide/component-store

Re:Angular 11 by strombofulous on Thursday November 12, 2020 @07:37AM (#25069154)
Out of curiosity how long have you used angular for? I usually have similar thoughts when first picking up a new framework before working with it for a few months to figure out how it's "supposed" to be used.

(I have not used angular myself)

Re:Angular 11 by foepys on Thursday November 12, 2020 @07:46AM (#25069223)
Their observations especially on Observables is very spot-on. We have quite a few "makeObservable(input)" in our code to make @Inputs observable. That the framework doesn't do it automatically is a bit of a let down.

Same about Reactive Forms.

Patching each @Output and @Input through a component by hand is also pretty annoying, especially if they change relatively often. A custom input field for a reactive form gets quite large due to all the in- and outputs.

Re:Angular 11 by geewee on Thursday November 12, 2020 @01:48PM (#25073295)
Is there a good implementation of makeObservable() somewhere? I haven't been able to find one that seemed to work elegantly.

Patching @Output and @Input is supremely painful - I really wish there was some sort of shorthand for that.

Re:Angular 11 by foepys on Friday November 13, 2020 @01:36AM (#25079619)
I cannot share the code we use directly but we use Object.defineProperty and override the getter and setter for it, tunneling the new setted value through a behavior subject that gets returned. The getter returns the value by synchronous access to the subject.

Inputs into makeObservable are the component class (this) and the requested property name (generics with keyof).

Re:Angular 11 by geewee on Friday November 13, 2020 @01:55AM (#25079721)
Oh that's a clever way of doing it. Thanks!
Re:Angular 11 by renke1 on Thursday November 12, 2020 @07:51AM (#25069267)
Not parent, but I am using Angular for like 3 years now, I apparently have not yet figured it out. I am still baffled how hard it is to figure how to something is supposed to be used (and when do I am often shocked how it can be considered the right way…). It does not help that there a countless of Angular resources (blogs, Stack Overflow answers/questions) with low quality.
Re:Angular 11 by nicoburns on Thursday November 12, 2020 @11:09AM (#25071306)
I used Angular for ~8 months on a project. It was just about ok. Much, much better than AngularJS (Angular 1.x). But worse than React in pretty much every way. What other people have said about everything needing to be mediated by the framework was the worst bit. That and making the change detection work correctly required understanding the internals in quite a bit of detail. React's approach is slightly slower but much simpler.
Re:Angular 11 by geewee on Thursday November 12, 2020 @01:47PM (#25073265)
I've used it for a couple of months, but I've primarily been pair programming with people who have used it for years. Some of it is probably still getting used to the ropes, but I've heard many share similar sentiments about most of the things.
Re:Angular 11 by Griever on Thursday November 12, 2020 @11:59AM (#25071796)
To address your point about the lack of fragments: It's not as clean as React's implementation, but you can set a component to "ViewEncapsulation.None" to help address this.

Within a components template though, I frequently use <ng-container> to execute async pipes, or to perform simple conditional logic without adding unnecessary divs.

Re:Angular 11 by JamesSwift on Thursday November 12, 2020 @12:09PM (#25071920)
For so little experience, you've actually hit pretty much the same list as me (with a couple years experience). I don't agree so much with the last bullet point but otherwise spot on.
Re:Angular 11 by geewee on Thursday November 12, 2020 @01:50PM (#25073323)
I'm glad that it isn't all just rookie mistakes and I'm not "getting" it :)

I'm intrigued about you not agreeing with the last part though. You don't think it's painful to manually reroute inputs/outputs to child-components?

Re:Angular 11 by JamesSwift on Thursday November 12, 2020 @03:30PM (#25074534)
Yeah, I don't find myself doing it as much as I felt like I did in react aka prop-drilling. React has context API and state to deal with it, angular you would probably use a service or dependency injection.