Having tough time with the Observer pattern

You two young little whippersnappers are having a nice convo, so apologies for this geezer butting into it.

@Tommertom’s advice in this thread has been stellar as usual, but I really want to highlight one thing:

This was absolutely the crucial insight for me. It’s much less RxJS the tool than it is embracing the tenets of functional programming that ReactiveX has laundered from places like Haskell. When I was first introduced to what Angular calls “dependency injection”, it was called “inversion of control”, and I actually prefer that term, especially when talking amongst fellow oldsters for whom it truly is an inversion of the way they’ve thought about programming for decades.

I’ve developed several heuristics and metaphors that have helped me navigate this way of thinking, and on the off-chance that you may find some of them useful, here are some samples:

3 Likes

Your insights are always enlightning, so welcome! Indeed the list of articles you refer to are legendary.

The biggest shock I had was realising that virtually everything is asynchronous in web development. You can never know when something has happened nor will happen. Just outline what needs to be done when it happens. Even something seemingly obvious like getting data from local storage.

Hope we can continue reminiscing the old days :slight_smile:

1 Like

Thank you @rapropos, thank you @Tommertom for your invaluable insights !

I’ve been using the methods like Lego pieces, without the needed deeper understanding. Having the above alpha site working with RxJS made me overconfident.

Today’s class begins with RxJS… I’ll start with the above articles… I’ll work on a minimal test application. Please do bear with me if I share some Stackblitz when I hit a wall…

1 Like

You’ve alreay got a lot of great advice here and the hall of heroes tutorial is a must to complete all the more advanced context.

If it at all helps I try to think of Observables and Subjects like promises that keep and open subscription until you stay otherwise

Observable - listens to a change in a Variable or Subject

Subject - is the Promise itself

Subscription - is the event loop that listens for changes and provides callbacks

combineLatest - similar to promise all

unsubscribe - an important step for killing off any unused subscriptions to prevent memory leaks

RxJs really makes continuous subscriptions and the passing around of data/state a lot easier that promises.

In your application keep it simple. Angular really now boils down to components and services. Generally you want to split thing into at least 3 areas of concern

A Data Service - deals with fetching data and returning data

A Page / Integration component - subscribes to the data service and passes the result into the sub level components

Components - the UI components, these shouldn’t know about data services, only data in, data out

One last tip, once you start compiling and testing on the devices local XHR requests to your test data will encounter issues, don’t bother fixing it too much as at this point you’ll probably want to swap the local test data out for real api data

1 Like

Thank you for the tips. As far as the statements above, I had them backwards.

  • I have a “raw” DataService
  • I have a ContentService on top of it. DataService is only accessed thru this one. This service has convenience methods which transform the raw data (e.g. stripping out tags out of raw HTML).
  • Components accessing parts of the data thru ContentService. E.g.:
  • The header component gets the Article header
  • A PageLayout component combining ArticleLayout and ModuleLayout (list of submodules) components
  • An ArticleLayout component which dynamically loads one of the layouts (e.g. ArticleLayoutBlog, ArticleLayoutGrid, ArticleLayoutList etc), similar for ModuleLayout…
  • A Page only has the following:
<app-header> <app-page-layout> <app-footer>

The app I’m trying to implement is mainly data driven. How a page will be layout in a page will be read from a json file, there is no code specific to a page visited. Except special pages such as maps or home page, which must have special code.

That architecture seems very logical to me. It worked as you can see in the alpha above but whenever I started to go deeper in implementation (in this case dynamic loading of ArticleLayout sub components), I lost track of the subscriptions.

The simplified flow was:

json ==> DataSvc ==> ContentSvc
  ==> HeaderCmp
  ==> FooterCmp
  ==> PageLayoutCmp
    ==> ArticleLayoutCmp => ADynamicTemplate
    ==> ModuleLayoutCmp => MDynamicTemplate

What you suggested happens at a lower level here (at PageLayoutComp), I suppose… I wanted to prevent data duplication. As far as I can understand the following code needs more memory and also it is not async:

