Reputation: 4272
I am working on an Ember app and I am having issues. I have to filter out results at a certain point of app, say, in a controller or in a view, like this:
At view.hbs
I am eliminating passed tests at {{#if test.passed}}...
:
{{#each tests as |test|}}<br>
{{#if test.passed}}
{{test.name}}
{{/if}}
{{/each}}
I can do it at a controller that initially loads and can filter out in an input field:
import Controller from '@ember/controller';
export default Controller.extend({
actions: {
filterByTest: function (param) {
if (param !== '') {
return this.get('store').query('test', { name: param }).then((filteredTests) => {
return { query: param, tests: filteredTests.filterBy('passed', true) };
});
} else {
return this.get('store').findAll('test').then((tests) => {
return { query: param, tests: tests.filterBy('passed', true) };
});
}
}
}
});
Now I need to filter out at a route
level. model()
in a route
is can't be edited, if it can, then it has to be a path, which I don't want. How can I achieve that? How can I make model editable? At the moment I can get to the route model, but it can't be edited. Here's the way I talk to the model from view.hbs
file and it works.
{{#each model as |test|}}<br>
{{test.name}}
{{/each}}
Please see below the model that returns file to above view.hbs
template. Again, it is working:
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return this.get('store').findAll('test').then(tests => {
return tests('passed', true);
})
}
});
How can I pass argument to the model and edit it, so that my UI gets changed as well?
This is my model I need to modify. At the moment the return
works, but not modifiable.
Upvotes: 1
Views: 1267
Reputation: 11289
Hi @AmazingDayToday 👋 I'm going to go into a bit of a longer explanation on how we saw your question and how we would solve it based on the information available. It seems like @Jeff has already answered you but hopefully this will help give you more context of how to think about these issues.
Our first assumption was that you were hoping to pass a filter parameter as part of the action. When testing this we created a series of buttons that passed a name
attribute to the action as follows
<button {{action 'filterByTestName' 'face'}}>Face</button>
<button {{action 'filterByTestName' 'first test'}}>First Test</button>
<button {{action 'filterByTestName' ''}}>Clear</button>
The last button is intended to utilise the same action to clear the filter. I would generally tend to create a separate clear
action in these cases but it works for now 🙂
One of the main ways that I implement these sorts of filters is by having a filteredModel
computed property on the controller for the route in question. The point of this computed property is to do the filtering based on a "filterValue" on the controller that can be set by the filterByTestName
action. Let's start there:
actions: {
filterByTestName: function(name) {
this.set('filterValue', name);
}
}
the design of this action is to make it so that when filterValue
is set we then filter the model based on that value. Let's move on to the computed property that will be doing the filter for us:
filteredModel: Ember.computed('filterValue', '[email protected]', function() {
if(Ember.isEmpty(this.get('filterValue'))) {
return this.model;
}
return this.model.filter((test) => test.get('name') === this.get('filterValue'));
}),
The first thing that you will notice on that computed property is that we have two dependant keys i.e. this computed property will update when filterValue
or any of the name
values on any of the models change.
The second thing of note in this implementation is the "base case" of when filterValue
is empty we want to pass the current model through without modification.
And the last part of this implementation is that we want to actually preform the filter on the current model based on the current value of the filterValue
.
You can see this entire example working on this Ember Twiddle and you can see us solving this problem live on this YouTube video
I hope this helps 🎉
Upvotes: 3
Reputation: 10064
If I am understanding the same as @Jeff has you want to query your back end based on the options a user has selected via buttons or some means. It is best to think in terms of state in this regard. The controller will have a state that represents the filter view you want to represent and your model()
hook will use that state to know how to query for the specific set of models you want to show.
Here is a working twiddle that demonstrates how this is accomplished (the back end is using ember-cli-mirage): https://ember-twiddle.com/150708278ceafc6f9f99961122a14f07
The highlights are that my route defines a queryParams
object (same as @Jeff's answer) which instructs the route to refresh its model every time the category
property on the controller changes.
queryParams: {
category: { refreshModel: true }
},
In the model()
hook I use the params.category
value to construct the correct query payload.
model(params) {
if (params.category === 'all') {
return this.store.findAll('test');
}
let passed = params.category === 'passed';
return this.store.query('test', { 'filter[passed]': passed });
}
finally I make sure that my controller knows that category
is driven from the URL:
queryParams: ['category'],
// A default is required to train ember on how to serialize
// the value in the URL
category: 'all',
You can now change the category
value and the whole route will automatically refresh for you or you can use the link-to
helpers
<button {{action (mut category) "passed"}}>Show passed</button>
{{#link-to "index" (query-params category="passed")}}Show passed{{/link-to}}
Upvotes: 4
Reputation: 6953
This is a bit of a guess, as I'm not 100% sure what you want to achieve.
I don't know where that param
comes from, but I assume a query parameter like that yourroute?name=TestName
. With this you can access that param like so:
export default Route.extend({
// if you leave that out, it'll work for the first entry, but the model will not update if you transitionToRoute with only the queryParams changed)
queryParams: {
category: {
refreshModel: true
}
},
model(params) {
// params has format of { name: "TestName" }
return this.get('store').query('test', params);
// you can of course put more complex logic in here, as you had it in controller
// f.e.:
// return this.get('store').query('test', {name: params.name, passed: true});
// or check first if you have a param at all...
}
}
And here's the docs.
Note:
You cannot set a param on the controller to change the model. I've tried it...deperately.
To change it you'd have to do a {{link-to "routeName" (query-params name="newName")}}
in template or a transitionToRoute({ queryParams: { name: 'anotherName' } });
in your controller (docs for transitionToRoute()).
Sidenote: having filterByTest
as an action is strange anyway. This should be a computed property.
Upvotes: 2