Ember Data belongsTo with Select dropdown view - ember.js

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.

Related

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-cli filtering model by name, with html select options

Im getting started with emberjs, and I have connected my ember app to an API, and my models working fine.
I can display the model in my template, but how can I filter them?
Example, this is my model:
import DS from 'ember-data';
export default DS.Model.extend({
placeofdamage: DS.attr('string'),
ees: DS.attr(),
type: DS.belongsTo('type'),
brand: DS.belongsTo('brand')
});
How can I display ex. only BMW? With this method:
<select class="form-control" id="selectBrand">
{{#each model.brands as |brand|}}
<option>{{brand.name}}</option>
{{/each}}
</select>
Thank you for any further reply.
There are two main approach. You can ask server to filter them for you or filter it yourself. The later will obviously not scale well for large amount of records.
To filter it yourself you can use addon like ember-composable-helpers it will make your life a bit easier or you can create computed property for it yourself.
To let it filter your api you will have to use query on store.
If you want allow users to toggle this filter you can wire it up yourself or use query-params. I would recommend you to read this one.
My answers is specific to your example. You can install ember-truth-helpers addon simply you can put eq check.
{{#each model.brands as |brand|}}
{{#if (eq brand.name "BMW") }}
<option>{{brand.name}}</option>
{{/if}}
{{/each}}
Or create computed property and filter the brand using filterBy,
onlyBMWBrand: Ember.computed('model.brands', function(){
return this.get('model.brands').filterBy('name','BMW');
})
Could write an is-equal helper by
ember generate helper is-equal
Content for helpers/is-equal.js
import Ember from "ember";
export function checkEquality([leftSide, rightSide]) {
return leftSide === rightSide;
}
export default Ember.Helper.helper(checkEquality);
In template
{{#each cars as |car|}}
{{#if (is-equal car.brand "BMW")}}
<img src={{bmwLogo}} />
{{else if (is-equal car.brand "Volvo")}}
<img src={{volvoLogo}} />
{{else}}
No match.
{{/if}}
{{/each}}
Shouldnt really use this approach when you have hundreds of car records. Let the backend do the job.

Ember.js: Is it possible to have a component for each field in a model?

So I thought I had this solved already as it worked with my prototype using just arrays, but now that I'm actually dealing with the model, I'm not sure if it will work.
If I create a property on my controller like so:
myAttributes: [{
name: 'attr1',
label: 'Attribute 1',
value: null
}, {
name: 'attr2',
label: 'Attribute 2',
value: 1
}]
then I can loop through myAttributes with {{#each}}, so while each object is essentially a field, that's different than the fields on a single model, which only have a value, so I can't do {{#each model as |rec|}} when there's only one record.
In a nutshell, I want to have a button group to set the value for each field in my model, like so:
I have around 60 of these fields so that's why I wanted to use {{#each}} and my component for each field, but of course each goes over records in the model, not fields in a record.
Is this impossible to do? Do I just have to bite the bullet and write out the markup for each field like I would do if I had only a few fields?
Update: Even if I could loop through the fields on a single record (maybe with {{#each model.#each as |field|}}?), for this case what I also need to do is break out the fields into sections in the UI, so for example loop through fields 1-10 in the first section, and 11-20 in the next section, and there doesn't seem to be a good way to do that.
In the end, I think I'm better off just using a component on each field, like so:
{{attribute-component value=model.attr1}}
{{attribute-component value=model.attr2}}
.
.
.
There's a neat helper called each-in that iterates over key/attributes of an object.
https://guides.emberjs.com/v2.6.0/templates/displaying-the-keys-in-an-object/
There is a way to do that in your case, but I think that actually a simpler way to get the same effect is this:
template.hbs
{{#each myArrayOfPeople as | person | }}
{{#each attribs as | anAttrib | }}
{{get person (mut anAttrib)}},
{{/each}}
<br>
{{/each}}
component.js
attribKeys:['name', 'phoneNumber', 'otherAttrib'];
This will output something like:
joe, 123, something,
sam, 456, anotherthing,
sarah, 944, foo
you can use that same (mut anAttrib) helper to bind an attribute to an input or whatever your need is.
{{attribute-component value=(mut anAttrib)}}
I have now forgone trying to iterate over each field because I actually need to break out the fields into accordion containers based on other criteria, although #averydev's answer about nested {{#each}} was a very cool tip and useful.
I also updated to Ember 1.13 in order to use the mut helper (although I had previously used an action on the component, that passes the new value to an action in the controller, that sets the value to the model property. Since that was convoluted, this new method using mut is much more understandable.
Just to help anyone else out in the future, here's my code (simplified for SO, the real code has nothing to do with rooms and furniture). I have a custom component that uses a radio button group component to actually set the value (this is from ember-radio-button):
template.hbs
{{room-style title='Living Room' field=(mut model.livingRoomStyle)}}
{{room-style title='Master Bedroom' field=(mut model.masterStyle)}}
templates/components/room-style.hbs
<div class="btn-group" data-toggle="buttons">
{{#radio-button value=1 groupValue=field classNames="btn btn-default" changed="changed"}}
Modern
{{/radio-button}}
{{#radio-button value=2 groupValue=field classNames="btn btn-default" changed="changed"}}
French
{{/radio-button}}
{{#radio-button value=3 groupValue=field classNames="btn btn-default" changed="changed"}}
Rustic
{{/radio-button}}
</div>
components/room-style.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
changed: function() {
//update() is a function you get with attrs.[prop] when using the mut helper
this.attrs.field.update(this.get('field'));
}
}
});

Accessing model data in router when using a view

I am looking for a way to access model data in a route when using a view to display model attributes.
Example
Template
<h2>New post</h2>
<form {{action save model on="submit"}}>
<label>Title</label>
{{input type="text" value=title placeholder="title" id="title"}}
<label>Text</label>
{{view "tinymce" value=text }}
<button>Post</button>
</form>
View Template
<textarea id="tinymce">
</textarea>
View
export default Ember.View.extend({
templateName: 'views/tinymce-textarea',
didInsertElement: function() {
tinymce.EditorManager.execCommand('mceRemoveEditor',true, 'tinymce');
tinymce.EditorManager.execCommand('mceAddEditor',true, 'tinymce');
}
});
Router
export default Ember.Route.extend({
....
actions : {
save : function(model) {
if (!model.get('title').trim() || !model.get('text').trim()) {
return;
}
model.save().then(this.onSuccessfulSave.bind(this), this.onFailedSave.bind(this));
}
}
});
Now, obviously this doesn't work, since model.text is never bound in the view, like it would be if I were to use the textarea template helper:
{{textarea value=text placeholder="text" id="text"}}
But this is just one of many (many) ways I have tried to get this to work, and I am at a complete loss as how one would access model attributes in the route when using a view. And it does seem like a pretty common usecase to me too.
I have failed to find information regarding this on SO or anywhere else, so if anyone is able to help me, thanks in advance! / AS.
So one of the main things that you're missing out is binding the view to the controller. This is actually really straight forward to do, but without it Ember doesn't know that it should propagate changes between the two. The first thing I would do is this:
{{view "tinymce" valueBinding="text" }}
This says that the views value will be binded to the controller's text value. Whenever view's value is updated, it will propogate to the controller and vice versa.
The next item to take care of is actually binding the value in the view. All you need to do is tell the input to bind it's value to the view's value. This can be done like this
{{textarea value="view.value" placeholder="text" id="text"}}
Try this out, and you can use this jsbin that I created as an example:
http://emberjs.jsbin.com/qanudu/26/
If you have any other questions just let me know, but this should solve your issues!

link-to action parameters are not working in ember js

Hi i am very new to ember js. i pass action parameters(id ) on link-to action in template but i did not get the values in my controller.
My Template code as follows:
index.html:
<script type="text/x-handlebars" data-template-name="search">
{{#each model.results}}
// here i pass id value along with action
{{#link-to 'profile' id action="profileinfo"}}
</script>
app.js:
App.SearchController = Ember.ObjectController.extend({
id: '',
actions:{
profileinfo: function(id){
// Here i access id value like this
console.log(id);
var id = this.get('id');})
when i click on the link action goes to Searchcontroller, but i get id value is empty.I follow some solutions in stack overflow but unfortunately i did not get anything. Please provide some solution
I don't get why you're using the {{#link-to}} helper for triggering an action on your controller. Maybe you could simply use the {{action}} helper ?
If you try doing it that way, would it work ?
<button type="button" {{action "profileinfo" id}}>Click me !</button>
From there, your console.log(id); should get your value.
EDIT
Would also work for a <a> tag
<a href="#" {{action "profileinfo" id}}>Click me !</a>
I've created popular addon for doing just that:
{{#link-to 'profile' id invokeAction='profileinfo'}}
Simply install it:
ember installember-link-action
Please leave a star if you enjoy it or leave feedback if you feel anything is missing. :) It works with Ember 1.13 and 2.X (tested on Travis CI).