How to bind value form input select to attribute in controller

15,370

Solution 1

Ember now has a built-in Select view.

You can find it in the latest Ember.js build here: http://cloud.github.com/downloads/emberjs/ember.js/ember-latest.js

Here's an example usage:

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/

Solution 2

Jumping off from the solution for @pangrantz, this Fiddle example (http://jsfiddle.net/bsyjr/) illustrates some improvements: The Handlebars code is cleaner through the use of tagName. When tagName is set to "select", the child views automatically become "option" elements. See the Ember.CollectionView.CONTAINER_MAP in https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/collection_view.js to understand why. On the Javascript side, by specifying an itemViewClass, we can add the value attribute to the option element.

<script type="text/x-handlebars" >
    {{#collection Food.SelectView tagName="select" contentBinding="Food.foodController"
        valueBinding="Food.appsController.selectedValue"}}
      {{content.title}}
    {{/collection}}

    selected: {{view Ember.TextField valueBinding="Food.appsController.selectedValue"}}{{Food.appsController.selectedValue}}
</script>

Food = Ember.Application.create();

Food.SelectView = Ember.CollectionView.extend({
    value: null,
    itemViewClass: SC.View.extend({
        attributeBindings:['value'],
        valueBinding: 'content.value'
    }),

    valueChanged: function(){
        this.$().val( this.get('value') );
    }.observes('value'),

    didInsertElement: function(){
        var self = this;
        this.$().change(function(){
            var val = $('select option:selected').val();
            self.set('value', val);
        });
    }
});

Food.appsController = Ember.Object.create({
  selectedValue: ""
});

Food.Todo = Ember.Object.extend({
  title: null,
  value: null
});

Food.foodController = Ember.ArrayProxy.create({
  content: []
});

Food.foodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
Food.foodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
Food.foodController.pushObject(Food.Todo.create({title:"c", value:"3"}));

There is still room for improvement in the event handling, which is not using Ember's event framework, and it would make a lot of sense to use a custom written SelectView that doesn't leverage Handlebars, since IMO, it is dubious how much value Handlebars adds in this case.

Solution 3

Using a custom Ember.View works for me, but I think there is a better solution...

See working example is this fiddle http://jsfiddle.net/pangratz/hcxrJ/

Handlebars:

{{#view Food.SelectView contentBinding="Food.foodController"
    valueBinding="Food.appsController.selectedValue"}}
    <select>
        {{#each content}}
            <option {{bindAttr value="value"}} >{{title}}</option>
        {{/each}}
    </select>
{{/view}}

app.js:

Food = Ember.Application.create();

Food.SelectView = Ember.View.extend({
    value: null,

    valueChanged: function(){
        this.$('select').val( this.get('value') );
    }.observes('value'),

    didInsertElement: function(){
        var self = this;
        this.$('select').change(function(){
            var val = $('select option:selected').val();
            self.set('value', val);
        });
    }
});

Food.appsController = Ember.Object.create({
  selectedValue: ""
});

Food.Todo = Ember.Object.extend({
  title: null,
  value: null
});

Food.foodController = Ember.ArrayProxy.create({
  content: []
});

Food.foodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
Food.foodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
Food.foodController.pushObject(Food.Todo.create({title:"c", value:"3"}));

Solution 4

I'm not sure if this is of use to others, but I've been doing something similar based on the answers here, and have made this SelectView that should work in this context too. It binds to 'change', works out the view that is currently selected and then does something with its content.

Food.SelectView = Ember.CollectionView.extend({
  change: function(e) {
    var selected = this.$().find(":selected").index();
    var content = this.get('childViews')[selected].content;
    // Do something with the result
    Food.appsController.set('selectedValue', content.title);
  }
});

This way you're able to pass around an object rather than the index of the select.

Share:
15,370
Bank
Author by

Bank

Updated on June 26, 2022

Comments

  • Bank
    Bank almost 2 years

    I try to bind value from input select to attribute "selectedValue" in controller.

    This is app.js

    Food = Ember.Application.create();
    
    Food.appsController = Ember.Object.create({
      selectedValue: ""
    });
    
    Food.Todo = Ember.Object.extend({
      title: null,
      value: null
    });
    
    Food.FoodController = Ember.ArrayProxy.create({
      content: []
    });
    
    Food.FoodController.pushObject(Food.Todo.create({title:"a", value:"1"}));
    Food.FoodController.pushObject(Food.Todo.create({title:"b", value:"2"}));
    Food.FoodController.pushObject(Food.Todo.create({title:"c", value:"3"}));
    

    This is index.html

    {{#collection
        contentBinding="Todos.todosController"
        tagName="select"
        itemClassBinding="content.isDone"}}
      {{content.title}}
    {{/collection}}
    

    Output look like this

    <select id="ember180" class="ember-view">
      <option id="ember192" class="ember-view">
        <script id="metamorph-0-start" type="text/x-placeholder"></script>
        a
        <script id="metamorph-0-end" type="text/x-placeholder"></script>
      </option>
      <option id="ember196" class="ember-view">
        <script id="metamorph-1-start" type="text/x-placeholder"></script>
        b
        <script id="metamorph-1-end" type="text/x-placeholder"></script>
      </option>
      <option id="ember200" class="ember-view">
        <script id="metamorph-2-start" type="text/x-placeholder"></script>
        c
        <script id="metamorph-2-end" type="text/x-placeholder"></script>
      </option>
    </select>
    

    I have no idea how to add value to option and how to binding selected value back to controller. Is this possible to do in Emberjs?

  • Peter Wagenet
    Peter Wagenet over 12 years
    It's worth noting that the {{#collection}} helper is deprecated as the same results can be achieved with greater clarity using {{#each}} as @pangratz did.
  • Bank
    Bank over 12 years
    should add this view to Ember core :-)
  • ebryn
    ebryn over 12 years
    I've got an improved version that will be included with Ember soon: github.com/emberjs/ember.js/pull/424
  • Peter Wagenet
    Peter Wagenet about 12 years
    To provide further clarification, there are still some places where {{#collection}} is useful. However, I would try using {{#each}} first and if it you aren't able to get the results you want then consider {{#collection}} we plan to make further improvements to {{#each}} which will cover more cases.
  • Visionscaper
    Visionscaper almost 12 years
    This solution works well for me for multiple reasons: a) The Ember.Select view gives back the whole object in the content array as selected value, while I'm only interested to get back the actual select value. b) For what I'm working on this implementation is much faster: I don't need bindings on the labels and values, they don't change (I removed the valueChanged method in the view class extension above since I didn't need it) Final remark : the select change handler method contains a small error (when you have multiple selectors): $('select options... should be self.$('select options...
  • HaoQi Li
    HaoQi Li almost 11 years
    btw, this is the Ember.Select api
  • Patrick M
    Patrick M about 10 years
  • Mike
    Mike almost 9 years
    Ember.Select is now deprecated. What is the updated way to do this?