Introducing the store/feed pattern

We can do better than this, though. We can do better in the sense that we really don't need to create that last array. You might think at this point, let me get this straight; you want my service to be stateful without a backing field? Well, kind of, and it is possible using something called a BehaviourSubject. A BehaviourSubject has the following properties:

  • It is able to act as an Observer and Observable, so it can push data and be subscribed to at the same time
  • It can have an initial value
  • It will remember the last value that it emitted

So, with the BehaviourSubject, we essentially kill two birds with one stone. It can remember things that were last emitted and it can push out data, making it ideal to use when connecting to other data sources such as web sockets. Let's add it first to our service:

export class TaskService {
private internalStore:BehaviourSubject;

constructor() {
this.internalStore = new BehaviourSubject([]); // setting initial
value
}

get store() {
return this.internalStore.asObservable();
}

private fetchTasks(){
this.http
.get<Task[]>('/data/tasks.json')
.map(this.mapTasks)
.do(data => {
this.internalStore.next( data )
localStorage.setItem('tasks', JSON.stringify(data))
})
.catch( err => {
return
this.fetchLocalStorage();
});
}
}

Here, we are instantiating the BehaviourSubject and as we can see its default constructor takes a parameter, an initial value. We give it an empty array. This initial value is the first thing to be presented to a subscriber. It makes sense from an application standpoint to showcase a first value while waiting for that first HTTP call to finish.

We also define a store() property that ensures that when we expose the BehaviourSubject to the world, we do so as an Observable. This is defensive coding. As the subject has a next() method on it that allows us to push values into it; we want to take that ability away from anyone that is not in our service. We do this because we want to make sure that anything added to it is handled through the public API of the TaskService class:

get store() {
return this.internalStore.asObservable();
}

The last change was the addition made to the .do() operator:

// here we are emitting the data as it arrives
.do(data => {
this.internalStore.next(data)
})

This will ensure that any subscribers to our service will always get the last emitted data. Try it yourself with a code like this in a component:

@Component({})
export class TaskComponent {
constructor(taskService: TaskService ) {
taskService.store.subscribe( data => {
console.log('Subscriber 1', data);
})

setTimeout(() => {
taskService.store
.subscribe( data => console.log('Subscriber 2', data)); // will get the latest emitted value
}, 3000)
}
}

At this point, we have made sure it doesn't matter when you start subscribing to taskService.store. Whether it's immediately or after 3 seconds, as the preceding code shows, we will still get the last emitted data.

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

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