5

私は AngularJS に比較的慣れていないので、Restangular を使用して AngularJS の CRUD メソッドを DRY に保つためのさまざまな方法を試してきたので、コミュニティからのアドバイスをいただければ幸いです。

多くの API エンドポイントがあり、それぞれに項目のリストを表示し、各項目を選択して (その項目の詳細を表示するために)、削除したり、項目をリストに追加したりしたいと考えています。

私がこれまでに試したアプローチはどれも、非常に非 DRY であるか、$scope に適切にフックされていないか、ややハッキーでした...

ラッピング逆角

Restangular をサービスにラップし、addElementTransformer を使用して返されたコレクションに追加のメソッドを設定することは、適切なアプローチのように思えました。selectメソッドとメソッドはクリーンで、addテンプレートから簡単に実装でき、promise が解決されるとリストを自動的に更新します。ただし、deleteメソッドは実際には要素スコープ (コレクションの子) 内から呼び出されるため、削除後にリストを更新するには、非常に疑わしい $scope をサービスに注入する必要があります。

(注: 単一の API エンドポイント/コントローラー/テンプレートのみを示すことで、例を読みやすくしました)

アプリ/js/services.js

function ListTransformer(collection) {
    collection.selected = {}
    collection.assignedTo = {}
    collection.assignedName = ""

    collection.add = function(item) {
        collection.post(item)
        .then(function(response){
            collection.push(response);
        })
    };
    collection.delete = function(item, scope) {
        item.remove()
        .then(function(response){
            console.log(item)
            console.log(collection)
            collection.assignedTo[collection.assignedName] = _.without(collection, item);
        })
    };
    collection.select = function(item) {
        this.selected = item;
    };
    collection.assignTo = function(scope, name) {
        scope[name] = collection;
        collection.assignedTo = scope
        collection.assignedName = name
    };
    return collection;
};

angular.module('myApp.services', ['restangular'])
.factory('Company', ['Restangular',
    function(Restangular){
        var restAngular = Restangular.withConfig(function(Configurer) {
                Configurer.addElementTransformer('companies', true, ListTransformer);
        });

        var _companyService = restAngular.all('companies');

        return {
            getList: function() {
                return _companyService.getList();
            }
        }
    }
])

app/js/controllers.js

angular.module('myApp.controllers', [])
.controller('CompanyCtrl', ['$scope', 'Company', function($scope, Company) {

    Company.getList()
    .then(function(companies) {
        companies.assignTo($scope, 'companies'); // This seems nasty
    });
}]);

アプリ/部分/会社.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <a href="" ng-click="clients.delete(client)">DEL</a>
    </li>
</ul>
<hr>
{{companies.selected.name}}<br />
{{companies.selected.city}}<br />

ベース コントローラの拡張

私が試した別のアプローチは、他のコントローラーが継承するベースコントローラーでテンプレートをサービスに接続することでした。ただし、ここではまだ $scope の問題があり、テンプレートからスコープを渡す必要がありますが、これは正しくないようです。

delete(メソッドだけに編集)

アプリ/js/services.js

angular.module('myApp.services', ['restangular'])
.factory('ListController', function() {
    return {
        delete: function(scope, item, list) {
            item.remove();
            scope.$parent[list] = _.without(scope.$parent[list], item); 
        }
    };
});

app/js/controllers.js

.controller('CompanyCtrl', ['$scope', 'ListController', 'Restangular', function($scope, ListController, Restangular) {
    angular.extend($scope, ListController);

    $scope.companies = Restangular.all('companies').getList();
}])

アプリ/部分/会社.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <a href="" ng-click="delete(this, companies, company)">DEL</a>
    </li>
</ul>

ディレクティブ

私はディレクティブに少し慣れていませんが、多くの調査の結果、これがおそらく最もAngularJSにアプローチする方法であると思われるので、飛び込みました。基本的に問題は、リストからアイテムを削除できますが、すべての変数をディレクティブに渡さない限り、リストが最初に割り当てられた $scope のプロパティにアクセスできないため、アクセスできないことです。ビューで更新します。

app/js/directives.js

