2016-06-10 79 views
0

试图绑定text到视图模型之外的全局函数引发以下错误:绑定文本到全局函数

knockout.js:60 Uncaught ReferenceError: Unable to process binding "foreach: function(){return names }" Message: Unable to process binding "text: function(){return myFunction($data) }" Message: myFunction is not defined

Reproduction online

HTML

<ul data-bind="foreach: names"> 
    <li data-bind="text: myFunction($data)"></li> 
</ul> 

JS

function myFunction(text){ 
    return text + '--'; 
} 

function demoViewModel() { 
    self.names = ['a', 'b', 'c']; 

    return self; 
} 

var mm = new demoViewModel(); 

ko.applyBindings(mm); 

相反,如果我延长String对象,并以下列方式应用功能,它按预期工作:

<li data-bind="text: $data.myFunction()"></li> 

扩展String对象:

String.prototype.myFunction = function(){ 
    return this + '--'; 
} 

Reproduction online

这是为什么?将全局函数应用于text绑定没有更好的方法吗?

+0

与您的问题没有真正的关系,但请注意,您在demoVM上缺少'var self = this';您现在实际上使用全局[window.self](https://developer.mozilla.org/en-US/docs/Web/API/Window/self)对象作为您的虚拟机。 – Retsam

+0

使用'window.myFunction'明确地将它添加到全局上下文应该可以,对吧? https://jsfiddle.net/2prmfwyx/看看knockout的'bindingContext'是如何工作的以及它通过'with($ data || {})'''with'关键字的内部使用' – user3297291

回答

1

要引用您的功能从你的淘汰赛模板中,它需要被附加到ViewModel。在上面的简单的例子,你可以将其连接到demoViewModel且模板中直接引用它:

function myFunction(text){ 
 
    return text + '--'; 
 
} 
 

 
function demoViewModel() { 
 
    var self = this; 
 
    self.names = ['a', 'b', 'c']; 
 
    self.myFunction = myFunction; 
 

 
    return self; 
 
} 
 

 
ko.applyBindings(new demoViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 
<ul data-bind="foreach: names"> 
 
    <li data-bind="text: myFunction($data)"></li> 
 
</ul>

这是不是一个真正的“全球性”的功能,它只是一个标准的视图模型属性,如果你结束了嵌套的绑定上下文,你必须做$parents[n].myFunction或者,如果你已经将它附加到你的根viewModel,你可以做$root.myFunction


处理此问题的另一种方法是直接将函数添加到binding context。这允许它被引用,而不管当前viewModel是什么。

“foreach”绑定处理程序和模板绑定处理程序上的“as”选项是向绑定上下文添加内容的一种方式;但为此我使用“let”bindingHandler,let bindingHandler不是KO的官方部分,而是Michael Best的核心贡献者之一often recommended

function myFunction(text){ 
 
    return text + '--'; 
 
} 
 

 
function demoViewModel() { 
 
    var self = this; 
 
    self.names = ['a', 'b', 'c']; 
 
    self.myFunction = myFunction; 
 

 
    return self; 
 
} 
 

 

 
//Let binding Handler 
 
ko.bindingHandlers['let'] = { 
 
    'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
 
     // Make a modified binding context, with extra properties, and apply it to descendant elements 
 
     var innerContext = bindingContext.extend(valueAccessor()); 
 
     ko.applyBindingsToDescendants(innerContext, element); 
 

 
     return { controlsDescendantBindings: true }; 
 
    } 
 
}; 
 
ko.virtualElements.allowedBindings['let'] = true; 
 

 
ko.applyBindings(new demoViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 

 
<!-- ko let: { 
 
    myFunction: myFunction 
 
} --> 
 
    <ul data-bind="foreach: { 
 
     data: names, 
 
     at: 'name' 
 
    }"> 
 
     <li data-bind="text: myFunction($data)"></li> 
 
    </ul> 
 
<!-- /ko -->

在上面的例子中,你可以内let结合Anywhere参考myFunction,不管你是多少级的深深的ViewModels。

1

这是我的建议,做你想做的事。您的功能未在该范围内定义。在这里,你实际上每个名字(甚至也可以是一个对象)绑定到功能视图模型外
例子:https://jsfiddle.net/1hz10pkc/2/
HTML:

<ul data-bind="foreach: names"> 
    <li data-bind="text:name "></li> 
</ul> 

JS

var myFunction = function(text){ 
    var self = this; 
    self.name = text + "--" ; 
} 

function demoViewModel() { 
    var self = this; 
    var arr = ['a', 'b', 'c']; 
    self.names = ko.observableArray($.map(arr, function (element) { 
     return new myFunction(element); 
    })); 
} 
var mm = new demoViewModel(); 

ko.applyBindings(mm); 
+0

但我并不想要修改原始值。因此,我必须根据您的建议创建一个新变量。 – Alvaro

+0

我给了我自己的答案,但这个答案也很好。实际上,尝试在ViewModel中执行尽可能多的逻辑通常更好,而不是在模板中执行。 – Retsam

+0

请注意,您只需执行'arr.map',使用'$ .map'的唯一真正好处就是如果您尝试支持IE8。 – Retsam