2012-11-14 29 views
8

我想将CodeMirror JavaScript编辑器集成到KnockoutJS中。我知道还有Ace,但在我看来CodeMirror会更容易。如何将CodeMirror整合到KnockoutJS中?

我已经集成的自定义绑定jQueryUI的小部件和QTip但这些代码段我在网上找到的,我则仅需要修改很小部分。

不幸的是,似乎我已经达到了我对Javascript的限制,所以我在这里转向JavaScript Sith Masters。我不一定要为我写的东西,指针和关于如何继续的建议会有很大的帮助。

的一段代码,我有:

的HTML(我删除自定义绑定我已经在textarea的,他们不事这里)

<body> 
    <textarea id="code" cols="60" rows="8" 
       data-bind="value: condition, 
       tooltip: 'Enter the conditions', 
       codemirror: { 'lineNumbers': true, 'matchBrackets': true, 'mode': 'text/typescript' }"></textarea> 
</body> 

我的自定义绑定处理程序的开始对于CodeMirror:

ko.bindingHandlers.codemirror = { 
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
     var options = valueAccessor() || {}; 
     var editor = CodeMirror.fromTextArea($(element)[0], options); 
    } 
}; 

此刻,这不会产生JS错误,但显示2个文本区域而不是一个。

那么接下来我该怎么做?

+0

请问您可以在jsfiddle中重现它吗?我没有看到你的绑定处理程序有什么问题。奇怪的是,你用jquery包装元素,然后取出第一个元素。你可以跳过这个,只提供元素。 – Anders

+0

@Anders谢谢,我删除了jquery包装。 JSFiddle:http://jsfiddle.net/SKZSm/ – Jalayn

回答

2

之前发布的解决方案似乎有点过时了。对我来说,所以我已经在这工作的形式改写他们不会工作:

// Example view model with observable. 
var viewModel = { 
    fileContent: ko.observable(''), 
    options: { 
     mode: "markdown", 
     lineNumbers: true 
    } 
}; 

// Knockout codemirror binding handler 
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; 
    }, 
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
     var observedValue = ko.unwrap(valueAccessor()); 
     if (element.editor) { 
      element.editor.setValue(observedValue); 
      element.editor.refresh(); 
     } 
    } 
}; 

ko.applyBindings(viewModel); 

随着<div data-bind="codemirror:fileContent"></div>为目标代码镜像附加到这将创建一个新的代码镜像实例,并在视图模型中传入选项(如果已设置)。

[编辑]我修改了codemirror结合处理器解开传递valueAccessor的更新方法,而不该行淘汰赛将不会触发更新方法时,可观察到的更新 - 它现在的作品,你会希望它至。

+0

接受你的回答,因为它比我的更新老东西:-) – Jalayn

+0

@Jalayn哇,谢谢你,我已经修改了我的代码示例来修复一个小错误,它现在可以按照预期工作 – carbontwelve

+0

@carbontwelve你为什么要重复viewModel的定义?那只是一个错字?Thx 。 – sidewinderguy

3

好吧,我终于设法做到了(请参阅更新的fiddle)。

我很快设法在自定义textarea中设置初始值。但在此之后,绑定元素不会被更新。

但是,CodeMirror的API允许您注册一个回调方法到onChange事件,以便每当textarea的内容被修改时被调用。所以这只是一个实现回调的问题,它更新了绑定元素的值。这是在选项中创建自定义文本区域时完成的。

这里是自定义绑定:

ko.bindingHandlers.codemirror = { 
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) { 
     var options = $.extend(valueAccessor(), { 
      onChange: function(cm) { 
       allBindingsAccessor().value(cm.getValue()); 
      } 
     }); 
     var editor = CodeMirror.fromTextArea(element, options); 
     element.editor = editor; 
     editor.setValue(allBindingsAccessor().value()); 
     editor.refresh(); 
     var wrapperElement = $(editor.getWrapperElement()); 

     ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 
      wrapperElement.remove(); 
     }); 
    } 
}; 

它可以或许缺少一些功能,但对于什么,我需要它完美的作品。

编辑:在Anders的评论中,我添加了addDisposeCallback部分,它在重新呈现模板时有效地销毁了由CodeMirror生成的DOM元素。由于CodeMirror产生的内容都在一个节点内,因此只需删除此节点即可。

+0

CodeMirror API是否提供拆卸功能?它的良好的KO练习听取ko.utils.domNodeDisposal.addDisposeCallback回调,如果模板绑定已完全重新显示视图,则引擎将调用此回调 – Anders

+0

@Anders非常感谢!我花了一些时间来重现和理解模板的问题,但我现在添加了一个自定义的处理回调函数,这看起来可行。 – Jalayn

+1

