DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

Cover image for I changed my mind. Angular needs a reactive primitive
Mike Pearson for This is Angular

Posted on • Updated on

I changed my mind. Angular needs a reactive primitive

YouTube

Angular developers have waited 7 years for better integration with RxJS, but this doesn't seem to be happening. Instead, the Angular team wants its own reactive primitive that can't even handle asynchronous reactivity.

This made me angry at first, but after a lot of thinking and talking with other developers, I now believe that a reactive primitive could provide an overall better developer experience than just better RxJS support, even for the most diehard RxJS fans.

  1. Better promises?
  2. A split in the Angular community
  3. Rejecting vs outgrowing NgRx
  4. Angular is not reactive enough
  5. You turned them against me!
  6. Ryan Carniato
  7. Oh yeah, RxJS actually sucks at that
  8. Selectors
  9. Colocation
  10. SolidJS signal syntax is awesome
  11. What I want
  12. RxJS compatibility is still necessary
  13. Summary and conclusion

Better promises?

Most Angular developers didn't even know about RxJS until Angular made them learn it for some core Angular APIs.

At first, most of us thought observables were just a nicer version of promises, since they could do everything promises could do, but also return multiple values over time. So we thought we would just get HTTP data like http.get(...).subscribe(data => this.data = data), and we could even handle websocket data the same way, but that would be the extent of RxJS's benefits. I think the Angular team thought of observables this way too.

But the ability to represent long-lived sources of data turned out to be far more than a slight improvement over promises. Observables enable functional reactive programming (FRP), where asynchronous code is defined declaratively. This is a huge deal. FRP can entirely eliminate race conditions and inconsistent state, improve code organization, reduce page load times, simplify state management and make naming easier. My last article explains all of this.

A split in the Angular community

But FRP requires adopting a different mindset in order to take advantage of these benefits. This is known as "thinking reactively." For most Angular developers, the opportunity to explore this mindset came with their first interactions with NgRx/Store.

Rob Wormald created NgRx in 2016 as a go-to state management library for Angular by combining Redux and RxJS. This seemed natural, because Redux was very popular in the React community, and the Redux store already came with a subscribe method, just like observables. It basically already was an observable waiting to be implemented with RxJS.

NgRx sounds really cool and powerful. It's probably really easy to use. Oh wait, how do you access current state? You can't just this.store.currentState?

Nope.

And what's more, there were no plans to add this. And there weren't even plans to plan on adding it either.

This turned out to be the result of some kind of RxJS doctrine. After further exploration, we encountered bizarre advice like "don't unsubscribe", and "don't subscribe". Can you imagine people telling you to not use .then with promises? But in RxJS, we're supposed to use withLatestFrom, takeUntil and Angular's async pipe instead. And if we use the async pipe, supposedly our apps can be more performant.

This was a fork in the road for Angular developers: Do I have something to learn, or is RxJS stupid because I can't do the first thing that came to my mind?

Fork in the road meme

Rejecting vs outgrowing NgRx

Soon after NgRx introduced more RxJS into Angular apps, alternatives started popping up that appealed to developers who didn't want to learn how to think reactively, such as NGXS and Akita.

The split in the Angular community widened as other developers took RxJS and ran with it. I remember listening to a podcast where Rob Wormald (creator of NgRx and member of the Angular team) suggested that the future of Angular would be zoneless with RxJS streams stretching all the way from event sources down to the template.

This was very exciting to me. I imagined we'd eventually have precise updates fed directly into the DOM by RxJS streams, with no need for change detection at all. This would supercharge Angular performance, which was already starting to be on par with React. I hoped that this incredible performance boost would be enough enticement for more and more Angular developers to learn how to think reactively, which would then bring the other benefits of reactivity to more and more Angular codebases: No more race conditions or inconsistent state, improved code organization, reduced page load times, simpler state management and better function names.

But this future seemed a long time away, because I kept having to fight both NgRx and Angular when writing reactive code.

For example, if you had to hit 3 services in series to aggregate data for a page, you could do this with RxJS:

data1$ = this.http.get(...);

