angular.js是查询DOM当前状态的最佳实践方法

时间:2013-02-24 01:52:09

标签: angularjs rangy

我已经开始使用角度js并且有一个问题需要获取控制器内部DOM的当前状态。基本上我正在一个可信的div内构建一个文本编辑器。 div中文本的修改可以来自外部服务(从服务器推送长轮询)以及实际在该字段中键入的用户。现在,服务器的修订版正在操纵我的角度模型,然后通过ng-bind-html-unsafe指令更新视图。唯一的问题是,这会消除用户当前光标位置和文本选择。

我已经想出了解决这个问题的方法,但它需要直接操作我的控制器中的dom元素,这似乎是在角度上不鼓励的。我正在寻找对我当前方法的验证,或者对更具“棱角分明”的东西的推荐。

基本上我所做的是在我的模型中添加两​​个事件,“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)制作补丁。

更改内容后,我调用范围。$ apply()更新视图。然后我从视图中获取新文本并应用之前的补丁,这将隐藏的标记添加回html。最后,我使用范围来恢复选择。

我不喜欢的部分是我如何使用jquery从视图中获取当前的html来构建和应用我的补丁。它会让单元测试变得有点棘手,感觉不对劲。但鉴于这个笨拙的图书馆如何运作,我想不出另一种方法。

1 个答案:

答案 0 :(得分:2)

以下是一个如何开始的简单示例:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
    <script type="text/javascript">
    function Ctrl($scope) {
        $scope.myText = "Here's some text";
    }

    angular.module("myApp", []).directive('texteditor', function() {
        return {
            restrict: 'E',
            replace: true,
            template: '<textarea></textarea>',
            scope: {
                text: '=' // link the directives scopes `text` property
                          // to the expression inside the text attribute
            },
            link: function($scope, elem, attrs) {
                elem.val($scope.text);
                elem.bind('input', function() {
                    // When the user inputs text, Angular won't know about
                    // it since we're not using ng-model so we need to call 
                    // $scope.$apply() to tell Angular run a digest cycle
                    $scope.$apply(function() {
                        $scope.text = elem.val();
                    });
                });
            }
        };
    });
    </script>
</head>
<body>
    <div ng-controller="Ctrl">
        <texteditor text="myText"></texteditor>
        <p>myText = {{myText}}</p>
    </div>
</body>
</html>

它只是绑定到textarea,所以你要用真正的文本编辑器替换它。关键是在文本编辑器中监听文本的更改,并更新范围的值,以便外界知道用户更改了文本编辑器中的文本。