0

Although I feel this answer is relatively close to my problem and got some reputation, I don't get it right. I read a lot of posts on how to use the "new" style of Observer-pattern ((...).pipe(map(...)).subscribe(...) and *ngFor="... | async") in Angular and now also stumbled across How to Avoid Observables in Angular. I don't want to avoid reactive behaviour; I want to have changes in the REST-API to be reflected "live" to the user without reloading the Observer. That's why I want to subscribe an object (and therefore also its properties) to an Observable (and the 'might-be-there' values from the data-stream in it), right?

In my template I have:

<p>Werte:<br><span *ngFor="let attribute of _attributes | slice:0:5; index as h">
 {{attribute.name}}: <strong>{{getParamNameAt(h)}}</strong> </span><br>
<span *ngFor="let attribute of _attributes | slice:5: _attributes.length; index as h">
 {{attribute.name}}: <strong>{{getParamNameAt(h + 5)}}</strong> </span></p>

In my component I have:

private _attributes: Attribute[];

constructor(private attributesService: BkGenericaAttributesService) {
    attributesService.getAttributes().subscribe({ next:  attributes => this._attributes = attributes });
  }

  getParamNameAt(h: number): string {
    let attrVal = this.bkHerstArtNr.charAt(h);
    return attributes[h].subModule.find(param => param.subModuleValue === attrVal).subModuleName;
  }

and as service I have:

const localUrl = '../../assets/json/response.json';

@Injectable()
export class BkGenericaAttributesMockService implements BkGenericaAttributesService {

  constructor(private http: HttpClient) {
    }

  getAttributes(): Observable<Attribute[]> {
    return this.http.get<Attribute[]>(localUrl).pipe(
      tap((attributes : Attribute[]) => attributes.map((attribute : Attribute) => console.log("Piping into the http-request and mapping one object after another: " + attribute.name))),
      map((attributes : Attribute[]) => attributes.map((attribute : Attribute) => new Attribute(attribute.id, attribute.name, attribute.title, attribute.description,
        (attribute.parameters ? attribute.parameters.map((parameter : Parameter) => new Parameter(parameter.id,
          parameter.name, parameter.value)) : [])))));
  }

My problem running the application at this point is the 'to-create-on-stream' Attribute-objects and "nested" Parameters[]-array (created by pushing Parameter-objects into it) pushed into the _attributes-array from the httpClient's Observable: Piping into the http-request and mapping one object after another: undefined.

Apart from this - is my construct the right way to read values from a JSON-file (or alternatively an API-stream, which may change while a user visits the SPA) into properties of multiple objects displayed on the Angular view?

With the answer mentioned above - or the way I thought I have to translate it into my code - I start to doubt that I'm really understanding (and using) the reactive Angular way with Data-Providers <= Observables => Operators => Subscribers and finally Observers displayed to the user.

I really am confused (as you can read), because a lot of answers and descriptions that I found so far use either older patterns (before Angular 5.5?) and/or partially contradict each other.

Do I handle the API-changes in the right place? Has the array for the template's *ngFor-directives to be an Observer (handled with | async) or will the changes of respectively within the array be handled by the model behind the template and the template grabs its new values (with interpolation and property binding) and also directives with a change in the components properties without asyncing?

Briefly: Is there a for-dummies default instruction to "stream-read" values from a http-request into multiple Typescript-objects and their properties concurrently displayed in a template with on-stream-changing directives rendered only in the relevant DOM nodes, the Angular 8 opinionated way? "Stream-reading" meaning: pulling and pushing (only) if there are changes in the API, without wasting resources.

Jochen Haßfurter
  • 875
  • 2
  • 13
  • 27
  • You have written a ton of text making it confusing to understand what the problem really is. You need to re word this question. From what i gather, 1) you want the client to display changes as soon as there's a change on the server and 2) you want the changes to happen if-and-only-if something has changed on the server. Am I correct ? – Delwyn Pinto Dec 20 '19 at 07:03
  • Yes, you are right - I will rewrite the question as soon as I get a better understanding. And the changing (json) data of a backend has to be mapped into multiple objects which depend on each other (Attribute-objects which have depending Parameter-objects) and which have to be displayed to users live on change. There is really much discussion about how to do this the right way in currenct Angular (e.g. https://stackoverflow.com/questions/41707721/ngrxstore-and-angular-use-the-async-pipe-massively-or-subscribe-just-once-in-t?rq=1 and many many more...) - as far as I read the last days until now – Jochen Haßfurter Dec 20 '19 at 11:57

1 Answers1

0

If I understand right, you want the server to push changes to the client, as soon as they happen, so that the client can react and render accordingly, right ?

this.http.get is a single call, that will resolve with a snapshot of the data. Meaning that it won't automatically update just because some data changed in your backend.

If you want to notify the client about new data or even send that data directly to the client, you'll need websockets.

There is also some problems with code:

  1. if you .subscribe(), you'll need to .unsubscribe(), otherwise you'll end up with a memory leak.
  2. param => param.value === attrVal, where is attrVal coming from, I don't see it being set aynwhere in the method ?
  3. You should use the async pipe, as it unsubscribes automatically for you.
  4. You don't neet to create class instances via new in your service, instead your Attribute typing should be an interface.
Zano
  • 86
  • 3
  • Thank you for your explanation. I see I will have to learn about websockets. I changed my code above regarding your question Nr. 2. If I use async: do I have to reach an Observable to the template directly? And can I update the this.http.get with a trigger (e.g. if an user clicks somewhere)? Thanks again for Nr. 4, I'll try to rewrite my code the DI way here. The whole app is running (without services) at https://bk.generica.net, the sources (master and branch with this service "try-out") are at https://github.com/Mesqualito/ngBkKeyCoder. – Jochen Haßfurter Dec 19 '19 at 19:33