export class MyComponent {
  private data$: Observable<IData[]>;
  public data: IData[];
}
...
  ngOnInit() {
     this.data$ = this.dataSvc.data$;
     this.data$.subscribe ( data => {
        this.data = data;
     })
  }

Thus I started to pass around Observables, for example, in PageLayoutComponent template:

    <app-article-layout data$="articles$" layout$="layout$"></app-article-layout>

This is where I got lost - I think…

Disclaimer: highly opinionated post, feel free to ignore.

BEEP BEEP BEEP WHOOGA WHOOGA WHOOGA

HTML is IMHO a terrible, no good, very bad choice for a transfer language. It’s hard to parse. It’s inefficient to store and query. You will be tempted to try to shoehorn it into your templates using innerHTML. Then you will discover that innerHTML doesn’t do what you want it to, and you’ll post a question here asking why links in it don’t work or something.

Your life will be much easier if you restrict yourself to structured data formats for transfer within your app and its network backend friends. The absolute best thing would be a language that is easily serializable to JSON, because it is braindead easy to work with JSON in web apps. Barring that, if you want to work with something like Markdown, see this post.

Even if you’re scraping web pages, put some intelligence into the scraper to the point where it can spit out something more manageable than HTML.

I wouldn’t keep both of these, because it’s just too conducive to spawning bugs that happen when they’re out of sync. Pick one or the other. Also, it’s not idiomatic to prefix types with I.

:sweat_smile:

Actually these are articles & categories from Joomla! + K2 websites we own. I wrote an API to get these from the database. The layout got messed up of course as they were based on Bootstrap & I had several special classes for my layouts. For now, I’m not scraping the whole article, I’m just scraping the “introduction” part, and point to the webpage for them to read the whole article. But, some of them have HTML left I wanted to ged rid of - but this can be done in the API using Joomla!'s methods (I know they exist) - I think / I hope…

Exactly… I found it in a rather hard way and forced myself to make a subscriber soup. But if you ever want to construct a view from the raw data, I found out that I must get a copy of the actual data and work on it.

Well, I use those “dummy Interface” files as you call them and I name them as such. Is it not OK?
E.g. interfaces/iarticle.ts

export interface IArticle {
  ...
}

@rapropos, thank you for your input, really! I learned many things from your posts in the past years. And your opinions and structures you propose are very logical for me.

It’s great that you’re using interfaces instead of classes, but I would just call it Article. The leading I convention comes, as I understand it, from C#. Having never worked in that language, I’ll simply assume that there is some grand great reason for it there, but in TypeScript, there isn’t. In fact, the original version of the TypeScript Handbook explicitly advised against this exact prefixing.

I think the temptation comes from the fact that there is going to be some CArticle or something that does the implementing. That’s not how interface in TypeScript works: they’re first-class language features that need no further assistance. You can instantiate Articles directly even if they’re interfaces. You can simply bless a JSON object that comes (for example) over the wire from a backend.

articleById(id string): Observable<Article> {
  return this.http.get<Article>(`/article/${id}`);
}

I consider that one-line function the most compelling reason to use interface instead of class for business objects, incidentally. If your business objects are classes, that function gets uglier for no good reason.

Also, I don’t see any need to put them in their own files. I put the definition of these sorts of business objects in the same file as the service that manages them.

I would also advise against the whole “dynamic template” idea in general. If, however, you’re attached to it, then maybe you might want to take a look at React or Vue instead of Angular. Angular is pretty opinionated about the fact that it wants statically analyzable templates, with HTML in the template, code in the controller, and ne’er the twain shall meet. Personally, I like that choice, so I like Angular. I gather from reading code snippets here and there that the other frameworks are more amenable to mashing up code and presentation.

I would not “pass through” Observables like you’re suggesting. Do check out the “bone/skeleton” idiom in my last post: I think it’s a good fit for your domain. I wrote a message board like this one using it in Angular.

Thank you again for your input :slight_smile:
I hope you don’t mind this noob answering…

Neither did I, I’m not an M$ guy :slight_smile: Only C and C++ …

I’ve been using the IArticle naming convention merely to prevent coding errors due to name space pollution, as a help for typing in VS Code such as:

