CodeMirror自定义绑定在“foreach”绑定中不起作用

时间:2017-11-30 06:41:34

标签: knockout.js codemirror

我为CodeMirror创建了一个自定义绑定。自定义绑定使用简单的字符串,但在foreach绑定内,它不再被初始化,尽管添加了所有HTML和CSS。

这是一个工作片段:

var viewModel = {
  options: {
    mode: "text/x-csharp",
    lineNumbers: true
  },

  //IT WORKS
  fileContent: "public sealed class DictionaryAttribute : Attribute{}1",

  //IT DOESN'T WORK
  codes: ["public sealed class DictionaryAttribute : Attribute{}1"]
};

ko.bindingHandlers.codemirror = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var options = viewModel.options || {};
    options.value = ko.unwrap(valueAccessor());
    var editor = CodeMirror(element, options);

    editor.on('change', function(cm) {
      var value = valueAccessor();
      value(cm.getValue());
    });

    element.editor = editor;
  }
};

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link href="https://codemirror.net/lib/codemirror.css" rel="stylesheet" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://codemirror.net/mode/clike/clike.js"></script>

<!-- This works -->
<div data-bind="codemirror: fileContent" style="width: 700px; height: 100px"></div>

<!-- This doesn't work -->
<div data-bind="foreach: codes">
  <div data-bind="codemirror: $data" style="width: 700px; height: 100px"></div>
</div>

1 个答案:

答案 0 :(得分:2)

问题在于自定义绑定中的var options = viewModel.options || {};viewModel参数是指上下文中的当前$data,而不是applyBindings中使用的viewModel。它适用于一个简单的字符串,因为在这种情况下,viewModel参数 是您传递给viewModel的主applyBidnigs对象。在foreach内,viewModel将是您阵列中的每个$data

因此,请改用bindingContext参数的$root属性。此外,在Knockout 3.x中不推荐使用viewModel参数:

像这样: var options = bindingContext.$root.options || {};

更新了代码段:

var viewModel = {
  options: {
    mode: "text/x-csharp",
    lineNumbers: true
  },

  //IT WORKS
  fileContent: "public sealed class DictionaryAttribute : Attribute{}1",

  //IT DOESN'T WORK
  codes: ["public sealed class DictionaryAttribute : Attribute{}1"]
};

ko.bindingHandlers.codemirror = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var options = bindingContext.$root.options || {};
    options.value = ko.unwrap(valueAccessor());
    var editor = CodeMirror(element, options);

    editor.on('change', function(cm) {
      var value = valueAccessor();
      value(cm.getValue());
    });

    element.editor = editor;
  }
};

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link href="https://codemirror.net/lib/codemirror.css" rel="stylesheet" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://codemirror.net/mode/clike/clike.js"></script>

<div data-bind="codemirror: fileContent" style="width: 700px; height: 100px"></div>

<div data-bind="foreach: codes">
  <div data-bind="codemirror: $data" style="width: 700px; height: 100px"></div>
</div>

以上代码适用于您的情况。但是,绑定期望顶级$root对象具有options属性。另一种方法是在绑定中添加codeMirrorOptions参数并完全删除该依赖项。

var viewModel = {
  options: {
    mode: "text/x-csharp",
    lineNumbers: true
  },

  //IT WORKS
  fileContent: "public sealed class DictionaryAttribute : Attribute{}1",

  //IT DOESN'T WORK
  codes: ["public sealed class DictionaryAttribute : Attribute{}1"]
};

ko.bindingHandlers.codemirror = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // use allBindings
    var options = ko.unwrap(allBindings().codeMirrorOptions) || {};
    options.value = ko.unwrap(valueAccessor());
    var editor = CodeMirror(element, options);

    editor.on('change', function(cm) {
      var value = valueAccessor();
      value(cm.getValue());
    });

    element.editor = editor;
  }
};

ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<link href="https://codemirror.net/lib/codemirror.css" rel="stylesheet" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://codemirror.net/mode/clike/clike.js"></script>

<div data-bind="codemirror: fileContent, codeMirrorOptions:options" style="width: 700px; height: 100px"></div>

<div data-bind="foreach: codes">
  <div data-bind="codemirror: $data, codeMirrorOptions:$parent.options" style="width: 700px; height: 100px"></div>
</div>

在这种情况下,自定义绑定独立于viewModel。即使您的viewModel不是$root对象,自定义绑定也会有效。