【问题标题】:Bind text to a global function将文本绑定到全局函数
【发布时间】:2016-10-11 12:42:04
【问题描述】:

尝试将text 绑定到视图模型外部的全局函数会引发以下错误:

knockout.js:60 Uncaught ReferenceError: Unable to process binding "foreach: function (){return names }" 消息:无法处理绑定“文本:函数(){return myFunction($data)}” 消息:myFunction 未定义

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.prototype.myFunction = function(){
    return this + '--';
}

Reproduction online

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

【问题讨论】:

  • 与您的问题没有太大关系,但请注意您的 demoVM 上缺少 var self = this;您现在实际上正在使用全局 window.self 对象作为您的虚拟机。
  • 使用window.myFunction 将其显式添加到全局上下文中应该可行,对吧? jsfiddle.net/2prmfwyx 看看淘汰赛的bindingContext 是如何工作的,以及通过with($data || {})with 关键字的内部使用@

标签: knockout.js knockout-3.0 knockout-3.2


【解决方案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>

这并不是真正的“全局”函数,它只是一个标准的 viewModel 属性,如果您最终使用嵌套的绑定上下文,则必须执行 $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 绑定中的任何位置引用 myFunction,无论您的 viewModel 有多少层。

【讨论】:

    【解决方案2】:

    这是我做你想做的事的建议。您的功能未在该范围内定义。在这里,您实际上将每个名称(甚至可以是对象)绑定到视图模型之外的函数
    示例: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);
    

    【讨论】:

    • 但是我真的不想修改原来的值。因此,我必须根据您的建议创建一个新变量。
    • 我给出了自己的答案,但这个答案也很好。实际上,尝试在 ViewModel 中而不是在模板中执行尽可能多的逻辑通常会更好。
    • 请注意,您可以使用arr.map,使用$.map 的唯一真正好处是如果您尝试支持 IE8。
    • @Alvaro 在这个jsfiddle.net/9aLvd3uw/229 示例中,您可以定义一个计算的可观察绑定到name observable 所以每当name observable 发生变化时,计算变量也会更新。
    • @Retsam 我总是使用你提到的$.map 来支持IE8,以防万一有人还在使用它。
    猜你喜欢
    • 2019-12-06
    • 2012-05-07
    • 2017-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2012-11-09
    • 1970-01-01
    相关资源
    最近更新 更多