export interface Article …
export interface Articles …
public articles$ …
private articles …
private articleArr …

Whenever I type “I” and press TAB it comes correctly.

The reason I’m keeping in a separate file is because they are included in multiple components and any change in the structure will be made in one place.

In this specific application I’m struggling with, I have 3-4 common Interface files, and each module may have its own interfaces. E.g. Article is common, Book is in library module.

I actually read your posts about this last month, also examined (tried) a lot of other methods which do compilation on the fly (on an older version of Angular) etc.

What I could not convince myself is this:
Say I have 10 different Article Layouts as components
Say I have 10 different Module Layouts as components
If I have a PageLayout component, which would include all of them and use one or two with *ngIf in the template, that would become a huge component. I don’t know how much it would hurt in a PWA (and running on mobile) thou…

If I cannot do it correctly, probably I’ll go back to that…

Nope, I did not suggest that. But after I had issues with Observables, I tried to convert them to see what happens. I’ve been already using your skeleton-bone style with @Input

That would most likely go to normal if I ever solve this Observable mix-up in my brain…

One quick question… I see that BehaviourSubject is a must for triggering event propagation. I also know that in TS non-primitives are referenced with pointers - but I’m neither a JS guy.

Say we have these in DataService:

export class DataService {

  private _data     = new BehaviorSubject<IData[]>([]);   // bsubject
  readonly data$    = this._data.asObservable();          // Observable

And whenever I modify this._data I call this._data.next(newValues)

In ContentService:

export class ContentService {

  private data$: Observable<IData[]>;
  public content$: Observable<IData[]>;

  constructor( private dataSvc: DataService ) {}

