淘汰赛无法处理绑定“foreach”

Posted

技术标签:

【中文标题】淘汰赛无法处理绑定“foreach”【英文标题】:knockout unable to process binding "foreach" 【发布时间】:2014-05-29 21:46:59 【问题描述】:

我是 Knockout 的新手,我正在构建一个实际上是大型计算器的应用。到目前为止,我在一页上运行了两个淘汰赛实例。一个实例运行良好,但另一个实例完全损坏,似乎根本无法注册?

下面是我的 javascript,fetchYear 是一个运行良好的函数,而 fetchPopulation 是完全坏掉的函数。它似乎根本没有从 html 中注册“ageview”,我无法弄清楚。

错误:

Uncaught ReferenceError: Unable to process binding "foreach: function ()return ageView " 消息:ageView 未定义

提前致谢。

JS:

var index = 

    fetchYear: function () 
        Item = function(year)
            var self = this;
            self.year = ko.observable(year || '');
            self.chosenYear = ko.observable('');
            self.horizon = ko.computed(function()
                if(self.chosenYear() == '' || self.chosenYear().horizon == undefined)
                    return [];
                return self.chosenYear().horizon;
            );
        ;
        YearViewModel = function(yeardata) 
            var self = this;
            self.yearSelect = yeardata;
            self.yearView = ko.observableArray([ new Item() ]);
            self.add = function()
                self.yearView.push(new Item("New"));
            ; 
        ;
        ko.applyBindings(new YearViewModel(yearData));
    ,

    fetchPopulation: function () 
        popItem = function(age)
            var self = this;
            self.age = ko.observable(age || '');
            self.chosenAge = ko.observable('');
            self.population = ko.computed(function()
                if(self.chosenAge() == '' || self.chosenAge().population == undefined)
                    return [];
                return self.chosenAge().population;
            );
        ;
        PopulationViewModel = function(populationdata) 
            var self = this;
            self.ageSelect = populationdata;
            self.ageView = ko.observableArray([ new popItem() ]);
            self.add = function()
                self.ageView.push(new popItem("New"));
            ; 
        ;
        ko.applyBindings(new PopulationViewModel(populationData));
    



index.fetchYear();
index.fetchPopulation();

HTML:

<div class="row" data-bind="foreach: yearView">
    <div class="grid_6">
        <img src="assets/img/index/calendar.png"   />
        <select class="s-year input-setting" data-bind="options: $parent.yearSelect, optionsText: 'year', value: chosenYear"></select>
        <label for="s-year">Start year for the model analysis</label>
    </div>
    <div class="grid_6">
        <img src="assets/img/index/clock.png"   />
        <select class="s-horizon input-setting" data-bind="options: horizon, value: horizon"></select>
        <label for="s-horizon">Analysis time horizon</label>
    </div>
</div>

<div class="row" data-bind="foreach: ageView">
    <div class="grid_6">
        <img src="assets/img/index/calendar.png"   />
        <select class="s-year input-setting" data-bind="options: ageSelect, optionsText: 'age', value: chosenAge"></select>
        <label for="s-agegroup">Age group of <br> target population</label>
    </div>
    <div class="grid_6">
        <img src="assets/img/index/clock.png"   />
        <input class="s-population input-setting"></input>
        <label for="s-population">Size of your patient <br> population <strong>National</strong> </label>
    </div>
</div>

【问题讨论】:

【参考方案1】:

当你这样做时(fetchYear):

ko.applyBindings(new YearViewModel(yearData));

您正在将整个页面与YearViewModel 视图模型绑定。但是 YearViewModel 没有名为 ageView 的属性,因此您会收到错误消息,并且淘汰赛停止尝试绑定其他任何内容。

你需要做的是通过将你想要的元素传递给ko.applyBindings来限制你的绑定只覆盖dom的一部分。例如:

<div class="row" id="yearVM" data-bind="foreach: yearView">
//....
<div class="row" id="popVM" data-bind="foreach: ageView">

然后:

ko.applyBindings(new YearViewModel(yearData), document.getElementById("yearVM"));
//...
ko.applyBindings(new PopulationViewModel(populationData), document.getElementById("popVM"));

现在您的绑定仅限于 DOM 中实际显示来自该模型的内容的部分。

另一种选择是将您的两个视图模型作为父视图模型的一部分,然后您可以将绑定应用于整个页面。如果您需要混合来自两个 VM 的部分并且它们不能方便地在页面的不同部分中分开,这会更容易。比如:

var myParentVM = 
    yearVM : index.fetchYear(),          // note, make this return the VM instead of binding it
    popVM : index.fetchPopulation(),     // ditto


ko.applyBindings(myParentVM);

然后你会像这样声明你的绑定:

<div class="row" data-bind="foreach: yearVM.yearView">

【讨论】:

这太好了,谢谢!我想知道是不是因为我两次调用绑定函数而我不知道我可以针对特定的 DOM 元素。我应该在我的 JS 中将替代方案(上面的选项 B)放在哪里才能让它正常工作?【参考方案2】:

这不起作用的主要原因是您在一个页面上多次调用 ko.applyBindings()(这并不是真正禁止的,但在我看来是一种不好的做法)。

如果你需要调用它两次,你必须用一个容器来调用它,这个绑定是针对哪个区域的。

类似这样的:

ko.applyBindings(new YearViewModel(yearData), document.getElementById('YourYearViewElementId'));

您得到的错误来自第一个绑定,它尝试处理整个页面并且在其 ViewModel 中找不到“ageView”。

如果您为单个页面构建单个 ViewModel 会更好,如果需要,您可以在其中为部分提供子模型。

这种场景的一些伪代码:

var Section1ViewModel = function() 
    var self = this;

    self.property1 = ko.observable();
    self.myComputed = ko.computed(function () 
        // do some fancy stuff
    );
    self.myFunc = function() 
        // do some more fancy stuff
    ;


var Section2ViewModel = function() 
    var self = this;

    self.property1 = ko.observable();
    self.myComputed = ko.computed(function () 
        // do some fancy stuff
    );
    self.myFunc = function() 
        // do some more fancy stuff
    ;


var PageViewModel = function() 
    var self = this;

    self.section1 = ko.observable(new Section1ViewModel());
    self.section2 = ko.observable(new Section2ViewModel());

    self.myGlobalFunc = function() 
        // do some even more fancy stuff
    


ko.applyBindings(new PageViewModel());

希望对您有所帮助。

最好的问候, 克里斯

【讨论】:

我选择了选项 A,但似乎遇到了另一个障碍。我现在想使用 fetchPopulation 中的一个值,该值现在绑定到 #populationVM,在 #populationVM 之外 - 这可能吗? 没有例子有点难。但是如果你使用单独的 ViewModel,那么你不应该互相引用(为此,我建议使用第二个选项,每页使用一个 VM)。但是你可以做什么(这很 hacky!):将构造的 ViewModel 定义为窗口变量,这样你就可以通过 window.xxx 访问它们(例如:window.yearModel = new YearViewModel(yeardata); 然后:window.yearModel.yearView () ...

以上是关于淘汰赛无法处理绑定“foreach”的主要内容,如果未能解决你的问题,请参考以下文章

如何在淘汰赛 foreach 绑定中使用表单

淘汰赛foreach绑定不起作用

带有按钮的表格上的敲除映射和 foreach 数据绑定,缺少对视图模型的引用?

淘汰foreach绑定,更新值

如何将项目从淘汰赛 foreach 传递到部分视图作为数据绑定?

observableArray 和 foreach 绑定的内存泄漏