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

Angularjs js是查询DOM当前状态的最佳实践方法,angularjs,rangy,Angularjs,Rangy,我已经开始使用angular js,但有一个问题需要在控制器中获取DOM的当前状态。基本上,我正在contenteditable div中构建一个文本编辑器。对div中文本的修改可以来自外部服务(来自服务器的长轮询推送)以及用户实际在字段中键入的内容。现在来自服务器的修订正在操作我的angular模型,然后通过ng bind html指令更新视图。唯一的问题是,这会破坏用户当前的光标位置和文本选择 我已经找到了解决这个问题的方法,但是它需要在我的控制器中直接操作dom元素,这在角度上似乎是不被鼓

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

我已经找到了解决这个问题的方法,但是它需要在我的控制器中直接操作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。然后我使用插件将隐藏的dom元素注入到文档中,以标记用户的当前位置和选择。我使用不带隐藏标记的html和带标记的html,并使用google的diff_match_补丁库(dmp)制作补丁

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


我不喜欢的部分是如何使用jquery从视图中获取当前html来构建和应用补丁。这会让单元测试变得有点棘手,而且感觉不太对劲。但是考虑到rangy库的工作方式,我想不出其他方法来实现它。

下面是一个简单的示例,说明如何开始:

<!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>

函数Ctrl($scope){
$scope.myText=“这里有一些文本”;
}
angular.module(“myApp”,[]).directive('texteditor',function(){
返回{
限制:'E',
替换:正确,
模板:“”,
范围:{
text:'='//链接指令作用域'text'属性
//指向文本属性内的表达式
},
链接:函数($scope、elem、attrs){
元素val($scope.text);
元素绑定('input',function(){
//当用户输入文本时,Angular将不知道
//因为我们没有使用ng模型,所以需要调用
//$scope.$apply()告诉您如何运行摘要循环
$scope.$apply(函数(){
$scope.text=elem.val();
});
});
}
};
});
myText={{myText}}


它只是绑定到一个textarea,所以您可以用真正的文本编辑器替换它。关键是在文本编辑器中侦听文本的更改,并更新范围上的值,以便外部世界知道用户在文本编辑器中更改了文本

为什么你的文本编辑器不能是一个指令而不是一个控制器呢?它可能可以,这是我第一次尝试angular,所以我仍然需要对指令做更多的研究。根据你的推荐,我可以想象这样的事情。接受两个参数的指令:1。字符串属性的路径。2.编辑方法。每当用户编辑元素时,该指令都会生成一个补丁并将其传递给编辑方法。然后该指令将字符串属性的值复制回视图。你是这么想的吗?是的,差不多吧。但不确定是否需要编辑方法。正如您所说,您将传递要绑定到的字符串属性的名称。如果外部需要知道什么时候发生了变化,您可以在该属性上添加一个监视。如果您将此作为一个答案,并使用一点基本的伪代码突出显示该指令的框架,我将接受它。当然,我已经添加了一个简单的示例作为答案。如果答案不够清楚,请对其进行评论。