2013-02-24 35 views
1

我已經開始使用angular js,並且有一個問題,需要獲取我的控制器內部DOM的當前狀態。基本上我正在建立一個文本編輯器內部的contenteditable div。修改div中的文本可以來自外部服務(長時間輪詢從服務器推送)以及用戶實際在該字段中輸入的內容。現在來自服務器的修訂版正在操縱我的角度模型,然後通過ng-bind-html-unsafe指令更新視圖。唯一的問題是這會吹走用戶當前的光標位置和文本選擇。angular.js查詢DOM當前狀態的最佳實踐方式

我已經想出了一個解決問題的方法,但它需要直接在我的控制器中操作dom元素,這似乎在角度上是灰心喪氣的。我正在尋找對我目前的方法進行驗證,或者對更多「angulary」的東西進行推薦。

基本上我已經做了兩件事添加到我的模型,「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,並使用谷歌的diff_match_patch庫(dmp)製作補丁。

一旦內容改變,我調用範圍。$ apply()更新視圖。然後,我從視圖中獲取新文本並應用之前的補丁,這會將隱藏的標記添加回html。最後,我使用範圍來恢復選擇。

我不喜歡的部分是我如何使用jquery從視圖中獲取當前的html來構建和應用我的修補程序。這將使單元測試有點棘手,但它感覺不對。但是,鑑於這個矮胖的圖書館是如何工作的,我想不出有另一種方式去做。

+0

爲什麼你的文本編輯器不能成爲指令而不是控制器? – 2013-02-24 07:45:31

+0

它可能會,這是我第一次進入角度,所以我仍然在我的腳下,我仍然需要對指令做更多的研究。從你的建議,我會想象這樣的事情。帶有兩個參數的指令:1.字符串屬性的路徑。 2.編輯方法。無論何時用戶編輯元素,該指令都會創建一個補丁並傳遞該編輯方法。然後該指令將字符串屬性的值備份到視圖。那是你在想什麼? – AndrewSwerlick 2013-02-24 20:39:49

+0

是的,類似的東西。不知道你是否需要編輯方法。就像你說的,你會傳遞一個字符串屬性的名字來綁定。如果外面有什麼需要知道什麼時候改變了,那麼您應該在該屬性上添加一個監視。 – 2013-02-24 20:48:12

回答

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> 

這只是綁定到一個文本,所以你會替換你真正的文本編輯器。關鍵是要聽取文本編輯器中文本的更改,並更新範圍值,以便外界知道用戶更改了文本編輯器中的文本。