3

I am in quite a pickle and it could be the approach that I have been taking with learning about rxJS and Observables.

Preface

Our application uses Angular to connect to our public API layer (C#). The type of object that is returned from my ASYNC call is of Observable< ListResultDtoOfNoteDto >. This stream is passed to a child component which loops through the items. This same stream also allows a user to add new note of type NoteDto or edit notes. Ideally when these actions are done the list of notes will reflect the updated change without having to call the same service call for getting the notes again.

Code

[notes.component.html]

<div *ngIf="(notes$ | async)?.items as notes; else loading">
    <note-card [noteData]="notes" (noteClick)="editNote($event)"></note-card>
</div>
<ng-template #loading>
    <div class="notes__container">
        <div class="note-card">
            <div class="note-card__primary-action">
                <div class="note-card__head">
                    <h2 class="note-card__title note-card-typography--headline1 text-center">Loading...</h2>
                </div>
            </div>
        </div>
    </div>
</ng-template>

[notes.component.ts]

import ...

@Component({
    ...
})
export class NotesComponent extends AppComponentBase implements OnInit {
    constructor(
        ...
        private _noteService: NoteServiceServiceProxy
    ) {
        super(injector);
    }

    public notes$: Observable<ListResultDtoOfNoteDto>;
    public userId: number;
    ...

    ngOnInit() {
        ...
        this.getNotes();
        ...
    }

    getNotes(): void {
        /*
            Binding to ASYNC pipe; which doesn't need a subscription since it is automatic?
            Returns type: Observable<ListResultDtoOfNoteDto> which is an array of NoteDto
        */
        this.notes$ = this._noteService.getUserNotes(this.userId, null);
        ...
    }
    ...

    public saveNote(note: NoteDto, mode: string) {
        if (mode === 'new') {
            this._noteService.addNote(note).subscribe(() => {
                this.cleanUp();
                this.getNotes();
                this.notify.info(this.l('SavedNoteSuccessfully'));
            });
        } 
        ...
    }
}

All of the functionality minus the clickEvent in the child happens within the parent. Which loads a form (reactive form methodology) that can take a new note and save or edit an older note to update.

My issue

I am just wanting a way to save the note and update the list without having to call this.getNotes() again in the subscription when a new note is added.

(I can provide more details if needed!)

EDIT: Forgot to show save method currently

2
  • 1
    does your notes service contains the direct api call? if you don't want to call getNotes again, you would need to store the data in a service and update that data after the add call was done (usually with a HTTP POST to create a object you would get the created object back and hence you could add this object to your service containing your data). If your application (many components) rely heavily on this data basis it might be worth checking out github.com/ngrx/store otherwise you could just write a singleton service containing your data. Commented Mar 5, 2019 at 23:00
  • I this not being done technically within the this.notes$ = this._noteService.getUserNotes(<>) in my getNotes() method? Also if you look at my SaveNotes() method I don't think I am utilizing ASYNC properly since I am calling GetNotes() again and recasting that service call, again Commented Mar 6, 2019 at 14:29

1 Answer 1

4

One way in which you can achieve this is to have a private BehaviorSubject inside of your NoteServiceServiceProxy which will always contain the latest value of notes.

Like: private _notes = new BehaviorSubject<ListResultDtoOfNoteDto>(null)

  • and you publicize a function that is only for consumers to subscribe.
  • Your API call will only update the subject with the response
public getNotes(): Observable<ListResultDtoOfNoteDto> {
    return this._notes.asObservable();
}

public getNotesFromApi(args): {
    // Api request
    this.httpClient.get().pipe(tap(result) => this._notes.next(result))
    // etc...
}

Then all you need to do in your component is declare your observable something like

notesData = this. _noteService.getNotes();

inside init just make request to API to get the notes and it will automatically update your notesData which should be subscribe on your template via async pipe output.

When you save all you need to do is just add a concatMap at the end so that your save goes first then you request the update from your api.

So something like

    this._noteService.addNote(note).pipe(
        concatMap(() => this._noteService.getNotesFromApi()))
        .subscribe();

This again will update your notesData variable which is on the template.

I hope you understand how this flow works and if not feel free to ask.

Hope this helps!

Sign up to request clarification or add additional context in comments.

3 Comments

So the API is wrote in C# and we're using the ASP .NET Zero product which has a process that converts the C# to auto-generated JS (Angular) as services. So the services are pre-generated and pipeline directly into the API calls. Would it be possible to do the first 1/2 of your answer within the notes.component.ts? The flow makes complete sense, I just can't modify the auto-generated service-proxy since future changes would just overwrite the file.
Yea, i mean if you have some constraints then you have to tweak it to your flavour. I think in your scenario having the state inside the component would also do the job. As the flow remains but the separation isn't there.
In resolving this, I modified off of your answer and made a private dataStore like object that I could push and pull data from and bind back to the observable that we made, which is pulled from a public variable as the async object. Everything is working amazingly well now and I don't have to manually hit the API again to refresh the component. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.