List of checkboxes values as array like Ember.Select (multiple) - ember.js

Is there a way to get the data of a list of checkboxes in Ember as an array of id's?
In my app I want to make a new Site. I made a route to /sites/new to show a form with fields to add a Site.
This is my Model:
App.Site = DS.Model.extend({
name : DS.attr('string'),
languages: DS.hasMany('App.Language')
});
App.Language = DS.Model.extend({
name : DS.attr('string')
});
This is my Controller:
app.SitesNewController = Em.ObjectController.extend({
needs : [ 'languages' ],
name: null,
languages: null,
createSite : function() {
// Get the site name
var name = this.get('name');
var languages = this.get('languages');
console.log(name,description,languages);
// Create the new Site model
app.Site.createRecord({
name : name,
languages : languages
});
// Save the new model
this.get('store').commit();
}
});
This is (part of) my SitesNewView:
{{#each controllers.languages}}
<label class="checkbox">
{{view Ember.Checkbox checkedBinding="languages"}}
{{ name }}
</label>
{{/each}}
<button {{ action "createSite" }}>Save</button>
In my console languages is null. How do I get an array of language-id's out of this.get('languages')?
UPDATE
I mean something like an Ember.Select with attribute multiple=true, like this: {{view Ember.Select selectionBinding="languages" contentBinding="controllers.languages" optionValuePath="content.id" optionLabelPath="content.name" multiple="true"}}

Take a look to the jsfiddle that I quickly created.
This may not be the best solution but at least it should help you.

Related

Ember Data 2 - Submit Two Associated Models in One Form

Total Ember newb here. My back-end is written with Rails. That piece is fine, so assume all is well on the server side. Here are the specifics:
I have two models
Project (Has Many Project Details)
Project Details (Belongs To Project)
I have confirmed that:
I am able to create a new Project from my projects/new.hbs template
My Index route retrieves all Projects and the Project Details associated with them.
My Show route retrieves an individual Project and the Project Details associated with them.
What I would like to do is, submit a project model along with a project detail model from my projects/new.hbs template.
I understand that the form in new.hbs should be in a component but I am keeping it simple for now. Once, I am able to create my associated models I will work on doing the same as a form component.
Project Model
export default DS.Model.extend({
project_details: DS.hasMany('project_detail', { async: true }),
project_name: DS.attr('string'),
status: DS.attr('string')
});
Project Detail Model
export default DS.Model.extend({
project: DS.belongsTo('project', { async: true }),
project_id: DS.attr('number'),
feature_name: DS.attr('string'),
hours_billed: DS.attr('number'),
available_hours: DS.attr('number'),
amout: DS.attr('number')
});
Projects/New.js Route
export default Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
projects: this.store.createRecord('project'),
project_details: this.store.createRecord('project_detail')
});
},
actions: {
create: function() {
var self = this;
this.controller.get('model').save().then(
function() {
self.transitionTo('projects.index');
});
}
}
});
Projects/New.hbs
<form {{ action "create" on="submit" }}>
<label for="project_name">Project Name</label>
{{ input value=project_name }}
<label for="status">Status</label>
{{ input value=status }}
<label for="feature_name">Feature Name</label>
{{ input value=feature_name }}
<label for="available_hours">Available Hours</label>
{{ input value=available_hours }}
<label for="hours_billed">Hours Billed</label>
{{ input value=hours_billed }}
<label for="amount">Amount</label>
{{ input value=amount }}
<button type="submit">Save</button>
</form>
Router.js
Router.map(function() {
this.route('home');
this.route('dashboard');
this.route('login');
this.resource('projects', function() {
this.route('new');
this.route('show', {path: '/:id'});
this.route('edit', {path: '/:id/edit'});
});
});
When I submit the form, the action appears to be called, but all data that is submitted is NULL, so evidently, my inputs are not being recognized. I have searched for a couple days and have not seen a solution for submitting multiple models. My code is a result of expanding on a standard POST along with how I handled retrieving my associated models via the RSVP Hash. Any advice would be great. Thanks!
You've got some things that are not wired up correctly.
When you return the following from your route:
model: function() {
return Ember.RSVP.hash({
projects: this.store.createRecord('project'),
project_details: this.store.createRecord('project_detail')
});
},
In your template, you can use {{model.something}} to access that information, in your case you have access to {{model.projects}} and {{model.project_details}}.
You probably want to do the following in your route to setup easier access to your models in the templates.
setupController(controller, model) {
//this._super(controller, model) I'll explain why this is commented
controller.set('project', model.projects);
controller.set('project_details', model.project_details);
}
If you do this, then in your template you can do things like this:
<label>Project Name</label>
{{input value=project.project_name}}
{{project.project_name}}
Which brings to the next thing. When you have in your template the following:
{{ input value=project_name }}
What you're doing is binding the input value to the project_name property of your controller. If you use the Ember Inspector (hint, if you're not using it, you should. It makes life so much easier) you can probably check that the controller has that property project_name with the value you typed.
Before Ember 2.0 there was this thing called the Object Controller (which you don't use anymore) that allowed proxying the model to the controller. Which meant that you could on your route do this:
model() {
return this.store.createRecord('project');
}
And in your template you effectively to this:
<label>Project Name</label>
{{project_name}}
Today you have to do this (Unless you install ember-legacy-controllers which I don't recommend):
<label>Project Name</label>
{{model.project_name}}
As you can see, you need to add the model before the project_name. This is because the model returned from the route is set as a property of the controller by default. That's something we can change, of course. You could also do the following in your route:
setupController(controller, model) {
controller.set('model', model.projects);
controller.set('myProjectDetails', model.project_details);
}
And in the template you could now do the following:
<form {{ action "create" on="submit" }}>
<label for="project_name">Project Name</label>
{{ input value=model.project_name }}
<label for="status">Status</label>
{{ input value=model.status }}
<label for="feature_name">Feature Name</label>
{{ input value=myProjectDetails.feature_name }}
<label for="available_hours">Available Hours</label>
{{ input value=myProjectDetails.available_hours }}
<label for="hours_billed">Hours Billed</label>
{{ input value=myProjectDetails.hours_billed }}
<label for="amount">Amount</label>
{{ input value=amount }}
<button type="submit">Save</button>
</form>
Which means that your save function now works because the "project" is now your model. If you used my example of setupController where I did this:
controller.set('project', model.projects);
Then you would have to change your create action to this:
actions: {
create: function() {
var self = this;
this.controller.get('project').save().then(
function() {
self.transitionTo('projects.index');
});
}
}
Also, do notice that you have your models declared with
//Project
project_details: DS.hasMany('project_detail', { async: true }),
//Project Detail
project: DS.belongsTo('project', { async: true }),
But you're not setting the relations in the code you posted. If you want the models to be related, in your create action you would need something like this (or in any other place really). I'm assuming in the following snippet that my setupController example is in the route so that the project is in the controller as project and the project_details as project_details.
create: function() {
var self = this;
var project = this.controller.get('project');
var projectDetails = this.controller.get('project_details');
project.get('project_details').pushObject(projectDetails);
project_details.set('project', project);
project.save().then(function() {
self.transitionTo('projects.index');
});
}
Assuming you're using Ember Data 2.0, there's also another problem with this. When you save the project it will only make a post request to save the project itself (it won't save the newly created project_details with it). At the moment JSONAPI(the standard Ember Data 2.0 by default uses) does not have a solution for updating multiple resources in one go (see this github issue). This means that you would have to save the project_details first and only then then project (which probably doesn't make sense looking at your models as it seems the project_details only exists under a project.)
It's the current state of affairs, as far as I know. I'm also looking for a good solution for this problem (I've hacked together solutions like only allowing to create a project_detail after the project is created, or having an extra attribute in the project with is a string of the serialized project_detail json and then have the backend to the things it needs)

Update value when value is selected in Ember.Select

I have a Ember.Select in my template and an image:
Difficulty: {{view Ember.Select id="id_diff" contentBinding="difficulties" optionValuePath="content.id" optionLabelPath="content.title"}}
<img src="(path)" />
The Select is filled with values coming from server; in the controller:
difficulties: function() {
return this.get('store').find('difficulty');
}.property()
and the model:
Gmcontrolpanel.Difficulty = DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
path: DS.attr('string')
});
And that's ok; but i would like that when a difficulty is selected from the Ember.Select, the corrispondent path property would be inserted in the img tag
Anyone knows how to get this result?
To accomplish this, I would set up a couple of things.
First, update your Ember.Select to include a valueBinding against the model with a new property:
{{view Ember.Select id="id_diff" contentBinding="difficulties" optionValuePath="content.id" optionLabelPath="content.title" valueBinding="selectedDificulty"}}
This will bind your select view to a model object. Which means, on the controller, we can now include a new function with a .observes on that field:
updateImage : function(){
this.set('fullPath', this.get('path') + this.get('selectedDificulty'));
}.observes('selectedDificulty');
And finally, change your image path to the newly created one:
<img src="(fullPath)"/>

choosing a value from select

using ember.js 1.0 and ember-data 1.0 beta2
I have a model (state) with the following properties
state: DS.attr('string'),
stateName: DS.attr('string'),
and a model (customer) with the following properties
name: DS.attr('string'),
stateID: DS.attr('string'),
state: DS.belongsTo("state")
I want to be able to edit the customer and choose the state from a drop-down (that has the stateID + name showing : eg "FL - Florida" and when selected, to store the state.stateID into the customer.stateID property
this is the first time I've tried something like this , and am slightly confused about the process.
In my customer route I've set up the following:
setupController: function(controller, model) {
this._super(controller, model);
this.controllerFor('state').set('content', this.store.find('state'));
}
and my select is this:
{{view Ember.Select
contentBinding="controllers.state.content"
optionValuePath="content.stateName"
optionLabelPath="content.stateName"
valueBinding="content.stateID"
selectionBinding="content.stateID"
prompt="Select a state"
}}
now I'm confused about where to go from here.
thanks
update
changed the view to say
{{view Ember.Select
contentBinding="controllers.state.content"
optionValuePath="content.stateID"
optionLabelPath="content.stateName"
valueBinding="customer.stateID"
}}
and I still don't get the stateid property to change . I've also tried
selectionBinding="customer"
to no avail.
update #2
I suspect that my problem may be linked to the property name. I changed the customer.stateID property to be customer.foobar and changed the select to read
{{view Ember.Select
contentBinding="controllers.state.content"
optionValuePath="content.stateName"
optionLabelPath="content.stateName"
valueBinding="foobar"
class="form-control"
}}
and now customer.foobar is updated with the value from the select.
Is there a problem with a property called stateID on customer ? I have a state model and state controller etc so is there a conflict ?
after all that - the problem was in the models themselves. The state model does not have a stateID field, it's state.state ...
My heartfelt apologies to all that wasted their time on this. Such a stupid error.
Okay, maybe not the best solution, but it works well:
App.ItemModalController = Ember.ObjectController.extend({
content: [],
availableCategories: function() {
return this.store.find('category');
}.property(),
//...
});
And the select:
{{view Ember.Select
contentBinding="availableCategories"
valueBinding="categorySelected"
optionLabelPath="content.title"
optionValuePath="content.id"
prompt="Please select a category"
class="form-control"
}}

Rendering multiple Models each containing multiple objects in Ember Js

I'm quite new to EmberJS. I am trying to render a simple hasMany modelisation.
My app is supposed to handle multiple tasks for multiple persons.
First, I have models:
App.Task = Em.Object.extend({
name: null
});
App.Person = Em.Object.extend({
firstname: null,
lastname: null,
avatar: null,
tasks:null,
});
A person can have multiple tasks. So my personsController works like this:
App.personsController = Em.ArrayController.create({
content: [],
tasks:[],
addPerson: function(){
var aPerson = App.Person.create({
firstname: this.firstname,
lastname : this.lastname,
avatar : this.avatar,
});
this.pushObject( aPerson );
}
My tasksController, wich will handle tasks jobs:
App.tasksController = Em.ArrayController.create({
content:[],
contentBinding: "App.personsController.tasks",
name:'',
removeTask: function(e){
this.removeObject( e.context );
},
addTask: function(e){
this.pushObject( App.Task.create({"name":this.name}) );
}
});
contentBiding seems to be one of the keys here. I want it to auto-bind to the tasks of the person I'm working on.
On the views side, just working on my Handlebars templates:
<script type="text/x-handlebars">
{{view Em.TextField valueBinding="App.personsController.firstname" placeholder="firstname" }}
{{view Em.TextField valueBinding="App.personsController.lastname" placeholder="lastname" }}
<button {{action "addPerson" target="App.personsController"}} class="btn btn-primary" >Ajouter un developpeur</button>
{{#each App.personsController}}
<div>
<h3>{{firstname}}{{lastname}}</h3>
{{view Em.TextField valueBinding="App.tasksController.name"}}
<button {{action "addTask" target="App.tasksController"}} >Add a task</button>
{{#each App.tasksController}}
{{view Em.TextField valueBinding="name" }}
<button {{action "removeTask" target="App.tasksController"}} >x</button>
{{/each}}
</div>
{{/each}}
</script>
So, when I'm adding a new person, everything works fine. But if I add a task, the task is added on each of the person's tasks in the view.
I'm afraid I'm making a conceptual mistake, and I can't find a good documentation about this kind of visualisation. I've seen examples of hasMany relations with ember.data, but I'd like first to understand properly what is going on here.
Your addTask function adds a new task to the array maintained within App.tasksController, but there is nothing binding these tasks to any instance of App.Person.tasks.
I suggest creating new View class with a binding to your tasksController and to a single App.Person instance. The personBinding of this view would be set inside of your outer {{#each}} loop. Have this view be responsible for rendering App.Person.tasks for a single person.
Change addTask in your controller to accept a reference to an App.Person as an argument, then add the task explicitly to that person's tasks array in the body of the function.

select dropdown with ember

I'm trying to produce a select input and pass the selected object to the change event on the view. The ember contact example uses a <ul> but with a select the view needs to be outside the each otherwise the change even isn't fired.
Here is the view js:
App.SelectView = Ember.View.extend({
change: function(e) {
//event for select
var content = this.get('content');
console.log(content);
App.selectedWidgetController.set('content', [content]);
},
click: function(e) {
//event for ul
var content = this.get('content');
console.log(content);
App.selectedWidgetController.set('content', [content]);
}
});
The ul from the example works:
<ul>
{{#each App.widgetController.content}}
{{#view App.SelectView contentBinding="this"}}
<li>{{content.name}}</li>
{{/view}}
{{/each}}
</ul>
But if I replace html directly, the change event isn't fired (which makes sense)
<select>
{{#each App.widgetController.content}}
{{#view App.SelectView contentBinding="this"}}
<option>{{content.name}}</option>
{{/view}}
{{/each}}
</select>
So I guess the select has to be wrapped in the view.. in which case how do I pass the relevant object?... This code results in the entire array being passed:
{{#view App.select_view contentBinding="App.widgetController.content"}}
<select>
{{#each App.widgetController.content}}
<option>{{name}}</option>
{{/each}}
</select>
{{/view}}
Ember now has a built-in Select view.
Here's a usage example:
var App = Ember.Application.create();
App.Person = Ember.Object.extend({
id: null,
firstName: null,
lastName: null,
fullName: function() {
return this.get('firstName') + " " + this.get('lastName');
}.property('firstName', 'lastName').cacheable()
});
App.selectedPersonController = Ember.Object.create({
person: null
});
App.peopleController = Ember.ArrayController.create({
content: [
App.Person.create({id: 1, firstName: 'Yehuda', lastName: 'Katz'}),
App.Person.create({id: 2, firstName: 'Tom', lastName: 'Dale'}),
App.Person.create({id: 3, firstName: 'Peter', lastName: 'Wagenet'}),
App.Person.create({id: 4, firstName: 'Erik', lastName: 'Bryn'})
]
});
Your template would look like:
{{view Ember.Select
contentBinding="App.peopleController"
selectionBinding="App.selectedPersonController.person"
optionLabelPath="content.fullName"
optionValuePath="content.id"}}
Again, here's a jsFiddle example: http://jsfiddle.net/ebryn/zgLCr/
check out the answers to a similar question: How to bind value form input select to attribute in controller
In the examples a CollectionView is used with an tagName=select. You may find this helpful in getting it work.
EDIT: Since I was looking to implement a select myself, here is the solution I came up with:
views/form.js.hjs:
{{#view contentBinding="App.typeController" valueBinding="type" tagName="select"}}
{{#each content}}
<option {{bindAttr value="title"}}>{{title}}</option>
{{/each}}
{{/view}}
{{#view Ember.Button target="parentView" action="submitEntry"}}Save{{/view}}
The select is part of a form. I do check for the submit event and in there read the value:
app.js.coffee
# provides the select, add value: 'my_id' if you need differentiation
# between display name (title) and value
app.typeController = Ember.ArrayProxy.create
content: [{title:'Energy'}, {title:'Gas'}, {title:'Water'}]
# simplified version, but should prove the point
app.form_view = Ember.View.create
templateName: 'views_form'
type: null
submitEntry: () ->
console.log this.$().find(":selected").val()
Hope this helps.
This isn't an Answer, just a fix on the broken jsfiddle link.. Apparently jsfiddle has no love for ember :/ But JsBin does! http://jsbin.com/kuguf/1/edit