使用带有 select 标记的 Marionette CompositeView 时获取 selected 选项

Getting the selected option when using Marionette CompositeView with select tag

我正在使用 CompositeView 来显示 select 标记,并且正在使用 CompositeView 的集合呈现选项。

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]"
    },
});

奇怪的是,当我尝试从包含 SelectView 的 LayoutView 中获取 selected 选项时,出现了不一致的行为:

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

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

当我在浏览器上更改 select 标签的值时,有时记录到控制台的是选项标签的内部 HTML,有时它会记录值。这是我的 OptionView 模板的样子:

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

我对不一致的行为感到有点难过。想法?

我明白了。问题出在我指定的 tagName 是 "option" 的 OptionView 中。在模板中,我还有选项标签,它实际上创建了一组嵌套的选项标签。这就是导致不可预测行为的原因。

我更改了它,所以我的模板现在只是:

<%= name %>

我现在遇到的问题是如何设置选项标签的值属性,因为现在它会为每个项目生成没有值的选项标签。我目前的解决方法是在视图上添加一个初始化函数,如下所示:

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

但如果有更好的方法,我绝对乐于接受...

要将选项的值属性与 innerHTML 一起包含而不必诉诸于初始化中的修改,您可以像这样使用 Backbone 的属性散列:

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

正如 BaskingShark 提到的,属性散列可用于指定选项元素的属性。 项目视图可用于选项元素。

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

使用项目视图的集合视图可用于 select 标签。

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

集合视图可以在布局视图的区域内呈现,其中也可以监听 select 标签上的事件。 Fiddle 例如: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>