Ember JS - Add select box option to URL string via Query Param - ember.js

I have a Select Box in my form and I would like to be able to use the selection as a Query Param so that I can refresh a model based on its selection. The Select Box is from this ember add-on.
{{#select-box/native value=sb name=module on-select=(action 'selected') class="form-control" as |sb| }}
{{sb.option value='Select a Module:'}} {{sb.option value='Option1'}} {{sb.option value="Option2" }} {{sb.option value="Option3" }} {{sb.option value="option4" }}
{{/select-box/native}}
The 'selected' action simply adds the option to a variable so that I can use it later in a switch statement:
selected(x) {
module = x
},
I'd like to have the selection (or a representation of the selection) in my URL string but I can'tt work out how. I have other inputs building into the URL string but none of them are select boxes.
I have a 'module' item in the QueryParams on my route but it doesn't do anything, I suspect I'll have to do something in the 'selected' action but I'm not sure what.

I haven't used the add-on you mentioned, but here is how you can do it using normal <select>, so just bridge the gap between normal <select> and the add-on you are using in terms of making sure that the status variable in the example below changes depending on what you select in your select box - Ember will do the rest.
Here's a configuration that works if you want to filter a list of users based on the status value you select from a dropdown:
// app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
status: DS.attr('string')
});
// app/templates/users.hbs
<select onchange={{action (mut status) value="target.value"}}>
<option value="" selected={{eq status ''}}>- All users -</option>
<option value="active" selected={{eq status 'active'}}>Active</option>
<option value="inactive" selected={{eq status 'inactive'}}>Inactive</option>
</select>
<ul>
{{#each model as |user|}}
<li>{{user.name}}, {{user.status}}</li>
{{/each}}
</ul>
// app/controllers/users.js
import Controller from '#ember/controller';
export default Controller.extend({
queryParams: ['status'],
status: ''
});
// app/routes/users.js
import Route from '#ember/routing/route';
export default Route.extend({
queryParams: {
status: {
refreshModel: true
}
},
model(params) {
var options = {};
if (params.status) {
options.status = params.status;
}
return this.get('store').query('user', options);
}
});
How does it work?
In the controller you define a property status, which you also indicate to be a query parameter (in the URL). Then in the route, you also define status to be a query parameter which refreshes the model. In the model() hook you extract the parameter and use it for Ember Data Store's query() to fetch the model every time you change the value of status. Your route URL will have ?status=... appended to it, and your server will receive a request similar to example.com/api/users?status=.... Of course, you can configure options in the model() hook differently to format the request URL for the server, but I kept it like this for the sake of simplicity.
The only thing that might be confusing is the template file. Apart from the {{eq status '...'}} syntax, which is a truth helper that simply determines whether the option is selected, the rest of the selecting simply aims to change the status variable (explained in depth here).

Related

Ember: globally-available search component (and action)?

I want to have a search field inside a component that can be placed anywhere in the app. It can appear on any template, or nested in components. The search form would accept user input (search term) and submit would trigger a search action which transitions to a results template.
Seems simple enough, but I can't figure out how to make an action globally available. And if I could, how do you pass the inputted term to the action in the first place? There's surprisingly little info on how to handle form submits with Ember CLI.
Thus far I've just been submitting a regular form with action='/results'. But that's obviously reloading the app.
I've been messing with creating an action in the index controller like this:
export default Ember.Controller.extend(defaultParams, {
term: '',
actions: {
keywordSearch() {
this.transitionToRoute('results', { queryParams: { q: this.get('term') }});
}
}
});
Then passing a closure action down to my search component, which is nested 2 deep from the index template.
index.hbs:
{{index-search keywordSearch=(action "keywordSearch")}}
index-search.hbs (component):
{{search-field keywordSearch=keywordSearch }}
search-field.hbs (nested component):
<form {{ action (action keywordSearch) on='submit' }}>
{{ input value=term }}
<button type="submit">Search</button>
</form>
And that will run the action, but the term is not supplied. How do you supply term to the closure action?
And...do I really need to pass the action down to every single place the search field is going to appear in the app, or is there an easier way to do it?
Instead of writing actions in all components and routes, you can create a service for search. Inject the service into the component and handle the route transition from service method. Check the sample code below,
Search-component.hbs
<form {{ action (action search) on='submit' }}>
{{ input value=keyword }}
<button type="submit">Search</button>
</form>
Search-component.js
export default Ember.Component.extend({
globalSearch: Ember.inject.service('search'),
actions: {
search() {
const { keyword } = this.getProperties('keyword');
this.get('globalSearch').showResults(keyword).then(() => {
alert('Success');
}, (err) => {
alert('Error while searching: ' + err.responseText);
});
}
}
});
Service - app/services/search.js
import Ember from 'ember';
export default Ember.Service.extend({
init() {
this._super(...arguments);
},
showResults(keyword) {
// write code for transition to search results route here
}
});

Ember unable to retain dropdown value

I am using Ember 2.5.0. In my application, I have created a page with a dropdown using the ember-select-list plugin.
I am able to render the dropdown, but unable to retain the value of the dropdown. Whenever I select the value, I am getting the following exception in the chrome console:
Assertion Failed: Cannot call get with 'id' on an undefined object
Please find the following code, for reference:
Template :
{{select-list content=roles
optionValuePath="role"
optionLabelPath="role"
value=role
action=(action (mut role))}}
Route.js
export default Ember.Route.extend({
model(){
return Ember.RSVP.hash({
books : this.store.findAll("book"),
roles : this.store.findAll("role")
});
},
setupController(controller,model){
controller.set('users',model.books);
controller.set('roles',model.roles);
}
});
Model
export default DS.Model.extend({
role:DS.attr()
});
In the router, when I pass Array(roles: this.store.findAll("role").toArray()) instead of model, I can retain the value, but it throws an error while passing model.
Anyone, could you please help me to resolve this issue?
The ember-select-list documentation and unit test indicate that an array is required for the content property on the {{select-list}} helper.
This is why Ember.RSVP.hash seems to fail, as hash expects and returns an object, which is a type that ember-select-list is not configured to use.
Instead of hash, you should use Ember.RSVP.all, as all expects and returns an array, which should work.
I've created an Ember Twiddle example to demonstrate.
If you'd rather not use ember-select-list, you might find it easier to merely use Ember's each helper to build out your own select list, like this:
<select onchange={{action "selectRole" value="target.value"}}>
{{#each roles as |role| }}
<option value="{{ role }}" label={{ role }}>
{{ role }}
</option>
{{/each}}
</select>
I would suggest you use ember-power-select addon.
To your question, you can try this,
setupController(controller,model){
controller.set('users',model.books);
controller.set('roles',model.roles.toArray());
}
let me know if this is not working

Ember model and route setup

Trying to learn Ember with a simple app, which is just a questionnaire. On the first page of the questionnaire (localhost:4200/animal) they choose their favourite animal from a select box (which is a component I made, that I would like to re-use on other questions). The select box is populated via a RESTful API.
//routes/animal.js
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return this.get('store').findAll('animal');
}
});
//models/animal.js
export default Model.extend({
name: attr()
});
//templates/animal.hbs
Choose your favourite animal:
{{select-box
items=model
selectedValue=???
}}
{{#link-to "xyz"}}Go to next question{{/link-to}}
//components/select-box/template.hbs
<select {{action 'actionOnChange' on 'change'}} class='{{class}}'>
{{#each items as |item|}}
<option value='{{item.id}}' selected={{eq item.id selectedValue}}>{{item.name}}</option>
{{/each}}
</select>
//components/select-box/component.js
import Ember from 'ember';
export default Ember.Component.extend({
selectedValue: '',
//when load the page, prepopulate the value
init(){
this._super(...arguments);
//retrieve it from somewhere?
//this.get('selectedValue') is blank if come back to the page
},
actions: {
actionOnChange(){
//save it somewhere? where?
this.set('selectedValue', this.$('select').val());
}
}
});
If I choose an animal, then go onto the next page of the questionnaire, and then come back to the /animal page again, their selection is lost. How do I retain the value that they chose?
All the examples I have looked at show the value being saved in the animal model, but in Ember Inspector on the 'Data' tab for 'animal', it shows all 5 different animals retrieved from the API call. Should I have a second model?
Any help is appreciated.

Set default (selected) option Ember 1.13.11

Sort version:
Why does this work: <option disabled={{option.isSelected}}>
But not this: <option selected={{option.isSelected}}>
Long version:
This is more about me learning how Ember works than about getting this working, since there are lots of working examples for this already.
I'm trying to set the default selected option in a select menu. It looks like there are different ways of doing this and that the recommended method is changing.
I'm working in Ember 1.13.11 but want to be Ember 2.0 compatible.
I haven't found a Ember 2.0 compatible method that didn't involve a template helper for the selected attribute. I can create a true/false value as a property on the controller. I know I'm doing it right because disabled works properly. Why does this fail only for select?
Template call:
{{version-details item=version status=version.status}}
Component controller:
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'select',
options: Ember.computed('status', function() {
var statusOptions = ['beta', 'current', 'future', 'retired-beta', 'retired', 'unknown'];
var selected = this.get('status');
var optionsData = [];
statusOptions.forEach( function(status){
var isSelected = (selected == status);
optionsData.push({ status: status, isSelected: isSelected });
})
return optionsData;
}),
setAction: '',
});
Component:
{{#each options as |option|}}
<option selected={{option.isSelected}}>{{option.status}}</option>
{{/each}}
As #blessenm points out. It does work.
I think couldn't tell it was working because the browser was remembering and selecting the value from the last time I visited the page.

Specifying which properties to use in a custom drop-down Ember component

I've created a reusable drop-down component in Ember:
/app/components/dropdown/component.js
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'select',
classNames: ['form-control'],
placeholder: null,
items: null,
selected: null,
change: function(event) {
var items = this.get('items');
var index = event.target.selectedIndex;
var selected = items ? items[index - 1] : null;
this.sendAction('selectedChanged', selected);
}
});
/app/components/dropdown/template.js
{{#if placeholder}}
<option value="">{{placeholder}}</option>
{{/if}}
{{#each items as | item |}}
<option value="{{item.id}}" selected={{is-equal item selected}}>{{item.name}}</option>
{{/each}}
The component currently uses the 'name' property as the label for the option. However, I want the ability to specify what property to use, in order to make the component more flexible (so that I can sometimes use, for example, 'displayName').
With the old Ember Select component, you could do the following:
{{view "select"
content=programmers
optionValuePath="content.id"
optionLabelPath="content.firstName"
value=currentProgrammer.id
}}
...and tell it which properties to use for both the value and label. I'd like to do something similar, but I'm not sure how. (I tried reading through the source but it was a bit beyond me). Thanks in advance.
<option value="{{ember-get item optionValuePath}}" selected={{is-equal item selected}}>{{ember-get item optionLabelPath}}</option>
You will need to implement the ember-get helper, in Ember 2.0 there is a default heper called get which will make the following redundant.
import Ember from 'ember';
export function emberGet(params/*, hash*/) {
return Ember.get(params[0], params[1]);
}
export default Ember.HTMLBars.makeBoundHelper(emberGet);
Also take a look at this jsbin which has an example of what you are trying to achieve.