  ngOnInit() {
    this.data$ = this.dataSvc.data$; **// does it subscribe?**
    // this.data$.subscribe ==?== this.dataSvc.data$.subscribe **// are these the same ???**
  }

The questions are in the last lines…

“A must” is stronger than I’d put it, but they’re a good choice if you don’t need a tristate, in which you need to differentiate between “we looked, and there’s nothing” and “not sure yet”. Typically, you don’t care about that distinction, in which case you can declare a BehaviorSubject<Thingy[] | undefined>, where undefined represents the initial state before (for example) stored data has been retrieved from on-device storage at startup, or before the first network fetch has happened. Consumers who can’t deal with undefined can simply use the RxJS filter operator to effectively turn that BehaviorSubject into a ReplaySubject that hasn’t emitted anything yet.

asObservable is IMHO useless. Instead of exposing a public property, I would simply have:

watchData(): Observable<IData[]> {
  return this._data$;
}

You get the exact same type safety as with asObservable, allow yourself more freedom to modify the implementation without breaking clients, and it’s (admittedly uber-slightly) faster. Similarly, you can decide whether external forces can update things by whether or not there is a pokeData method.

No. It copies the reference, which has zero knowledge of what is contained therein. I wouldn’t do this assignment - instead just:

@UntilDestroy() // from @ngneat/until-destroy, handles unsubscribing automatically at teardown
export class ContentService {
  private data: IData[] = [];
  constructor(dataSvc: DataService) {
    dataSvc.watchData().pipe(untilDestroyed(this)).subscribe(d => {
      this.data = d;
      // do whatever else is needed when upstream changes
    });
  }
}

Yes in all but the most pedantic sense, and if you do it without calling asObservable as suggested above, then yes even in the most pedantic sense. Here’s what asObservable really does:

asObservable(): Observable<T> {
    const observable: any = new Observable<T>();
    observable.source = this;
    return observable;
}

It creates an Observable that passes through everything it receives from the underlying Subject, but which does not have the ability to do other Subject-y things like calling next on it. The reason I say it’s pointless is that as far as type safety is concerned, simply returning a Subject<T> as an Observable<T> does the same thing.

Imagine in C++ you are returning a pointer to an interface or a base class. Same deal: it’s impossible for outsiders to access any of the fields outside of that, even if the underlying object has them (unless they resort to unsafe casting voodoo, which obviously you could do in TypeScript as well, but shouldn’t).

Wow, thank you @rapropos !

I examined so (really sooo!) many examples which burned my brain. I’m very used for getters & setters, it is nice to have them back.

I’ll implement these quickly into a stackblitz, as I have problems with “resetting” the data…

It does mean a bit more overall code yes, but you’re repeating smaller simpler repeatable blocks.

for me I usually have same <app-header> <app-page-layout> <app-footer> approach but do it at component level the app component itself really just handles routing, storage and state it doesn’t really have much of a template, maybe a slideout menu at most…

I split the data services into groups.

The first is any data that can be preloaded happens in

data.service -> init.service -> app.component

and is loaded into the Webviews local IndexDB. The init.service can be accompanied with a state.service which can store bits of application state and global Observables and Subjects. Later if state starts getting complicated you can easily retrofit a Redux/flux pattern …but always later.

In Ionic/Angular there’s the idea of a “Page” level component which is done in the CLI tool and it’s always best to use the CLI to generate your files and folders. So from there it’s

data.service -> mypage.page -> ...sub.components

or

data.service -> mypage_got_a_bit_complicated.service -> mypage.page -> ...sub.components

When designing the components of a view it can get complicated and usually a time comes where you want to reuse one of your components in a different project.

Try to keep them small and functional just data goes in something gets displayed, the user does something or the data is modified programmatically @Input and the result outputs
@Output @EventEmitter @Subject, back to the page level component, it’s all tied together there or in whatever code behind pattern you prefer.

this is just another approach to consider, it’s worked for me but there’s always a better or different pattern out there that works for you.

I’d argue this is a matter of opinion. Mine is that one is better off writing the files from scratch, especially when starting out, in order to familiarize oneself with what is really going on. I also think that the lazy-loading foisted upon people by default with the generators causes confusion that greatly outweighs their value, again especially exacerbated when one is new to the framework in general.

It sure is only a humble opinion of one and the generators themselves are only really the opinion of their creators. There’s plenty of reasons you might not want to roll your own depending on the scale and complexity of the application.

But in general I’d argue back that if you’re not familiar with a framework or application architecture, Ionic and it’s supported frameworks have pretty good generators that will scaffold applications in a standardized way that will always work well, are easy to understand and follow the upgrade path. Great for learning and still good for most use cases of small to medium applications.

To lazy load or not is a whole other subject of debate, but yeah it is annoying and confusing to setup, having the generator deal with it saves a lot of time and rolling out of lazy loading strategies is easy, agree it would be nice to have the option to disable it though.

You’re totally right that generators aren’t a silver bullet for every application, but they are a great place to start from.

@rapropos, I could make my multilevel test app work with the information you provided, thank you ! I only used BehaviorSubject, watch, peek & setters.

On the other hand:

  • I had to console.log every step to find multiple executions caused here and there
  • At the ContentService I had to define _content$ also as BehaviorSubject for being used at upper levels. I have/had problems with multiple processing at this level.
  • I had to put some if-then-else to check the initial “undefined” case where the business logic needs to access contents.

I have two questions regarding your code in constructor with “untilDestroyed”:

