1

私はAngularjsの使用を開始しましたが、コントローラー内でDOMの現在の状態を取得する必要があるという問題があります。基本的に、私はcontenteditablediv内にテキストエディタを構築しています。div内のテキストの改訂は、ユーザーが実際にフィールドに入力するだけでなく、外部サービス(サーバーからの長いポーリングプッシュ)から行うことができます。現在、サーバーからのリビジョンが角度モデルを操作しており、ng-bind-html-unsafeディレクティブを介してビューを更新しています。これに関する唯一の問題は、これがユーザーの現在のカーソル位置とテキスト選択を吹き飛ばすことです。

問題を回避する方法を見つけましたが、コントローラーのdom要素を直接操作する必要があります。これは角度がお勧めできないようです。現在の方法の検証か、より「角度のある」ものに関する推奨事項のいずれかを探しています。

基本的に、私が行ったことは、モデルに「contentChanging」と「contentChanged」の2つのイベントを追加したことです。1つ目はモデルを更新する直前に起動され、2つ目はモデルを更新した直後に起動されます。私のコントローラーでは、このようなイベントをサブスクライブします。

//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要素をドキュメントに挿入するrangyプラグインを使用して、ユーザーの現在の位置と選択をマークします。非表示のマーカーのないhtmlとマーカーのあるhtmlを取得し、googleのdiff_match_patchライブラリ(dmp)を使用してパッチを作成します。

コンテンツが変更されたら、scope。$ apply()を呼び出してビューを更新します。次に、ビューから新しいテキストを取得し、以前のパッチを適用します。これにより、非表示のマーカーがhtmlに追加されます。最後に、範囲を使用して選択を復元します。

私が気に入らないのは、jqueryを使用してビューから現在のhtmlを取得し、パッチをビルドして適用する方法です。ユニットテストが少しトリッキーになり、気分が悪くなります。しかし、ランジーライブラリがどのように機能するかを考えると、それを行う別の方法を考えることはできません。

4

1 に答える 1

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>

テキストエリアにバインドするだけなので、実際のテキスト エディターに置き換えます。重要なのは、テキスト エディターでテキストの変更をリッスンし、スコープの値を更新して、ユーザーがテキスト エディター内のテキストを変更したことを外の世界が認識できるようにすることです。

于 2013-02-25T16:33:47.423 に答える