3

(My question is related to this one but that deals with data that resolves once, whereas I need repeated resolution.)

I download 400 items, and filter that to a default 20. Users can change the filters and thus the 20 results shown to them. Should I use a Service to store the data and to apply the filters, or should I just handle that in the parent component? If the former (which feels like better practise), I need some help in wiring that up.

I have the following Service definition, which will download the data:

@Injectable()
export class RestosSvc {
    http: Http;
    data: {
        restos : Array<Resto>;
        recommendations: Array<Resto>;
    };

    constructor(http:Http) {
        console.log('RestosSvc constructor');
        ...

In short, restos becomes a 400-strong list from a server at bootstrapping, and recommendations is a chosen handful of those. Then, based on user input, the choice of recommendations changes.

My plan is to use this html (component home, called by top-level app)

<h1>Home</h1>
<filters></filters>

<map [recommendations]="recommendations"></map>

<list [recommendations]="recommendations"></list>

And then I need to find a way to get recommendations refreshed once the data is downloaded and when it subsequently changes due to changes in filters used.

Questions:

  • should Filters talk directly to the Service, or be provided a event handler by its parent(s) which themselves talk to the Service?
  • how do I set up the Service so that, once it has updated recommendations, this information is provided to .

In my Angular 1 code I had filters send the new filter information to the service, which calculated what to show, and then $broadcast an event so that the components could refresh themselve:

      this.data.recommendations = ...

      // this.$rootScope.$broadcast('recommendations');

And my home component is currently only getting the empty recommendations at bootstrap time (before restos are downloaded and default filter applied)

export class HomeCmp {
  recommendations : Array<Resto>;

  constructor(restos: RestosSvc) {
    // ObservableWrapper.subscribe(  
    //   restos.data.recommendations, 
    //   recs => this.recommendations = recs
    // );
    this.recommendations = restos.data.recommendations;
  }
}
Community
  • 1
  • 1
Simon H
  • 20,332
  • 14
  • 71
  • 128
  • 2
    Is something like this [plnkr](http://plnkr.co/edit/ltAPrF?p=preview) what you want? – Eric Martinez Nov 07 '15 at 19:40
  • @EricMartinez in terms of the end result yes, but you have put the data handling in the parent component, and the service is not actually being used as far as i can see. In practise, my app is not so complex, so I could perhaps do this too, but it doesn't feel like 'best practise', and is not the way i think I should go. I want an event in filters to be passed to the service, cause a recalc of recommendations and then that to be passed to parent component to feed into map and list – Simon H Nov 07 '15 at 19:53
  • I'm also stunned how simple you made Angular2, TS and SystemJS look together - I'd ended up doing it myself and started using seeds, but then you are using code you don't understand. I might take your framework as my basis going forward too! – Simon H Nov 07 '15 at 19:54
  • It'll be much easier to understand the problem/questions if you could provide some Plunker with simplified example (I find it hard to understand it from current code examples). 10x! – Yaniv Efraim Nov 07 '15 at 20:13
  • OK, I might have a go with @EricMartinez 's template tomorrow as I'm too tired now – Simon H Nov 07 '15 at 20:47

2 Answers2

0

I'm not sure that I understand your question, but I guess that the answer is that Angular 2 has a build in observable - http will return an observable, not a promise! Take a look at this post for more details. The basic Idea is that you can now do something like this (reference taken from the post):

getRandomQuote() {
  this.http.get('http://localhost:3001/api/random-quote')
    .map(res => res.text())
    .subscribe(
      data => this.randomQuote = data,
      err => this.logError(err),
      () => console.log('Random Quote Complete')
    );
}

Reactive programming is built in Angular 2 so you don't have to so something special in order to use it...

Hope this answered your question. If not, please add some more explanations.

Edit:

In response to your last edit, I guess that it could be best to use unidirectional tree data flow (similar to React's approach). This means the data/state should be immutable and shouldn't be changed by a component that does not own it.

I think that the data should come from parent component, and on user interaction the filters component should trigger an event to parent component, which will notify the child components about the change (this is by Input data, using the [data] syntax).

I hope that I understood your question correctly!

Community
  • 1
  • 1
Yaniv Efraim
  • 6,633
  • 7
  • 53
  • 96
  • Yes, i can see how I can get the data at load time, but how do handle a user event that causes a different filtering of the full data, which requires a re-render of the data – Simon H Nov 07 '15 at 20:08
  • @SimonH: Please see my last edit. Hope it answers some of your questions. – Yaniv Efraim Nov 07 '15 at 20:20
0

OK, so I solved this by adapting @EricMartinez plnkr. This one is based on his but delegates a little more data handling to the service.

export class Hello {
  original: [];
  recommendations: [];

  constructor(public svc: RestosSvc) {
  }

  downloadData() {

    // Data coming from the server via service
    this.svc.http
      .subscribe((data) => {
        this.original = data;
        this.recommendations = this.original;
      });
  }

  handleFilterChange(count) {
    // Filter 
    console.log(count);
    this.recommendations = this.original.slice(0, count);
  }
}
Simon H
  • 20,332
  • 14
  • 71
  • 128