  • In many examples subscription code is ngOnInit. I can see that putting the subscriptions into the constructor has no problems - and I think - provided that “undefined” case is handled. And this leaves ngOnInit for @Input related variables only. Can I say that?
  • I’ve been extensively using the async pipe in templates. As far as I know these are also subscriptions. Will they collide with the code in the constructor? I think I see some leftover multiple executions because of that. Should I drop the pipe or is there any other possibilities?

And one feeling I have to express: I would never think a BehaviorSubject to be a main data structure. For me a data structure is a record, array etc. Having the real data burried in BS is just not natural for me.

And another one: I learned that signal (event) flow is major part of the design. While designing reusable components one has also think this aspect beforehand. And when writing the word “subscribe” I need to think twice. If you do destructive data manipulation (e.g. replacing content) you may fire multiple times.

That brings me to @digital-pollution’s suggestion: Take the data structure out of the subscription at a higher level and pass the data towards lower levels. If event flow is working, the data will be updated.

Unfortunately I could not upload the files to stackblitz as they are, that environment is also new to me. After so much effort it could be useful for other newbies. And of course: I might have other major misunderstandings that you all might find at a glance.

Hi there. I like to chip in. I think ideally/if possible you don’t subscribe (and unsubscribe) explicitly using code, but let the async pipe do it for you - if you can avoid it. You just need to use the rxjs magic transform, join etc the datastream into what you need for the user.

In an ngIf to create a local variable or a ngFor to do the loop are the possible ways to get data from observable streams in the template.

I don’t think it is a data structure. It is the road to transport and notify content of data structures.

You should write components to be fairly isolated (pure is the right word?) for testability and reusability reasons, making them agnostic about when what will happen. But when it happens it should know what to do for the user.

Angular should do the trick on change detection (and if you create the state mgt pretty pure and immutable, you can change the detection strategy to Push, so the performance gets better).

The one thing you may bump into as well, if you need to observe one data stream in multiple objects, then the usage of asObservable() comes in handy - looking at some of the posts before.

Afterall, you can only subscribe once to an Observable instance. (right?)

Have a good one!

Tom

I wouldn’t stress overly much on the distinction between setting up subscriptions in constructors versus ngOnInit, especially in services that typically aren’t reaped until the app is.

Another religious argument, and seeing as how @Tommertom has already covered the pro-pipe position, I’ll take the opposite. I tried to love AsyncPipe. I really did. In the end, after about a month of wrestling with it, I sacked it, and here’s why.

First, as I’ve already mentioned, is the philosophical objection: I don’t like mixing logic and presentation: I find it hampers readability. Sometimes things that didn’t used to be asynchronous become so. Sometimes it goes the other way. Templates shouldn’t be bothered with this.

Secondly is the practical matter of “how do I debug this stuff when things go wrong?”. I’ll give you a concrete example that actually happened to me. You have an Observable that is backed by an HTTP request, subscribed to by an AsyncPipe in a template. You decide to add an app-wide network “busy-ness” indicator by writing an HttpInterceptor that keeps track of a reference count of open connections. Now you inadvertently are subscribing to the same HTTP request twice, and that’s (a) a bad idea and (b) extremely hard to debug because you can’t set breakpoints in the AsyncPipe’s subscriptions. It took me taking out the AsyncPipe before I could see the double subscription. I spent several hours staring at server logs wondering why there were being multiple duplicate requests to the same endpoint.

So I prefer to manage subscriptions strictly in the controller, and pretend the AsyncPipe doesn’t exist. The way I see it, the biggest value of AsyncPipe is that it manages the unsubscriptions for you, and since ngneat/until-destoy does that job in what I would consider an even less obtrusive manner than AsyncPipe, I use it instead.

1 Like

(Without taking my lightsaber out)

I cannot say Angular is promoting an MVC architecture or similar. As long as ngIf and ngFor are there, it is designed in the contrary.

If your component is small, where you just get the input data and present it in loop, async pipe seems to be easy to code.

On the other hand, if you do some data manipulation and want to debug, it can easily become a hell. If you look at my first post, you will see that I was there. On the console I saw 200+ deep calls to to click and watch for variable values to find out where it is coming from.

I coded in 30+ languages and this environment is not the best as far as debugging is concerned - it is not even on live environment. I can compare it with my hardware debugging experiences, where some noise is coming from an unknown source and causing HW interrupts (it turned out to be a unshielded fan speed controller in the room).

So, what I learned is: I will either use pipe or code in component, not both. I have been doing it wherever I see fit :confused:

1 Like

On Subjects / BehaviourSubjects, I’m not recommending the data or application state sits inside the Subjects themselves.

You’ll still want to hold any in memory data and state at least in a global variable, or with whatever state management pattern you prefer. This is simply because there will likely be many times you want to access state programmatically without the need for managing a subscription.

Subjects are just broadcasters to notify other components of state / data changes. They could / should return new state / data, but maybe shouldn’t be used as the source of truth, just the messenger.

edit: imo forget the async pipe, do async await in the class only.