我已经开始使用 Angular js,并且遇到了一个问题,需要在我的控制器中获取 DOM 的当前状态。基本上,我正在一个 contenteditable div 中构建一个文本编辑器。对 div 中文本的修订可以来自外部服务(来自服务器的长轮询推送)以及实际在字段中输入的用户。现在来自服务器的修订正在操纵我的角度模型,然后通过 ng-bind-html-unsafe 指令更新视图。唯一的问题是这会影响用户当前的光标位置和文本选择。
我已经找到了解决这个问题的方法,但它需要在我的控制器中直接操作 dom 元素,这在 Angular 中似乎是不鼓励的。我正在寻找对我当前方法的验证,或者对更“有角度”的东西的推荐。
基本上我所做的是在我的模型中添加了两个事件,“contentChanging”和“contentChanged”。第一个是在我更新模型之前触发的,第二个是在我更新模型之后触发的。在我的控制器中,我像这样订阅这些事件。
//dmp is google's diff_match_patch library
//rangy is a selection management library http://code.google.com/p/rangy/wiki/SelectionSaveRestoreModule
var selectionPatch;
var selection;
scope.model.on("contentChanging", function() {
var currentText = $("#doc").html();
selection = rangy.saveSelection();
var textWithSelection = $("#doc").html();
selectionPatch = dmp.patch_make(currentText, textWithSelection);
});
scope.model.on("contentChanged", function() {
scope.$apply();
var textAfterEdit = $("#doc").html();
$("#doc").html(dmp.patch_apply(selectionPatch, textAfterEdit)[0]);
rangy.restoreSelection(selection);
});
所以基本上,当内容发生变化时,我会抓取可编辑区域的当前 html。然后我使用rangy插件将隐藏的 dom 元素注入到文档中来标记用户当前的位置和选择。我使用不带隐藏标记的 html 和带标记的 html,并使用 google 的 diff_match_patch 库(dmp)制作补丁。
更改内容后,我调用 scope.$apply() 来更新视图。然后我从视图中获取新文本并应用之前的补丁,这会将隐藏的标记添加回 html。最后我使用范围来恢复选择。
我不喜欢的部分是我如何使用 jquery 从视图中获取当前 html 以构建和应用我的补丁。这会让单元测试变得有点棘手,而且感觉不太对劲。但是考虑到 rangy 库的工作方式,我想不出另一种方法来做到这一点。