2016-06-10 80 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