Dennis
Dennis

Reputation: 1018

Getting the selected option when using Marionette CompositeView with select tag

I'm using a CompositeView to display a select tag, and the options are being rendered through using the CompositeView's collection.

OptionView = Marionette.ItemView.extend({
    template: ...
    tagName: "option",
  }
});

SelectView = Marionette.CompositeView.extend({
    template: ...           
    childView: OptionView,
    childViewContainer: "select",       

    triggers: {
        "change @ui.select": "do:something"
    },

    ui:{
        select: "[ui-select]"
    },
});

The weird thing is that when I try to get the selected option from the LayoutView that holds the SelectView, I'm getting inconsistent behavior:

EditActivities.LayoutView = Marionette.LayoutView.extend({
    template: ...
    regions: ...

    onChildviewDoSomething: function(view){
        console.log(view.ui.select.val());
    }
})

When I change the value of the select tag on the browser, sometimes what gets logged to the console is the inner HTML of the option tag, and sometimes it logs the value. This is what my OptionView template looks like:

<option value="<%=id%>"><%= name %></option>

I'm a little stumped on the inconsistent behavior. Ideas?

Upvotes: 0

Views: 1834

Answers (3)

Akshad
Akshad

Reputation: 81

As BaskingShark mentioned, the attributes hash can be used to specify attributes to the option elements. Item View can be used for the option element.

var OptionView = Marionette.ItemView.extend({
    tagName: 'option',
    attributes: function () {
        return {
            'value': this.model.get('id')
        }
    },
    template: _.template('<%= name %>')
});

A collection view that uses the item view can be used for the select tag.

var SelectOptionsView = Marionette.CollectionView.extend({
    childView: OptionView
});

The collection view can be rendered inside a region of a layout view, where the events on the select tag can also be listened to. Fiddle for an example: http://jsfiddle.net/95g1ojwk/3/

var pills = new Backbone.Collection([{
    id: '',
    name: 'Choose'
}, {
    id: 'illusion',
    name: 'Blue'
}, {
    id: 'reality',
    name: 'Red'
}]);

var OptionView = Marionette.ItemView.extend({
    tagName: 'option',
    attributes: function () {
        return {
            'value': this.model.get('id')
        }
    },
    template: _.template('<%= name %>')
});

var SelectOptionsView = Marionette.CollectionView.extend({
    childView: OptionView
});

var MyLayoutView = Mn.LayoutView.extend({
    ui: {
        destiny: "#destiny",
        choice: '#choice'
    },
    regions: {
        "options": "#options-region",
    },
    events: {
        'change @ui.choice': 'chosenDestiny'
    },
    onRender: function () {
        this.showChildView('options', new SelectOptionsView({
            collection: pills,
            el: this.ui.choice
        }));
    },
    chosenDestiny: function (event) {
        if($(event.target).val()==='reality'){
            this.ui.destiny.text('Nothing but Truth!');
        } else if($(event.target).val()==='illusion'){
            this.ui.destiny.text('The Story Ends!');
        } else {
            this.ui.destiny.text('This is your last chance!');
        }
    }
});

new MyLayoutView({
    el: '#app',
    template: '#layout-template'
}).render();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>
<script src="http://marionettejs.com/downloads/backbone.marionette.js"></script>
<script type="text/template" id="layout-template">
    <div id="options-region">
        <select id="choice"></select>
    </div > 
    <label id = "destiny" > </label>
</script>
<script type="text/template" id="image-template">
    <div class = "sizer" > <img src = "http://placehold.it/<%= width %>x<%= height %>" /> </div>
</script>
<div id="app"></div>

Upvotes: 0

BaskingShark
BaskingShark

Reputation: 171

To include the value attribute of the option along with the innerHTML without having to resort to modifying things in initialize, you can use Backbone's attributes hash like this:

SelectView = Marionette.CompositeView.extend({
  tagName: 'option',
  template: _.template('<%= name %>'),
  attributes: function() {
    return {
      'value': this.model.id
    };
  }
});

Upvotes: 1

Dennis
Dennis

Reputation: 1018

I figured it out. The problem was in the OptionView I was specifying the tagName is "option". In the template, I also had the option tag, which essentially created a set of nested option tags. This is what was causing the unpredictable behavior.

I changed it so my template is now just:

<%= name %>

The issue I now have is how to set the value attribute of the option tag, because right now it would produce and option tag with no value for each of the items. My current fix is to just add an initialize function on the view like the following:

initialize : function (options) {
  this.$el.attr("value",this.model.attributes.id);
}

but I'm definitely open to if there's a better way of doing this...

Upvotes: 1

Related Questions