angular.module('myApp.directives', [])
.directive('delete', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<button>Delete</button>',
        link: function (scope, element, attrs) {
            element.bind('click', function() {
                var item = scope[attrs.item];
                item.remove();
                scope.$parent[attrs.from] = _.without(scope.$parent[attrs.from], item)
            });
        },

    };
});

アプリ/部分/会社.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <delete item="company" from="companies">
    </li>
</ul>

基本的に、私はすべてを機能させることができますが、多くの繰り返しコードを実行するか (これは明らかに間違っています)、すべての呼び出しと共にスコープを送信するか (これも非常に間違っているようです)、またはサービスを初期化する必要があるようです。スコープを渡すことによって(私のコントローラーにとって最も適切なソリューションではありませんが、これを達成するための最も維持しやすく、最も脆弱な方法のようです.

もちろん、機能を削除するか、詳細ビューまたはチェックボックス/一括アクション機能に移動することもできますが、問題に夢中になり、最善の解決策を見つけることに興味がありました:)

明らかな何かが欠けている場合はお詫びします。

4

1 に答える 1

3

ディレクティブから始めて、メモを書き留めています。

最大の学習曲線は、スコープの継承です。一般的な経験則ではscope、ディレクティブから返されたオブジェクトにプロパティを追加しない場合、ディレクティブはその親スコープを継承します。

したがって、削除ボタンに表示するCompanyCtrlディレクティブは、ディレクティブの内部にスコープ オブジェクトを定義していないため、同じスコープを持ちます。

つまり、どちらの場所にも削除関数を記述して、削除ボタンで使用できます。を使用して独自のクリック ハンドラーを作成しないでくださいelement.bind('click')。主な理由は、その中でスコープ項目を操作するときは、通常、scope.$apply() を呼び出して、スコープが外部イベントから変更されたことを角度に知らせる必要があるためです。ng-clickスコープに直接バインドするので使用します。削除機能の場合:

スリム化された属性。カスタム テンプレートと角度範囲のバインドされたクリック ハンドラーを持つ単なるタグです。

<!-- company object comes from ng-repeat so can pass it right into a click handler as param-->
<delete ng-click="deleteCompany(company)">

この関数は、ディレクティブまたはコントローラーのいずれかに記述できます...それらは同じスコープを共有するためです。

.controller('CompanyCtrl', function($scope){
    $scope.deleteCompany=function(company){
        /* I've never used restangular...using native methods*/
        /* call my AJAX service and on resolve*/
        var idx= $scope.companies.indexOf(comapny);
        /* remove from array*/
        $scope.companies.splice(idx,1); 
    }
})

ディレクティブで同じ機能になります

link:function(scope,elem,attrs){
      /* call my AJAX service and on resolve*/
    var idx= scope.companies.indexOf(comapny);
    /* remove from array*/
    scope.companies.splice(idx,1); 
 }

あるコントローラーを別のコントローラーに挿入することで、コントローラー間でデータを共有しようとしているようです。最初の考え方は、ディレクティブの深いネストなど、必要に応じてディレクティブと同様に、サービスを共有することです。

データをサービスに保存し、それを必要とするコントローラー/ディレクティブにサービスを注入します。すべてのデータがプリミティブ (文字列またはブール変数) としてではなくオブジェクトとして保存されている限り、オブジェクトはアプリ内のどこからでも参照でき、プロトタイプの継承を共有できます。スコープのネストに関係なく、どこからでも変更でき、Angular オブジェクト ウォッチは、そのオブジェクトが参照されている場所ならどこでも DOM を変更します。

これは、フォーム送信ビューから別のコントローラーを使用して別のビューに移動し、コントローラー間でデータを渡したい人のために、以前に遊んでいた小さなデモです。基本的にフォーム データを受け取り、それを共有サービスに渡し、モック AJAX 呼び出しを行い、フォーム データをサービスに保存し、パスを変更して新しいテンプレートとコントローラーを初期化します。データはサービスに保存されるため、2 番目のビュー コントローラーが必要とするときに準備が整います。私が話していることを視覚化するのに役立つかもしれません...そうではないかもしれません. また、自動角度フォーム検証がどのように開始されるかを示します

サービス デモを介してデータを渡すビュー/コントローラーを切り替える

于 2013-11-04T03:59:41.227 に答える