Having tough time with the Observer pattern

I’m a seasoned Computer Engineer (read “old”) coming from procedural era (Pascal et.al.). A couple of years ago I implemented an Ionic 3/Angular/Cordova project for Android where I mainly used Promise’s.

Nowadays, to refresh myself with a useful example, I’m implementing a “CMS like” PWA with Ionic 5 + Angular 10 + Capacitor. It has different/unrelated/semi-related modules which have their own data, from local JSON files and from generated from remote API’s which we own. It basically is a portal combining data from multiple websites’ into an app-like structure with some additional hard-coded data.

After a months work I found out that I’m forced to only using observables, but I think I also found the so called “observable hell” (I miss the good old days)…

Here is my idea of implementation:

  • Data Service - providing the data, mainly in raw format (a singleton object)
  • Content Service - A higher level interface which handles the raw data to adjust for the application.
  • Components - Using content service to render some stuff (menus, header, footer, dynamic loading of other components to render templates for articles/modules in “CMS” etc)
  • Pages - Using Content Service and Components to render the final page.

I try to combine all off them with Observables, so if one process modifies the data (e.g. a menu click) the view changes.

My first question is: Is this the way to go?

I could make a first alpha with dummy data and no business logic work. The first alpha of an alpha can be seen here with some dummy data:

Then I started to implement more.

After a while, I started to get hard to debug runtime errors, each one consuming more time, those only reference main, polyfill & vendor.js. Each time I find the culprit (mainly data not ready) I started to add if-then-else or ngIf controls, .subscribe here and there, get to local variables and try to use them, move code around between ngOnInit and other hooks, put/remove async here and there etc etc.

There must be a major misunderstanding from my side, although I spent a week to read and re-read documentation and many tutorials. Version differences also do not help at all. And most probably I’ve got tired, my brain is neary to explode…

My second question: What is the correct way of chaining observables between my layered objects?

Probably if I would only use DataService and include it everywhere it would work. But when layered (logic dictates that), I cannot get rid of errors.

Must each level subscribe to one lower levels’s data?
Where should it implemented? ngOnInit?
Do need to (re-)implement getters/setters at each level?

I tried to write a minimal code today but again I failed to run it. If I can get some pointers and/or a link to a good tutorial, probably I can correct the code and present here.

Any input is very much welcome.

Hi

Sorry to hear and I remember my own frustrations

I strongly recommend going through the FULL tour of heroes example at angular.io to get the basic architectural principles right

Then chain two streams using rxjs operators like switchmap etc

Then start building your own use case slowly and assuring each individual step works

2 Likes

You are correct of course… I only did parts of the Heroes… I’ve been reading the announcements and had some glimpses in the past years, but Ionic, Angular and rxjs changed so much, to the point where you need to start learning from scratch.
The old tools I remember just do not work…
Tomorrow is school day - once again :frowning:

1 Like

Well enjoy school!

I find learning to rephrase my problems in rxjs (software and maybe even real life) one of the greatest and most rewarding experiences even since I learned to program 37 years ago - and still enjoy it to the max

Someone said: everything is a stream - so go with the flow!

2 Likes

Probably you also started with Commodore 64 (or Sinclair) basic & assembly language, like me - if not Cobol :slight_smile:
What I see from my last month’s experience is Ionic became mainly Angular (or others) with addition of ornaments, and Angular’s flow is mainly rsjx.
My problem is with the speed they are evolving. I don’t do these full-time. When I thought about updating my older Ionic 3 app to Ionic 4 last year, I just kept myself doing it, after I saw the range of changes.
I can see the need for advancement but it does not help if a programming language or framework changes so drastically. Being more a C/C++ or PHP guy, I hate JS from the first pop-up I programmed. Most languages expand with libraries but the foundation stays the same. Alas, “it is the way” for today and I plan to use it not only for PWA but also as UI for my IoT projects. The environment Ionic provides suits very nicely with my requirements.

Now, it is time to be a Hero…

Those were the days…C64/Sinclair. My first rubber keyboard was a Mattel Aquarius though, then moving over to MSX, including asm indeed. I never got along well with remembering truckloads of PEEKs and POKEs. Such as waste of time :slight_smile:

And indeed, after drilled in C/Pascal (no COBOL, but LISP - some PHP but kind left it) I frowned upon seeing my first sight of JS. What a horror-show! Proven by the number of sites still dedicated showing off the freaky things it can produce. And now look at us…or at leas me… spending much of leasure time in JS (and bit of work). Good thing there is Typescript.

As to framework, there is still a choice to be made if you like. Ionic has become pretty agnostic to framework (as long as it is web). React if you want to be hip, of VUE if you like to be into startup-scene.

Personally, I am very keen to use Svelte. Maybe the closest thing you get to Vanilla JS (and hence performance/lightweight), with some superpowers that alow you to do more with less code - routing, animation, statemgt, styling etc.

I only revert to Angular (which updates every 6 months) because of the solid and aligned suite of toolboxes/CLI stuff and good tutorials around, so you can be productive without too much of research. I like the guidance…

Anyway, nice to reminisce a bit. Good luck and happy to look at some code if there is an issue.

Ideally through Stackblitz :slight_smile:

Everybody can be a Hero!

Shameless plug:


And my own project: https://ionicsvelte.firebaseapp.com/
1 Like

OK, after half a days overview of Angular, I found nothing new - except some details which I’ll be looking into such as use cases of PromisLike etc.

My main culprit is in my (non-)understanding of rxjs. After I had some problems I had found the following tutorial and tried to implement it:

In my minimal test, after I remove them and just use “return of (myData)” and subscribe to it on the upper level, I could add new records onClick and they propagated top-to-bottom-to-top and they are automatically listed.

Now, I fail to understand the need of concepts in that tutorial. So I need to work on rxjs more in detail.

BTW: My interest and masters thesis was robotics, image processing and (old-school) AI in late 80’s. I used LISP extensively in research those days, I missed car, caar, cdr :slight_smile:

Not sure if it helps, but if u come from imperative programming paradigm you in some point in time find yourself trying to understand component lifecycle and/or creating proprietary event busses to do what observable magic brings you: making sure the right data state is present in every component.

This you don’t want to venture into, but use the observable-service pattern only (as a singleton provided in root) to ensure state everywhere in your app is the same.

RXJS just/also brings in a nice set of operators to manipulate data streams into the thing that makes the end user happy (and yourself) - RXJS marbles… sometimes difficult to understand, but once you do, extremely useful

2 Likes

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…