Persisting data

What if we need to persist what is coming from a form in a component? Well then, we need to do the following:

  • Expose an add() method on our service
  • Do an http.post() call
  • Poke the getTasks() to ensure it refetches the data

Let's start with the simpler case of adding a task from a component. We assume the user has entered all the necessary data needed to create a Task in the application UI. An addTask() method has been invoked from the component, which in turn invokes a similar addTask() method on the service. We need to add the last method to our service and also in that method call an endpoint with a POST request so our task gets persisted, like this:

export class TaskService {
addTask(task) {
return this.http.post('/tasks', task);
}
}

At this point, we assume the calling component is responsible for doing all sorts of CRUD operations on a component, including showing a list of tasks. By adding a task and persisting it, the mentioned list would now lack a member, which is why it makes sense to do a fresh call to getTasks(). So, if we had a simple service with just a getTasks() method, then that would just return a list of tasks, including our newly persisted task, like so:

@Component({})
export class TaskComponent implements OnInit {
ngOnInit() {
init();
}

private init(){
this.taskService.getTasks().subscribe( data => this.tasks = data )
}

addTask(task) {
this.taskService.addTask(task).subscribe( data => {
this.taskService.getTasks().subscribe(data => this.tasks = data)
});
}
}

OK, so this works if we have a simplified TaskService that lacks our pretty store/feed pattern. There is a problem, though—we are using RxJS wrong. What do we mean by wrong? Every time we use addTask(), we set up a new subscription.

What you want is the following:

  • One subscription to a stream of tasks
  • A cleanup phase where the subscription is being unsubscribed 

Let's start by tackling the first problem; one stream. We assume that we will need to use the stateful version of our TaskService instead. We change the component code to this:

@Component({})
export class TaskComponent implements OnInit{
private subscription;

ngOnInit() {
this.subscription = this.taskService.store.subscribe( data => this.tasks = data );
}

addTask(task) {
this.taskService.addTask( task ).subscribe( data => {
// tell the store to update itself?
});
}
}

As you can see, we are now subscribing to the store property instead, but we have removed the refetch behavior inside of the taskService.addTask() method to read like this:

this.taskService.addTask(task).subscribe( data => {
// tell the store to update itself?
})

We will instead place this refresh logic in the taskService, like so:

export class TaskService {
addTask(task) {
this.http
.post('/tasks', task)
.subscribe( data => { this.fetchTasks(); })
}
}

Now, everything works as intended. We have one subscription to our task stream in the component and the refresh logic is being pushed back to the service by us poking the fetchTasks() method.

We have one more order of business. How do we deal with subscriptions, and more to the point, how do we deal with unsubscribing? Remember how we added a subscription member to our component? That took us halfway there. Let's implement an OnDestroy interface to our component and implement the contract:

@Component({
template : `
<div *ngFor="let task of tasks">
{{ task.name }}
</div>
`
})
export class TaskComponent implements OnInit, implements OnDestroy{
private subscription;
tasks: Task[];

ngOnInit() {
this.subscription = this.taskService.store.subscribe( data => this.tasks = data );
}

ngOnDestroy() {
this.subscription.unsubscribe();
}

addTask(task) {
this.taskService.addTask( task );
}
}

By implementing the OnDestroy interface, we have a way to call unsubscribe() on the subscription and we do that in the ngOnDestroy() method that the OnDestroy interface makes us implement. Thus, we clean up after ourselves.

The pattern of implementing the OnInit interface and the OnDestroy interface is something that you should be doing when creating a component. It is a good practice to set up subscriptions and anything else the component needs in the ngOnInit() method and conversely tear down subscriptions and other type of constructs in the ngOnDestroy() method.

There is an even better way, though, and that is using the async | async pipe. The async pipe will remove the need to save a reference to a subscription and call .unsubscribe() on it, as this is handled internally in the async pipe. We will talk more about the async pipe in the upcoming sections in this chapter, but here is what the component would look like leveraging it instead of the OnDestroy interface:

@Component({
template: `
<div *ngFor="let task of tasks | async">
{{ task.name }}
</div>
`
})
export class TaskComponent implements OnInit{
get tasks() {
return this.taskService.store;
}

addTask(task) {
this.taskService.addTask( task );
}
}

Our code just removed a lot of boilerplate, and the best part is that it is still working. As long as all your data is being displayed in a component, then the async pipe is the way to go; however, if you fetch up data that is shared between other services or used as a precondition to fetching other data, then it might not be so clear-cut as to be using the async pipe.

The important thing at the end of the day is that you resort to using one of these techniques.  

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset