credwards27 credwards27 - 26 days ago 7
Javascript Question

Ember.js 2 How to filter an existing model?

I'm currently learning Ember 2, and I'm trying to figure out the best approach to stacking model filters in Ember.js (2.4.2 with CLI workflow) and Ember Data.

Specifically, I am wondering if there is a good way to apply filters to an existing route's filtered model, without overriding any filtering that the route (or other component) itself may be doing.

My problem is this:

I have log entries (model "entry") which are sorted by date and associated with a user by user ID (model "user").



Model as defined in
/app/routes/index.js
:

export default Ember.Route.extend({
// ...

model: function() {
return Ember.RSVP.hash({
users: this.store.findAll("user")
entries: this.store.findAll("entry")
});
},

// ...
});


Model is overwritten in
/app/routes/user.js
:

export default Ember.Route.extend({
// ...

model: function() {
return Ember.RSVP.hash({
users: this.store.findAll("user")
entries: this.store.filter("entry", function(entry) {
// ...
return entry.get("userId") === userId;
})
});
},

// ...
});


When viewed,
localhost:4200/
lists all entries, and
localhost:4200/user/alice
lists all entries owned by the user named "alice". Likewise,
localhost:4200/user/bob
lists all entries by user "bob".

I also have a filtering component that allows entries to be filtered by date.



Specifically, entries on or after a "from" date, and/or entries on or before a "to" date. These date filters need to be able to applied to both the index and user routes.

The problem comes in with the user route. I can do the following in
/app/components/log-filter.js
to filter by date (assume
this.entries
is the "entry" model passed as a template attribute from the current route):

// ...

var filtered = this.entries.store.filter("entry", function(entry) {
var logDate = entry.get("logDate");
return logDate >= filterDateFrom && logDate <= filterDateTo;
});

this.set("entries", filtered);


This code properly filters by "date from" and "date to", but it overrides the user name filtering when run in the user route.

The quick and dirty route would be to replicate the user name filtering in the date filter callback, but this is not DRY and it will quickly become unwieldy as more routes are added (category, location, etc.), since date filters need to be universal.

My question



This is a front-end-only app using local storage as its data store, so I can't use a backend to automatically filter out entries outside of ember.


  1. Is there any way to filter against an existing model, rather than always starting with the global
    *.store.filter("modelName", callback)
    collection?

  2. Is this even a good approach or is there a better, more "Ember" way of handling multiple arbitrary filters?



Any help at all is appreciated.

Answer

The filter should definitely be done by your adapter. If a user enters user route directly, you won't have any entry records in store. You hack only works if a user enters via index route.

Also I think userId is not defined. I guess it's a dynamic segment of your user route?

// /app/routes/user.js
export default Ember.Route.extend({
  model: function(params) {
    return Ember.RSVP.hash({
      user: this.store.findRecord("user", params.userId)
      entries: this.store.query("entry", {
        filter: params.userId
      })
    });
  }
});

You could easily filter a DS.RecordArray by Ember.computed filterBy() or filter() methods or sort it with sort().

// /app/component/user-view.js
Ember.Component.extend({
  filterDateFrom: new Date(),
  filterDateTo: new Date(new Date().setDate(new Date().getDate() + 7)),
  filteredEntries: Ember.computed.filter('entries', function(entry) {
    var logDate = entry.get("logDate");
    return logDate >= this.get('filterDateFrom') && logDate <= this.get('filterDateTo');
  })
});

// /app/templates/user.hbs
{{user-view entries=model.entries}}