onChange处理程序不再工作。像这样修复:var editor = CodeMirror.fromTextArea(element,options); (“change”,function(cm){ allBindingsAccessor()。value(cm.getValue()); });' – nexuzzz

1

我试图使用上述结合的处理程序,但我有没有文本具有TextArea中可见,直到我进入的东西(一个空格或任何其他字符)的问题。

当加载完成,只要我在该地区输入的东西,所有绑定数据变得可见它是空白。

还有一个问题,如果我将一个简单的ko.observable()绑定到TextArea的值,一切都很好,但是如果我用ko.observableArray中的某个列替换它,值:selectedProfile()查询字符串,我从绑定的处理程序得到以下错误:有问题

线:editor.setValue(allBindingsAccessor()。值()); 错误:未捕获TypeError:对象#的属性'值'不是函数

想法?

/LM

+0

嗨Lars,我不确定你应该发布这个一个答案,但我会回答你的问题。你在数组中有什么不是可观察对象,只是一个简单的String,这就是为什么“value()”不起作用。您可以通过首先检查“allBindingsAccessor()。value”是一个函数(参见http://www.idealog.us/2007/02/check_if_a_java.html)并使用“allBindingsAccessor()”来更改我的代码。值“或”allBindingsAccessor().value()“视情况而定,希望有帮助! – Jalayn

+0

谢谢Jalayn,我会记得在下次创建一个新问题。 –

4

通过Jalayn(或之一的jsfiddle)不更新观测变量也编辑犯规显示负载值列出的代码..这是我更新的代码

ko.bindingHandlers.codemirror = { 
     init: function (element, valueAccessor, allBindingsAccessor, viewModel) { 
      var options = valueAccessor(); 
      var editor = CodeMirror.fromTextArea(element, options); 
      editor.on('change', function(cm) { 
       allBindingsAccessor().value(cm.getValue()); 
      }); 
      element.editor = editor; 
      if(allBindingsAccessor().value()) 
       editor.setValue(allBindingsAccessor().value()); 
      editor.refresh(); 
      var wrapperElement = $(editor.getWrapperElement()); 

      ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 
       wrapperElement.remove(); 
      }); 
     }, 
     update: function (element, valueAccessor) { 
      if(element.editor) 
       element.editor.refresh(); 
     } 
    }; 
0

我在上面的答案之一中遇到了与Lars相同的问题,即codemirror直到第一次交互时才开始加载数据。我通过添加一个标志来解决这个问题,通过键入或编程并订阅该值来确定codemirror值是否已更改。

ko.bindingHandlers.codemirror = { 
     init: function (element, valueAccessor, allBindingsAccessor) { 
      var typed = false; 
      var options = $.extend(valueAccessor(), { 
       onChange: function (cm) { 
        typed = true; 
        allBindingsAccessor().value(cm.getValue()); 
        typed = false; 
       } 
      }); 
      var editor = CodeMirror.fromTextArea(element, options); 
      element.editor = editor; 
      editor.setValue(allBindingsAccessor().value()); 
      editor.refresh(); 
      var wrapperElement = $(editor.getWrapperElement()); 
      ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 
       wrapperElement.remove(); 
      }); 

      allBindingsAccessor().value.subscribe(function (newValue) { 
       if (!typed) { 
        editor.setValue(newValue); 
        editor.refresh(); 
       } 
      }); 
     } 
    }; 

我相信会有一种'更清洁'的方法来达到同样的效果,但这似乎很好。

2

我在将光标设置为第一个位置时出现问题。 This fiddle修复程序,同时也接受CodeMirror options对象绑定值,并且内容绑定到一个options.value观察到的(我发现,减少混乱,因为这实际上是从那里CM得到它的starting value属性名)

ko.bindingHandlers.codemirror = { 
    init: function(element, valueAccessor) { 
     var options = ko.unwrap(valueAccessor()); 
     element.editor = CodeMirror(element, ko.toJS(options)); 
     element.editor.on('change', function(cm) { 
      options.value(cm.getValue()); 
     }); 

     ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 
      var wrapper = element.editor.getWrapperElement(); 
      wrapper.parentNode.removeChild(wrapper); 
     }); 
    }, 
    update: function(element, valueAccessor) { 
     var value = ko.toJS(valueAccessor()).value; 
     if (element.editor) { 
      var cur = element.editor.getCursor(); 
      element.editor.setValue(value); 
      element.editor.setCursor(cur); 
      element.editor.refresh(); 
     } 
    } 
}; 

示例HTML:

<div data-bind="codemirror: { 
    mode: 'javascript', 
    value: text 
}"></div> 
+0

那解决方案对我更好。我仍然有问题,但我认为这是因为我使用杜兰达。当视图组成时,编辑器不显示。只有当我点击它时,它才会弹出。然后它正常工作。 – Cilyan