If I have a custom ember select (or any ember select) like:
App.SelectGender = Ember.Select.extend
content: ['Gender', 'Male', 'Female']
And I want to set attributes on the first option like:
<option value="false" disabled="disabled" selected="selected">Gender</option>
How can I go about doing that? Thank you in advance for the help.
Since understandably you wanted to add a prompt label for your Select component you can pass it as a property to Ember.Select like below.
{{view Ember.Select
content=sexArray
value=SelectedSex
prompt="Gender"
}}
Docs link for same http://emberjs.com/api/classes/Ember.Select.html#toc_supplying-a-prompt
Related
I am trying to do something pretty basic with Ember and Ember Data.
1) Posts belongsTo Users; Users hasMany Posts
2) In the create new Post form, I want to have a select/dropdown of all Users
3) When I edit a post (using the same form), I want to bind it correctly back to the dropbox
Question: What is the best practice to do the dropdown that binds to the list of users?
How can I bind the edit form to populate the dropdown again?
User Model:
App.User = DS.Model.extend({
posts: DS.hasMany('post', {async: true}),
});
Post Model:
App.Post = DS.Model.extend(Ember.Validations.Mixin, {
user: DS.belongsTo('user', {async: true}),
});
Create New Post Form:
{{view Em.Select
content=users <<<<< How do I bind this.store.find("user") here?
optionValuePath='content.id'
optionLabelPath='content.name'
}}
I don't understand the best practice to bind the select content with users.
Attempt 1:
*I am using Ember-Form
{{em-select
property="user_id"
label="user"
prompt="Select user:"
content=controllers.users.content
optionValuePath="content.id"
optionLabelPath="content.first_name"
}}
And on the save action:
newItem.set('user', this.store.getById('user', this.get('user_id')));
I tried to use user_id as my property for the form, and translate back to a user object to assign to the post when I save. However, this method is kind of stupid because I am actively translating user_id to user object every time I save. I feel like there should be a way that that is done automatically if I did the correct binding, instead of jumping through hoops to bind the property as user_id. This also makes it hard for me to bind back when I use the same form for editing the post. Since all the relationship has been setup, I have a feeling that I am doing something wrong here. I think there must be a better way.
Thanks!
As of September 2014 there is an issue with Ember.Select plus { async: true }. They don't play well together.
myModel.get('myAsyncRel') will return a promise, Ember.Select is not promise aware, so it cannot handle this properly.
One workaround is to use a non-async relationship.
https://github.com/emberjs/data/issues/1405
https://github.com/emberjs/data/issues/2111
Another workaround is to use something like this:
http://emberjs.jsbin.com/rwjblue/43/edit?html,css,js,output
The ember team has opened an issue (#5259) for this problem, they are planning to rewrite the whole Ember.Select part.
In the meantime you can use this Add-on from thefrontside:
{{#x-select value=bob action="selectPerson"}}
{{#x-option value=fred}}Fred Flintstone{{/x-option}}
{{#x-option value=bob}}Bob Newhart{{/x-option}}
{{/x-select}}
Install it with ember install emberx-select
EDIT As Stefan Penner wrote, you can now use the ember select as follows (jsbin):
<select onchange={{action "changed" value="target.value"}}>
{{#each model.choices key="#identity" as |choice|}}
<option value={{choice}} selected={{is-equal currentValue choice}}>{{choice}}</option>
{{/each}}
</select>
When you say dropdown I presume you mean a select box? Ember's API has a select view that handles all of the data binding. Here are the docs for Ember.Select.
You can use it in your post form like so:
{{view Em.Select
content=users
selection=user
optionValuePath='content.id'
optionLabelPath='content.name'
}}
If you're using Dockyard's awesome ember-easyForm library you can utilize an Em.Select like this:
{{input user type='as'
collection='users'
selection='user'
optionValuePath='content.id'
optionLabelPath='content.name'
prompt='Choose type...'
}}
Advanced UI using Chosen can be easily integrated with Ember like this.
New deprecation information was added in Ember 1.13 for Ember Select, see http://emberjs.com/deprecations/v1.x/#toc_ember-select.
The new recommended approach in Ember 1.13/2.0 is to implement a component yourself:
e.g.
/components/my-select.hbs
<select>
{{#each users "id" as |user|}}
{{log address}}
<option value="{{user.id}}">
user.name
</option>
{{/each}}
</select>
The ember link shows how to implement selection with selected={{is-equal item selectedValue}} and creation of a is-equal helper.
Using the select method with handlebars each and if helpers I can successfully display an array where only those with active = true.
<select>
{{#each content.users}}
{{#if active}}
<option value="">{{firstName}}</option>
{{/if}}
{{/each}}
</select>
vs
{{view Ember.Select class='btn btn-default dropdown-toggle' style='max-width: 200px'
content=content.users
optionValuePath='content.id'
optionLabelPath='content.firstName'
selectionBinding='someSelectionBinding'}}
The downside to the select way is I can't bind an action to the option value, and I lose out on some of the useful Ember Selection binding and label functionality/observers. Where the downside to the Ember.Select way is I can't set my content to only those users who have the active flag.
I'm assuming you're using a recent version of Ember. In your controller, assuming an Ember.ArrayController with content.users defined as you seem to have it, you'll want to create a new attribute, like activeUsers:
App.ThisController = Em.ArrayController.extend({
// Other stuff
activeUsers = Em.computed.filterBy('users', 'active', true)
})
This should give you an array of users where the active attribute is true, so you can set the content of your Ember.Select view to content.activeUsers.
I have this jsbin : http://jsbin.com/IYUSiLUz/1/edit
in here you can select a value in the select, and it shows the selected value in the form. You can also set the default value, and that selection will be highlighted by default in the select.
How can I achieve the same result for a multiple select ? If you change multiple=true, what do you need to change to highlight two items (for example) in the select, and how do you get the selected values ?
thanks
Use the selection property of Ember.Select to get a list of all the selected objects:
{{view Ember.Select
contentBinding="App.data"
optionValuePath="content.id"
optionLabelPath="content.personName"
valueBinding="App.demo"
selectionBinding="selected"
multiple=true }}
{{#each selected}}
{{input value=id}}
{{/each}}
Demo: http://jsbin.com/IYUSiLUz/7/edit
I'm trying to link a label to an input field using the {{bindAttr}} and the input field's [viewName].elementId. It works on a single entry view, but not when there are several records being displayed: it just links the label to the last input field in the collection. (This used to work in a previous iteration using an older ember library but now it doesnt.) I've created a fiddle but the gist of it is:
{{#each controller}}
<fieldset>
<label {{bindAttr for="view.tbFullName.elementId"}}>Full Name</label>
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
</fieldset>
{{/each}}
I thought maybe I could create a collectionView and create a calculated property for viewName which would generate a unique ID for each item in the collection, sort of mentioned in answer to another problem here. But that is getting WAY too complicated - just so that I can have the input field highlight itself if the user clicks on the corresponding label.
Any help appreciated.
Create a wrapper Ember.View around the label and input field. Let's call it App.FieldView:
App.FieldView = Ember.View.extend({
tagName: 'fieldset'
});
Then in your template:
{{#each controller}}
{{#view App.FieldView}}
<label {{bindAttr for="view.tbFullName.elementId"}}>Full Name</label>
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
{{/view}}
{{/each}}
Fiddle: http://jsfiddle.net/NQKvy/26/
Panagiotis Panagi, has answered the question correctly. I'll just add why this is happening, ie:- linking to the incorrect view.
The view property inside a template refers to the Ember View wrapping the html markup. This property however has different value depending on the context it is in.
This value is dependent on the view block it placed in. By default the template itself corresponds to a view in this case, ListOfPeopleTemplateView.
So when you are binding to view.tbFullName.elementId, you are actually binding to an {instance of ListOfPeopleTemplateView}.tbFullName.elementId. And when the loop finishes the only tbFullName visible is the last one.
Panagiotis Panagi's solution is to wrap the label inside another view, so the value of view changes to within that block, and hence points to the correct tbFullName
Finally an even easier way to achieve the same result is to wrap the textfield inside the label. Then you do not need the label for binding at all.
<label>Full Name
{{view App.DetailTextField viewName="tbFullName" placeholder="Full Name" valueBinding="fullName" readonly="readonly"}}
</label>
See this jsfiddle
Forms are somewhat tricky I must admit if you want to do things right. But there are is an ember add-on that comes to the rescue, for example easyForm.
Have a look it might helps you solving exact the problems you are facing, like the ones on having unique labels for your form fields etc.
Hope it helps.
A label tag is of the form:
<label for="id_of_text_field">
<input type="text" name="example" id="id_of_text_field" />
Where the for tag of the label and the id tag of the text field need to match. I had two ideas to make this work in my Ember.js template:
Idea #1: I tried to make a special binding named field_id to use in both the label and the TextField. I executed this as follows:
<label {{bindAttr for="content.field_id"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.data" id="content.field_id"}}
Unfortunately only the label's id renders correctly. The TextField's id does not render correctly and turns out to be "metemorph... something-or-other".
Idea #2: To somehow conjure the TextField's id and use that for the label tag, but I'm afraid at some point the TextField's id will not be ready when the label tag is rendered. Even if this were not an issue, I do not know how to find the TextField's id from JS.
This is in a template so I will have more than one of these label/TextField pairs.
How can I get the label's for tag to match up with the TextField's id tag with Ember.js?
Thank you!
UPDATE
Using Peter Wagenet's advice and slight modification, I did the following:
<label {{bindAttr for="textField.elementId"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.value" viewName="textField"}}
Using this technique users can now click labels to select TextFields, CheckBoxes, and Selects.
First off, the Metamorph tag is for the label's value. It's not related to the field's id. However, this code still won't work because standard properties don't do anything special with property paths. In this case, the value of id, is literally content.field_id. This is certainly not what you wanted. In normal circumstances, you could use elementIdBinding (id is just an alias for elementId), however the element ids for Ember Views cannot be changed after creation so that approach won't work here.
One possible solution makes use of the viewName property. The viewName property provides a named reference to the view on the parentView. You could then, do the following:
<label {{bindAttr for="view.textField.field_id"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.data" viewName="textField"}}
This won't always solve your problem, but I just wanted to add that simply nesting the input inside the label is often a convenient solution, since it allows you to drop the for attribute altogether (reference).
Here was my solution to this problem from a while back:
App.Widget.TextField = Em.ContainerView.extend({
tagName: '',
type: 'text',
label: null,
value: null,
valueSize: '30px',
childViews: ['labelView', 'inputView'],
labelView: Em.View.extend({
tagName: 'label',
attributeBindings: ['for'],
forBinding: 'parentView.inputView.elementId',
init: function () {
this.set('defaultTemplate', Em.Handlebars.compile(this.getPath('parentView.label')));
this._super();
}
}),
inputView: Em.TextField.extend({
typeBinding: 'parentView.type',
sizeBinding: 'parentView.valueSize',
valueBinding: 'parentView.value'
})
});
Then from within a Handlebars template:
{{view App.Widget.TextField label="Some Label"}}