data2$ = this.data1$.pipe(switchMap(data1 => this.http.get(...));

data3$ = this.data2$.pipe(switchMap(data2 => this.http.get(...));
Enter fullscreen mode Exit fullscreen mode

The benefit of this is that each data source is declarative, so it stands on its own, agnostic to how it may be used to define other state or features. This is extremely flexible, and has all the benefits of FRP I've already mentioned twice.

But if you want to put this data in NgRx/Store, the recommended solution is not very reactive, so it doesn't come with the full benefits of FRP. I ultimately came up with a way to wrap the NgRx API (dispatch & select) inside RxJS so I could structure my code exactly the same way I could with pure RxJS, and here is how it compared to the recommended pattern with NgRx/Effects:

NgRx/Effects vs RxJS

I used this RxJS-first approach to rewrite a feature that was using effects heavily and reduced code by 25+% and the page load + render time by 90+%.

It took pro-reactivity Angular devs longer to outgrow NgRx than it took anti-reactivity devs to reject it. But we now have some RxJS-first libaries like RxAngular and StateAdapt, which are both state management libraries that enable maximum reactivity in Angular.

So, although both NgRx and Angular introduced developers to RxJS, they were both designed in ways that made full reactivity difficult. I mentioned one issue with NgRx, but Angular had a lot more.

Angular is not reactive enough

It is obvious that RxJS was sort of an afterthought in the design of Angular. It was used to represent event streams, such as component outputs. But it was also used for route parameters, which are states changing over time. But then component inputs, which are also states that change over time, were not represented as observables. RxJS in Angular is basically a really inconsistent experience.

When I hear some Angular developers complain about how cumbersome it is to use RxJS in Angular, I can actually empathize with them. But they should be blaming Angular, not RxJS. Some things that take 4 lines of code with RxJS + Angular only take a single character with RxJS + Svelte. Angular is the only major framework for which most component libraries require dialogs to be opened imperatively, which means the async pipe can't be used, requiring manual subscription management instead. Which is itself much easier in other frameworks.

I could go into a lot more detail, and I have before, but the main idea is this: Even after all the wrapper components and utilities I can create, I still can't fix Angular's core APIs.

You turned them against me!

I have waited a long time for Angular to improve its integration with RxJS. This issue, which was one of the all-time most upvoted Angular issues, was created in December 2015! The first hope it would be worked on was when the Ivy compiler was finally finished in 2019, but after another 2 years of relative silence, the issue was closed entirely! The reason? The Angular team said, "Turning inputs into Observables would more tightly couple Angular with RxJS, and we don't want to further couple with RxJS."

What???

The last comment on that issue before it was closed was by Minko Gechev, who started by reiterating the community split over RxJS: "As we've discussed in the past, the community is very split when it comes to using less or more RxJS with Angular." At the end of the comment he said, "We will share our plans for more ergonomic RxJS APIs when we prioritize that project." For someone who had already waited for 5 years for better Angular APIs, this was very frustrating, as he gave no timeframe at all. Not only that, but the primary reason given was that many Angular developers dislike RxJS, and I strongly believe that the primary reason for this is Angular's obnoxious integration with RxJS.

RxJS turned developers against Angular meme

Then I saw that a member of the Angular team was exploring a different reactive primitive altogether. If Angular developers don't like reactivity, why would they prefer another reactive primitive over RxJS? If they don't like Angular's integration with RxJS, how would using a different reactive primitive help at all? None of it made sense to me.

Ryan Carniato

Another thing that has been bothering me has been Ryan Carniato's insistence that RxJS isn't fine-grained reactivity. I know that RxJS can be used to update the DOM in fine-grained ways, and it turns out Ryan knows it too, because he built a previous version of SolidJS with RxJS. I've been pestering him in the comment section of his YouTube streams for a few weeks now, and he finally gave me an explanation that caused something inside my head to click.

Ryan told me that the reason he calls RxJS "course-grained" reactivity is because, while you technically can perform fine-grained updates with it, people tend to combineLatest with streams instead. Is it fair to say that RxJS isn't fine-grained because people tend not to use it in fine-grained ways? I don't think so, but Ryan's main point was actually interesting. He caused me to make a mental connection that I had never made before.

Oh yeah, RxJS actually sucks at that

When I first used NgRx, I wanted to use RxJS for everything, so I relied heavily on the map operator for derived state. But nowadays, derived state is completely implemented with selectors in pretty much all NgRx projects. So, whereas modern NgRx will combine state from multiple reducers like this:

const selectItems = createSelector(state => state.items);
const selectFilters = createSelector(state => state.filters);

const selectFilteredItems = createSelector(
  selectItems,
  selectFilters,
  (items, filters) => items.filter(filters),
);
Enter fullscreen mode Exit fullscreen mode

Originally I did it like this:

items$ = this.store.select(state => state.items);
filters$ = this.store.select(state => state.filters);

filteredItems$ = combineLatest(
  this.items$,
  this.filters$,
  ([items, filters]) => items.filter(filters),
);
Enter fullscreen mode Exit fullscreen mode

I was pretty happy with this RxJS approach, until my team lead put a console log in the filter function and saw that it was being run 28 times and asked me why. I basically told him, "I have no idea, I swear FPR makes apps more performant."

My first idea was to use distinctUntilChanged. That reduced some of the reruns.

Then I realized that every single observable that chained off of filteredItems$ caused the filter function to run again. The solution? shareReplay(), except actually publishReplay(), refCount() because of some weird RxJS issue. That also reduced some of the reruns.

Then I realized that whenever both items and filters were changed at the same time, the combineLatest would run twice, once per input observable. This was inefficient, but also caused errors in some states, because it was annoying to handle the intermediate situations where one input had an updated value but the other didn't.

I really wanted to get RxJS to work with this. I was only a junior developer at the time, but I spent hours and hours trying to think a way around this problem with RxJS, and came to the conclusion that it had to involve schedulers, but I didn't understand how to actually make it work.

At that point, I began to question my goal. Look at all the work I've had to go through, and it still feels awkward. Do we really want to create custom operators to make nice syntax for all of this? Why doesn't RxJS make handling derived states easier? Would it be so bad just to use selectors?

There were a lot of other Angular developers struggling with this stuff at the same time I was, and all of us concluded that selectors were the way to handle derived state in NgRx.

Selectors

Every time I see a new state management library pop up, I check to see if they use selectors, or if they require you to use distinctUntilChanged, publishReplay, refCount, combineLatest and debounceTime. Most of them do, or you have to be okay with inefficient code. RxAngular is an exception with not requiring the debounceTime, since it gives you coalesceWith, which is awesome.

I never liked the syntax of selectors, but they seemed necessary to me. So when I designed my own state management library, StateAdapt, I wanted to find a better syntax for selectors. This is what I came up with:

// NgRx:
const selectItems = createSelector(state => state.items);
const selectFilters = createSelector(state => state.filters);

const selectFilteredItems = createSelector(
  selectItems,
  selectFilters,
  (items, filters) => items.filter(filters),
);

// StateAdapt:
const adapter = buildAdapter<State>()({})({
  items: s => s.state.items,
  filters: s => s.state.filters,
})({
  filteredItems: s => s.items.filter(s.filters),
})();
Enter fullscreen mode Exit fullscreen mode

I used a proxy object s (stands for both state and selectors) that watches for the selector you're accessing and creates a more efficient memoization than createSelector.

I've been very happy with this approach. I have thought that nothing more was needed in Angular than RxJS for events and async logic, and selectors for synchronous, derived state.

But it turns out that selectors aren't perfect either.

Colocation

There are 3 types of selectors.

State selectors

These are selectors that simply select from a state object:

const selectItems = createSelector(state => state.items);
Enter fullscreen mode Exit fullscreen mode

It makes sense to export these with the reducers that manage the state they're selecting from.

Derived selectors

These are selectors that combine state from multiple reducers/stores and calculate derived state. This is derived state that doesn't belong with the top-level state, and these selectors can contain a lot of business logic.

UI selectors

These take the derived state from the first 2 selectors and change the shape of the data to be convenient for consumption in a template.

For example, let's say I had some state that described a rectangle with a position like { x: 50, y: 200 }. If that needs to be rendered as a div with CSS like style="left: 50px; top: 200px" then we could have a UI selector like this:

itemWithStyle: s => ({ left: `${s.x}px`, top: `${s.y}px` }),

<div [ngStyle]="store.itemWithStyle$ | async"></div>
Enter fullscreen mode Exit fullscreen mode

Then the component can stay lean and you can test this selector as a pure function by directly importing the state adapter and passing values to it. That's really nice. And where is a better place to put a pure function than in a selectors file where it can be tested as a pure function instead of as part of a component? And as part of a state adapter, it's reusable by default, whereas if you put the logic in the component template, you'd have to extract it in order to reuse it.

But after trying out putting UI selectors inside state adapters for a while now, I have decided that managing the same concern across 2 separate files was too awkward for the small benefit of having it in an adapter. It felt like I was polluting the derived selectors file with really boring UI concerns, and those UI concerns were more convenient colocated with the template anyway. Imagine if the template needed to implement this as an SVG. Wouldn't it be convenient to be able to edit this logic in the template instead of in a separate file? Also, in what situation would you want to reuse this UI logic? Probably when you want to reuse the component too, right?

But how do we define efficient derived state in the component? In StateAdapt it's actually not that hard, but it's not super easy either, and it definitely isn't convenient with the NgRx-style syntax.

Component inputs and the diamond problem

Another issue with both RxJS and selectors is the question of how to deal with component inputs.

For a long time, I just wanted component inputs as observables. But it turns out there's a small issue with this: If each component input gets a new value at the same time, each input observable would fire in succession, so if you needed to combine input values inside the component, a combineLatest would fire once for each input observable. Selectors can't help with this, since component inputs aren't part of the global store.

This problem actually has a name: The diamond problem.

But you know what does have really awesome syntax and handles the diamond problem perfectly? A reactive primitive that isn't RxJS: Signals.

SolidJS signal syntax is awesome

Here's a simple SolidJS component:

const CountingComponent = () => {
    const [count, setCount] = createSignal(0);
    const doubleCount = createMemo(() => count() * 2);
    return (
        <div onClick={() => setCount(count() + 1)}>
            Double count value is {doubleCount()}
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode

Here's the same thing with Angular and RxJS:

@Component({
  selector: 'app-counter',
  template: `
    <div (click)="count$.next(count$.value + 1)">
      Double count value is {{doubleCount$ | async}}
    </div>
  `,
})
export class CounterComponent {
  count$ = new BehaviorSubject(0);
  doubleCount$ = this.count$.pipe(
    map(count => count * 2),
    distinctUntilChanged(),
    publishReplay(),
    refCount(),
  );
}
Enter fullscreen mode Exit fullscreen mode

The SolidJS syntax is better for many reasons:

1. Derived or not?

In Angular you need to know you're going to have derived state in order to have a reason to use BehaviorSubject right off the bat. With SolidJS, all of your state that changes will be signals, and whether they eventually get used to derive other state does not matter. So you never need to rewrite a signal with different syntax after you first create it.

2. Recomputing

By default, SolidJS's signals will not recompute if the state they're derived from doesn't change. No need for distinctUntilChanged.

3. Shared

No matter how many signals are derived from a signal, it will not recalculate for each of them, unlike RxJS which runs a map function for each derived observable, unless you use publishReplay(), refCount().

4. Combining

When combining SolidJS signals, you don't need to define the dependency array up front like you would with NgRx selectors or combineLatest. You can just define it like c = createMemo(() => a() + b()).

And the issue with RxJS's combineLatest running once for each input is not a problem with signals. SolidJS signals wait for each dependency to finish running before they run. SolidJS signals elegantly handle the diamond problem.


Okay, now let's compare SolidJS with some examples using selectors. Here's the SolidJS code again:

const CountingComponent = () => {
    const [count, setCount] = createSignal(0);
    const doubleCount = createMemo(() => count() * 2);
    return (
        <div onClick={() => setCount(count() + 1)}>
            Double count value is {doubleCount()}
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode

Let's try Angular with StateAdapt:

@Component({
  selector: 'app-counter',
  template: `
    <div (click)="count.increment()">
      Double count value is {{doubleCount$ | async}}
    </div>
  `,
})
export class CounterComponent {
  count = adapt(['count', 0], {
    increment: state => state + 1,
    selectors: {
      double: state => state * 2,
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

That's actually not bad! But it's a little more code than it has to be, as you'll see below.

Angular with NgRx would be too long, but you can imagine defining the selector in the component file:

@Component({
  selector: 'app-counter',
  template: `
    <div (click)="increment()">
      Double count value is {{doubleCount$ | async}}
    </div>
  `,
})
export class CounterComponent {
  selectDoubleCount = createSelector(
    selectCount,
    count => count * 2,
  );
  doubleCount$ = this.store.select(this.selectDoubleCount);

  constructor(private store: Store) {}
  //...
}
Enter fullscreen mode Exit fullscreen mode

Basically, selectors get the job done efficiently, but I've never seen a way to use them with as little code as signals require.

Wouldn't it be nice to have a reactive primitive in Angular that's efficient by default, with minimal syntax?

What I want

How about this:

@Component({
  selector: 'app-counter',
  template: `
    <div (click)="count.set(count.get() + 1)">
      Double count value is {{doubleCount.get()}}
    </div>
  `,
})
export class CounterComponent {
  count = signal(0);
  doubleCount = memo(() => this.count.get() * 2);
}
Enter fullscreen mode Exit fullscreen mode

SolidJS's tuple syntax isn't available, since we're assigning class properties here, but this is pretty good too.

And of course there should be a way to specify Angular inputs as signals:

@InputSignal: count!: Signal<number>;
Enter fullscreen mode Exit fullscreen mode

RxJS compatibility is still necessary

Signals are great for synchronizing derived state, but RxJS is still necessary for asynchronous reactivity. If you have any nontrivial amount of experience using RxJS it should be obvious why, but I explain a lot more in this article.

So, it would be cool if the Angular team found a way to make converting from signals to observables extremely convenient:

export class CounterComponent {
  count = signal(0);
  doubleCount = memo(() => this.count.get() * 2);
  delayedCount$ = this.count.pipe(delay(1000));
}
Enter fullscreen mode Exit fullscreen mode

The pipe would convert the signal to an observable and pass the arguments to the new observable's pipe method.

Since we're accessing an observable, hiding that pipe() logic behind a lazy import could be possible, and that would allow the RxJS code to not be loaded until it was needed.

Or Angular could copy how SolidJS does it:

const CountingComponent = () => {
    const [count, setCount] = createSignal(0);

    // signal to observable (`from` imported from 'rxjs'):
    const count$ = from(observable(count)); 

    // observable to signal (`from` imported from 'solid-js'):
    const countAgain = from(count$);
};
Enter fullscreen mode Exit fullscreen mode

Summary and conclusion

I have some strong opinions, but I also seem to change my mind a lot. But it's not like I just love moving from each JavaScript fad to the next every month. It has taken a lot of experience and pain to form my opinions, and everything I learn adds onto them.

First I thought RxJS was just a slight improvement over promises.

Then I learned about the benefits of thinking reactively, and wanted to use RxJS everywhere.

Then I learned that synchronizing state was better done with memoized selectors than with RxJS. But for asynchronous logic, RxJS was amazing, and I wanted better APIs than what Angular and NgRx provided out of the box.

Then I encountered Ryan Carniato's version of fine-grained synchronous reactivity, as well as his streams about colocation in Marko 6, and lastly people's comments on the old Angular issue describing the diamond problem for component inputs. All of this was swimming around my head until my conversation with Ryan about RxJS's problems. That's when it all fell into place and I realized there was an important role for a reactive primitive that wasn't RxJS or selectors.

So,

  • RxJS is necessary for asynchronous reactivity.
  • Selectors are necessary for reusing state management patterns with state adapters.
  • A simple reactive primitive is necessary for simple, local, reactive state synchronization.

As I work to improve StateAdapt, I will need to figure out how the new reactive primitive fits into the state management picture. I will probably start by exploring how it can be used most easily with SolidJS.

We all have different pieces of the puzzle, and I have faith that the web development community will eventually find beautiful, declarative syntax and ways of efficiently training developers with the right mindset to take advantage of reactive programming, and our industry will become increasingly productive and enjoyable to work in.


Thanks for reading!

I'd love to hear your own personal experience with anything I've shared in this article.


Cover photo by Casia Charlie: https://www.pexels.com/photo/sea-dawn-landscape-nature-2433467/

Top comments (45)

Collapse
 
mpolutta profile image
Mat Polutta

And now I get it. I transitioned from AngularJs to Angular years back. I then transitioned from Promises to RxJS. Later I was assigned to a Dynamics CRM team where I created an Angular Library to cover all of the custom and missing components for our CRM apps. Well, whenever I went back to RxJS Observables for bug fixes or enhancements, it made my head hurt. Your examples make it very clear why. I am currently converting the Angular library to the Dynamics 365 PCF (which uses React). The Dynamics API provides for Promises, and that is what I first ported. Very timely, because my next component is invoking an LDAP API which I wrapped with RxJS Observables. It seems I'll keep it that way.

Collapse
 
amitbeck profile image
Amit Beckenstein • Edited on

Very interesting read. There's indeed lots of issues with Angular and RxJS and I do hope that all the reworks of Angular prove to be good solutions. Making NgModules optional was already a great step towards better DX.

Collapse
 
mfp22 profile image
Mike Pearson

Definitely

Collapse
 
omergronich profile image
Omer Gronich

SolidJS signals wait for each dependency to finish running before they run.

Not sure this is true TBH. Just tested this in the solid playground and it seems solid memos behave the same way as combineLatest here.

try running this solid code and check the console:

import { render } from "solid-js/web";
import { createMemo, createSignal } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  const [count2, setCount2] = createSignal(1);
  const increment = () => {
    setCount(count() + 1);
    setCount2(count2() + 1);
  };
  const derived = createMemo(() => {
    console.count('run calculation');
    return count() + count2();
  })

  return (
    <button type="button" onClick={increment}>
      {count()} 
      <br / >
      {count2()}
      <br />
      {derived()}
    </button>
  );
}

render(() => <Counter />, document.getElementById("app")!);
Enter fullscreen mode Exit fullscreen mode

The memo runs twice even though the signals were changed "at the same time". It can be solved using batching or scheduling but that's true for RxJs also.

The quotes are because values in JavaScript rarely ever change at the same time, I think multiple component inputs in angular can't ever change at the same time because angular always creates them in order synchronously.

Great article regardless, I enjoyed it thoroughly :)

Collapse
 
mfp22 profile image
Mike Pearson

Glad you liked it!

So what I mean by "same time" is literally same time, not just synchronous. It's called the diamond problem because you'll have a state at the top, then 2 derived states from that one, then a final derived state that combines them back again. So it's shaped like a diamond. So more like this:

import { render } from "solid-js/web";
import { createMemo, createSignal } from "solid-js";

function Counter() {  
  const [count, setCount] = createSignal(0);

  const derived1 = createMemo(() => count() * 10);
  const derived2 = createMemo(() => count() * 100);

  const combined = createMemo(() => {
    console.count('run calculation');
    return derived1() + derived2();
  })

  return (
    <button type="button" onClick={() => setCount(count() + 1)}>
      {count()} 
      <br / >
      {derived1()}
      <br />
      {derived2()}
      <br />
      {combined()}
    </button>
  );
}

render(() => <Counter />, document.getElementById("app")!);
Enter fullscreen mode Exit fullscreen mode

It's actually expected if you were to set the signal values sequentially that they would run twice like in your example. In reactive programming, you never update more than one thing for each event. (Technically you shouldn't update anything, and there should just be streams that listen to events, but whatever.) You probably know, but in React it would queue the updates and rerender only once. This actually makes imperative programming easy in React. That's something I'm not interested in.

Thanks for the comment. I didn't actually verify this before now; I just assumed based on things Ryan Carniato has said that signals behaved the optimal way. And I just checked it and it is right:

SolidJS Diamond

The diamond problem actually does come up quite often in real world apps. Not all the time, but often.

Collapse
 
omergronich profile image
Omer Gronich

Ohhh gotcha. That's cool. Thanks for the reply!

Collapse
 
sebosek profile image
Sebastian

Partially agree with @oz, about negative under tone.
However, I think it creates nice contrast to proposed bright future.
And your proposed API? Love it! โค๏ธ
Short, simple, keen, easy to use, located to scope of the component. It would play soooo nicely with Standalone components.
You know what? You've restored my faith in Angular ๐Ÿ˜„

Collapse
 
mfp22 profile image
Mike Pearson

I'm completely clueless about the implementation details, so there might be unexpected limitations from that. And I thought for about 5 minutes before coming up with this, so there's a good chance there's something better. But I like concise things.

As for the negativity, on certain topics I can't help it. This is my experience and I don't see a point in dressing it up. I couldn't decide what audience to write for, because there's something for every perspective to hate and love in this. So I just included all the truth I thought was relevant.

Collapse
 
antischematic profile image
Michael Muscat

Signals make sense for functional components. I'm not convinced that signals alone will make Angular class-based components better.

I hope that whatever solution Angular comes up with works as well in TypeScript code as it does in templates, has interop with RxJS and handles things like inputs, host bindings and queries seamlessly, and isn't an eyesore to look at.

Collapse
 
mfp22 profile image
Mike Pearson

I think it will, as long as it doesn't try to mimic something from somewhere else beyond what makes sense for Angular. If these basic requirements are met, I'm sure people will like it.

I prefer function components, but besides syntax, it doesn't make a big difference

Collapse
 
maslow profile image
Maslow

Very interesting read

Collapse
 
martinmcwhorter profile image
Martin McWhorter

Yes. A thousand times yes.

Collapse
 
alexandis profile image
alexandis

Oh goodness... It's still too much for my brain. I got rid of state in my code. And I do use behaviorsubject and combinelatest. It does work. I know it's not optimal, but all these things and approaches stuffed into Angular apps... It's just too much to get hands into all this. It needs to be simpler.

Collapse
 
mfp22 profile image
Mike Pearson

You will like signals

Collapse
 
alexandis profile image
alexandis

I already use SignalR. But I still use BehaviorSubject and CombineLatest as well... :-D

Collapse
 
bretto profile image
Brett

Did you check ? rx-angular.io/

Collapse
 
timsar2 profile image
timsar2

โ€ŒYes he did, He preferred rx-angular until stateAdapt being stable.

Collapse
 
jurugi profile image
Jurugi • Edited on

I hate you. I'm a oldschool dev who learned from scratch since the 2000s.

The thing is I hate you because your whole rant is based on some crybaby methodology like you don't want to learn the structured CS way that works really well, and that Angular mostly has. Angular has a nice structure that's unlike the others 'worth it' to learn and results in better results. You generally have a TS file, a HTML file, and obviously component benefit. But instead, you want some hyped up BS that 'you think' will help, but it is many step backwards. Underlying its core,all of these just runs Javascript anyway.

What the heck are you talking about reactive primitive blahblahblah. who cares? Angular has a much better structure than React, which usually ends up a mess that tries to incoprorate HTML into 'strings' in the javascript file, that is a total joke. In Angular that's only like an 'option' and same with placing javascript easily and seamlessly anywhere, which is 'far easier' imo than React/Vue and those with the formats you said were better. Let's not get into the even more broken Vue and other JS frameworks where things you knew how to make for years, will suddenly just 'not work' and they have 'no support' for it, and it turns out to be because some novice in their new language decided that > symbol was needed somewhere, or something totally unstructured like that where they try to 'shorten things' but it ends up 'retarded'.

Do you know why React has more jobs? Because novices copyit and use it and they get 'stuck'. Do you know why Angular has less? Because it is structured better and projects with Angular tend to be completed from start to finish. They hav less need to eventually like 'hire some expert' once your novice programmers get stuck or can't handle their own React/Vue choices and of course there's no copy pasta or indian tutorials for them to dig them out of their selfmade hole. React is not faster and there is no need for like reactive primitive whtaever this is.

You can already do what you are describing in angular more than one way. RxJS is not even a requirement but it's just a like highly used formatting for subscribing and piping data. It's again, using underlying javascript principles, once compiled, it's not 'slower' or even 'worse' like you claimed it just because when using it, you got confused at it. In the end it has the traditional HTML, TS/Javascript, pretty easy file structures to build off. react on the other hand is a child's mess that's overhyped, and I will always spend an extra day fixing a basic thing. You among others seem to think like there's some 'reactive hype' where it's faster but you are only imagining it, it literally is not faster or even more bare bones, unless in a way like, jsx files 'are' more raw than like a compiled .ts file most times, but at that point just make it all yourself in raw javascript, so you will run into less issues and compatibility problems then?

This just looks like a mess of an article where you say an ugly and nonused format must be better because it looks like a bit less but in reality the structure underneath is far worse performance.

For example you say solidJS which is more like React/Vue, and that sort of format is better, but it is actually a big mess where it's essentially just the same as using a .js file, only you may run into compiler errors or mess up on it. You try to make some outlandish claim like Angular has 'a bit more lines' so thus it must be worse, but in reality, that structure it has is way better, and the 'html' component code is not required. You can also just put an HTML file which is the default and it is just cleanly alongside each .ts as a component, by default, this is so easy for any expert who thinks TS is weird to come in and learn it, as well as, suffers no speed/other issues. Putting HTML into javascript files is a mess and is a thing novices do all the time in react, then pretend like it's the new defacto standard, not realizing like, when they compile it back to raw javascript that runs on the server, it's literally juts doing the same thing, it's a step backwards to the raw javascript days where you'd write html into .js files. e.g. when you'd literaly be like placing the table rows HTML manually each line like 'lalalaa'.

I hate that you and others like yoy are just 'cheating and lying' where you essentially just are going backwards and you learnt one script language, and you just think everything should be the way that 'you' thought it should be. Yet that 'way' is the one where experts spend more time because the frameworks are worse and get stuck on basic things again because your idea of 'better' is actually 'backwards'. Usually these things like setState and all these described things are wrappers only for Javascript's actual setLocalSession and setSessionData and yet it's a wrapper made by someone else, that ends up using that anyway.. Ridiculous to have to learn this crap just as you suggest doing and end up 5 steps backwards just because you and a few noobies wanted it.

Ifyou are like a novice team making a car app you might have copy pasted React and have this JSX format and pretend there's something special that you praise so much (no, it's all wrappers for JS existing things). If you are like an expert and want to give the best to like a client who uses banking data and car data, more sophisticated like, you'd probably want Angular, if choosing, and I'd bet even someone who isn't as expert, would run into less problems while using that structure. Why? Because simple and efficient = better in most cases. These things you say are 'lacking' are usually just you whining it's not like the weird format seen in like Vue/React jsx files and you think like if something takes 5 lines it's better than 10 lines type of ideology, (which are, of course, no benefit really, it's backwards), the problem with this is someone 'else' made that less line of code shortened and you have no idea what it's truly doing in the backend, so when that breaks by their neglect, aformat update, or anything like that, you are screwed when it's just like your '1' line that calls a plugin doesn't work, or they just ad in some stupid symbol change like =>!<(!! (who knows), instead of just fix your '100 lines' and fix the lines yourself in like a day. Your suggestions also reminding me like back to the time of manually .js file adding html into the current page, before there were like major frameworks like these which sort of benefit and help you out, because you learnt one way, but don't understand why your suggestion is going backwards.

Collapse
 
mfp22 profile image
Mike Pearson

Lol, well I love you.

You know, Angular was designed with a lot of of traditional CS principles. You could tell the creator really understood these patterns and encouraged Angular developers to understand and use them too. That's really what's great about it, right? So what is the creator of Angular doing now? He created a new framework called Qwik. I wonder what syntax he chose for that.

I have been in your shoes. I was disgusted by JSX when I first saw it, and rightfully. I remember the vanilla JS days of HTML strings imperatively slapped into the DOM like it was nobody's business. I was doing that stuff in 2013. But I have a strong feeling you have never given JSX a serious try. Declarative code is what makes code maintainable, not some artificial separation of syntax style. If they're separate concerns, why do you always edit them at the same time to complete all the same tasks? I have never used the same template for multiple components, and any use case where someone has can easily be done with a shared component.

I care about developer productivity that scales well with complexity, and reactive programming scales simply, whereas imperative programming does not. Whatever you think Angular has that removes any need for a reactive primitive, you're wrong.

How am I arguing backwards from what I wanted from the beginning if what I've publicly argued for has changed so much over the years? I've changed my mind so much I probably have lost credibility with people who don't understand what learning feels like. And you know what, there are things I've never changed my mind on, even when there was a fad going the opposite way, because I'm capable of disagreeing with the crowd. I do it all the time.

Here's what you should do. Spend a few weeks creating a complex React demo, and afterwards, if you still hate JSX, or whatever it is you think I like about React (this article was not about React), write an article yourself in such a logical manner that nobody could fail to be convinced by it. Don't tell your readers you hate them though. Love is the answer โœŒ๏ธ๐Ÿ•Š๏ธโ˜ฎ๏ธโœŒ๏ธ๐Ÿ•Š๏ธโœŒ๏ธโœŒ๏ธ๐Ÿ•Š๏ธ๐Ÿ•Š๏ธ

Collapse
 
jurugi profile image
Jurugi • Edited on

Yes, I still dislike and do not recommend react after a few instances of touching others react Frankenstein projects, where it compile a jsx file back to a js file, and things like usestate are just wrappers for session/local storage, and so on. It isn't that I couldn't rewrite it, or just use react, fyi I have completed everything I ever worked on. It's that by not swapping away and realizing limitations, they ended up with lower performance, and chose to make a quick tune up in a way, where they're likely to suffer the same issue in the future by remain ing with the bad toolsets and methodology used. In context I will never recommend writing a react project, I will only fix up or upgrade existing ones. No matter how optimal I did upgrades or fixes to some react project to get it running nicely again or add a page, If they ultimately want the lowered performance and % less efficiency by continuing to use it, I say let them learn. I'm a fan of letting those types learn the hard way, while they will even argue about how 'right' they are; or as you have done, imply like 'you probably just never used it to see the benefit', no, with me it's like the exact opposite of that.

Most of react is backwards, placing html back into js files as strings, and compiling something that in essence is func2 calls func1 style of language anyway, as it pertains react even to raw javascript. Tutorials on what 'usestate' is a total example of this, it's like some rewrite that's really not important, yet because someone made it that way, becomes a new concept to learn somehow. Let them have more jobs because people get stuck on these Vue/react formats, lacking support, and not even understand why overall structure is better even for smaller projects. They will just remain with the 'dont change that' mentality with their less than desirable react projects, that hopefully don't scale badly (but in my experience, and with proof that there's tons of react jobs in demand, of course it does, and it did scale badly for them, they just don't understand why like I can). I'm not saying it's worth it to rewrite all react to a better format either in many cases, even if that day of rewriting will many times likely save them some headaches or some problems later. If it's simple however, and likely not to change much, then I'd say leave it alone. Again I'll generally never recommend writing a new thing in what I'll call now 'gook' script (react and weird format there) in favor of actual es6 typescript or JavaScript that runs in pure form on the browser, in some form for anything worthwhile, over some more complete toolset at least like angular where you can see 'ok that's helpful' in certain ways, or jquery which is like reacts better grandpa that still works better.

Thread Thread
 
menosprezzi profile image
Guilherme Prezzi • Edited on

Dude, you have to understand (and it applies to any other tech - any senior dev knows it): The problem not is (always) the React itself. It a small simple well-designed lib (and not intended to be a fw as Angular is). If you have touched a creepy React project in the past is because the programmers that made it doesn't understand well how to design it (ok that with React, because of its non-opinative way of structure, it's easier to do things wrong), and it also apply to angular projects! Do you have experienced to get a creepy angular project?? Such one that nor even the component model is respected? A such case that the data flows from nowhere to everywhere??

If you have this such a nice experience with design patterns etc since 2000's as you described, good! try to make an App from scratch with React and apply your knowledge to it! it will be nice! The best of all worlds is to experience different things and understand what goes well and what is not!

(also: no, usState is not a wrapper to local/session storage hahaha. It's more like a BehaviourSubject than a local storage thing. It's clear that you don't know anything about it! Please, go and use it before throwing such a 'strong' opinion on the web!)

Best regards!

Collapse
 
intellix profile image
Dominic Watson • Edited on

enjoyed reading the initial comment and loved your reply. Being a fullstack developer I have always rolled my eyes whenever someone suggested swapping from Angular to React or wanting to change job so they got a chance to play with it. There are bigger problems worth solving...

It never really solved any problem we had, it was just different and more popular and that's not worth migrating for. Qwik really is a game changer and it solves a huge problem we have with server side rendering and now it's time to open your eyes and look at migrating (when it's ready and mature).

Thread Thread
 
mfp22 profile image
Mike Pearson

I like trying new things, but React never appealed to me until hooks in 2018/2019. Before hooks, Angular could be more declarative than React. After hooks, it flipped, and React's syntax was overall much simpler. So I liked both after that.

I am interested in running Angular inside of Qwik. They have an integration for React that might still be buggy. So Angular might be a while still.

Thread Thread
 
jurugi profile image
Jurugi

I agree. Just to clarify though I am not stuck trying to change one to the other, I only recognize that angular is a far better toolset that actually has benefits over react. That doesn't mean that when paid to fix or upgrade someone's monstrosity react project that it was ever great to just rewrite it in better format (like raw javascript or type script), nor that they'd accept it even if you took the day or few days and fully did that. I agree in that context, it'd be extra work, so to clarify, they get what they want but still get stuck with lower performance overall by their choice.

Collapse
 
intermundos profile image
intermundos

Another great article of why not to use angular. Over engineering at its best.

Collapse
 
mfp22 profile image
Mike Pearson

Glad you liked it!

Collapse
 
jwp profile image
John Peters

To me Angular's glory days ended about 7 years ago. The core team left after Angular 2, and it took them all that time to address the Ngmodel clash in Angular 14 stand alone components. With Svelte we see simplicity where even React is heavy weight.

Collapse
 
mfp22 profile image
Mike Pearson

There are a lot of good people working on Angular. I love Svelte, but a lot of web apps depend on Angular and it's a lot better for them to have an improved Angular than to migrate to something else. Angular is on par with React in every way except syntax, and signals will bridge that gap a bit and hopefully enable a performance boost to something literally impossible with React, and faster than current Svelte. It would take a big effort but I think it's possible.

Collapse
 
hiumesh profile image
Umesh Chandra Dani

good

Collapse
 
hiumesh profile image
Umesh Chandra Dani

good

Collapse
 
hiumesh profile image
Umesh Chandra Dani

good

Thread Thread
 
hiumesh profile image
Umesh Chandra Dani

good

Thread Thread
 
dinhphuong20z1 profile image
DinhPhuong20Z1

good

Collapse
 
xania profile image
Ibrahim ben Salah

Angular is making it hard to be happy Angular developer, it is not just RxJS.
e.g. I basically decided to never use @ContentChildren because of issues I had with it. I even tried to rewrite one of the core functionalities in Angular (the async filter) just find out that was not the issue. Also the templating issues typesafety and missing features still existing in v12 (last version I tried) was discouraging.

Collapse
 
oleksandr profile image
Oleksandr

Isnt RxJS zip do the trick for derived observables combination?
And yes it will work if adding operators to hot observable(Subject) wouldnt make derived observable cold

Collapse
 
mfp22 profile image
Mike Pearson

I remember thinking a lot about that, but sometimes an input observable will emit when others don't.

Collapse
 
oleksandr profile image
Oleksandr • Edited on

I think RxJS Observable needs kind of .pipeShare method which applies operators to hot observable and still keep it hot - it is regarding derivatives.
But regarding combineLatest - interesting how signals do it (wait until all settled for all derivatives). curious if appying debounceTime(0) can reach same effect.

Collapse
 
hiumesh profile image
Umesh Chandra Dani

good

Become a Moderator Can you help us make DEV a better place?

Fill out this survey and help us by becoming a tag moderator here at DEV.