web-dev-qa-db-ja.com

AngularJSで自動動的ブレッドクラムを作成する方法+ Angular UI Router

Webアプリケーションの重要なコンポーネントの1つは、パンくずリスト/ナビゲーションです。 Angular UI Routerでは、ブレッドクラムメタデータをコントローラーではなく個々の状態に配置するのが理にかなっています。必要な各コントローラーのブレッドクラムオブジェクトを手動で作成するのは簡単ですタスクですが、それは非常に面倒なものでもあります。

Angularを使用した自動ブレッドクラムのソリューションを見てきましたが、正直なところ、かなり原始的なものです。ダイアログボックスやサイドパネルなどの一部の状態では、パンくずリストを更新するべきではありませんが、現在の角度のアドオンでは、それを表現する方法がありません。

別の問題は、パンくずリストのタイトルが静的ではないことです。たとえば、ユーザーの詳細ページに移動する場合、パンくずリストのタイトルは一般的な「ユーザーの詳細」ではなく、ユーザーのフルネームである可能性があります。

解決する必要がある最後の問題は、親リンクに正しい状態パラメーター値をすべて使用することです。たとえば、会社のユーザー詳細ページを表示している場合、親状態には:companyIdが必要であることを明らかに知りたいでしょう。

このレベルのパンくずリストのサポートを提供するangularへのアドオンはありますか?そうでない場合、それを回避する最善の方法は何ですか?コントローラーを乱雑にしたくない-私は持っていますそれらの多く-そして私はそれを可能な限り自動化され、痛みのないようにしたいと思います。

ありがとう!

34
egervari

何も利用できなかったので、私はしばらく前にこれを自分で解決しました。 dataオブジェクトを使用しないことにしました。これは、実際にはパンくずリストのタイトルを子供に継承させたくないからです。時には、モーダルダイアログとその中をスライドする右側のパネルがありますが、それらは技術的には「子供向けのビュー」ですが、パンくずリストには影響しないはずです。代わりにbreadcrumbオブジェクトを使用することにより、自動継承を回避できます。

実際のtitleプロパティには、$interpolateを使用しています。別の場所で解決を行わなくても、パンくずリストのデータを解決スコープと組み合わせることができます。私が持っていたすべてのケースで、とにかく解決スコープを使用したかったので、これは非常にうまく機能します。

私のソリューションもi18nを処理します。

$stateProvider
    .state('courses', {
        url: '/courses',
        template: Templates.viewsContainer(),
        controller: function(Translation) {
            Translation.load('courses');
        },
        breadcrumb: {
            title: 'COURSES.TITLE'
        }
    })
    .state('courses.list', {
        url: "/list",
        templateUrl: 'app/courses/courses.list.html',
        resolve: {
            coursesData: function(Model) {
                return Model.getAll('/courses');
            }
        },
        controller: 'CoursesController'
    })
    // this child is just a slide-out view to add/edit the selected course.
    // It should not add to the breadcrumb - it's technically the same screen.
    .state('courses.list.edit', {
        url: "/:courseId/edit",
        templateUrl: 'app/courses/courses.list.edit.html',
        resolve: {
            course: function(Model, $stateParams) {
                return Model.getOne("/courses", $stateParams.courseId);
            }
        },
        controller: 'CourseFormController'
    })
    // this is a brand new screen, so it should change the breadcrumb
    .state('courses.detail', {
        url: '/:courseId',
        templateUrl: 'app/courses/courses.detail.html',
        controller: 'CourseDetailController',
        resolve: {
            course: function(Model, $stateParams) {
                return Model.getOne('/courses', $stateParams.courseId);
            }
        },
        breadcrumb: {
            title: '{{course.name}}'
        }
    })
    // lots more screens.

パンくずをアプリケーションに視覚的に表示する方法は複数あると考えたため、パンくずをディレクティブに結び付けたくありませんでした。だから、私はそれをサービスに入れました:

.factory("Breadcrumbs", function($state, $translate, $interpolate) {
    var list = [], title;

    function getProperty(object, path) {
        function index(obj, i) {
            return obj[i];
        }

        return path.split('.').reduce(index, object);
    }

    function addBreadcrumb(title, state) {
        list.Push({
            title: title,
            state: state
        });
    }

    function generateBreadcrumbs(state) {
        if(angular.isDefined(state.parent)) {
            generateBreadcrumbs(state.parent);
        }

        if(angular.isDefined(state.breadcrumb)) {
            if(angular.isDefined(state.breadcrumb.title)) {
                addBreadcrumb($interpolate(state.breadcrumb.title)(state.locals.globals), state.name);
            }
        }
    }

    function appendTitle(translation, index) {
        var title = translation;

        if(index < list.length - 1) {
            title += ' > ';
        }

        return title;
    }

    function generateTitle() {
        title = '';

        angular.forEach(list, function(breadcrumb, index) {
            $translate(breadcrumb.title).then(
                function(translation) {
                    title += appendTitle(translation, index);
                }, function(translation) {
                    title += appendTitle(translation, index);
                }
            );
        });
    }

    return {
        generate: function() {
            list = [];

            generateBreadcrumbs($state.$current);
            generateTitle();
        },

        title: function() {
            return title;
        },

        list: function() {
            return list;
        }
    };
})

実際のブレッドクラムディレクティブは、非常に簡単になります。

.directive("breadcrumbs", function() {
    return {
        restrict: 'E',
        replace: true,
        priority: 100,
        templateUrl: 'common/directives/breadcrumbs/breadcrumbs.html'
    };
});

そしてテンプレート:

<h2 translate-cloak>
    <ul class="breadcrumbs">
        <li ng-repeat="breadcrumb in Breadcrumbs.list()">
            <a ng-if="breadcrumb.state && !$last" ui-sref="{{breadcrumb.state}}">{{breadcrumb.title | translate}}</a>
            <span class="active" ng-show="$last">{{breadcrumb.title | translate}}</span>
            <span ng-hide="$last" class="divider"></span>
        </li>
    </ul>
</h2>

ここのスクリーンショットから、両方のナビゲーションで完全に機能することがわかります。

enter image description here

Html <title>タグと同様に:

enter image description here

PS to Angular UI Team:すぐにこのようなものを追加してください!

40
egervari

これに対する私のソリューションを共有したいと思います。コントローラーに何も挿入する必要がないという利点があり、名前付きブレッドクラムラベルをサポートし、resolve:関数を使用してブレッドクラムに名前を付けます。

状態設定の例:

$stateProvider
    .state('home', {
        url: '/',
        ...
        data: {
            displayName: 'Home'
        }
    })
    .state('home.usersList', {
        url: 'users/',
        ...
        data: {
            displayName: 'Users'
        }
    })
    .state('home.userList.detail', {
        url: ':id',
        ...
        data: {
            displayName: '{{ user.name | uppercase }}'
        }
        resolve: {
            user : function($stateParams, userService) {
                return userService.getUser($stateParams.id);
            }
        }
    })

次に、ディレクティブの属性でブレッドクラムラベル(displayname)の場所を指定する必要があります。

<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>

このようにして、ディレクティブは$state.$current.data.displayNameの値を見て使用するテキストを見つけることを認識します。

$補間可能なパンくず名

最後の状態(home.userList.detail)では、displayNameは通常のAngular補間構文{{ value }}を使用します。これにより、状態設定のresolveオブジェクトで定義された値を参照できます。通常、これは、ユーザー名の上記の例のように、サーバーからデータを取得するために使用されます。これは通常のAngular文字列であるため、上記の例のようにdisplayNameフィールドに任意のタイプの有効なAngular式を含めることができます。それ。

デモ

Plunkerのデモを以下に示します。 http://plnkr.co/edit/bBgdxgB91Z6323HLWCzF?p=preview

コード

ここにすべてのコードを配置するのは少し多すぎると思ったので、ここにGitHubにあります: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/uiBreadcrumbs

24
Michael Bromley

Angular ui-routerの状態に基づいてパンくずリストを生成するモジュールを作成しました。あなたが話すすべての機能が含まれています(最近、この記事を読んでいる間、パンくずリストの状態を無視する可能性を追加しました:-)):

githubリポジトリ

コントローラースコープ(ネストされたビューまたは複数のビューの場合は「最も深い」)に対して補間された動的ラベルを許可します。

状態のチェーンは、状態オプションによってカスタマイズ可能です( APIリファレンス を参照)

モジュールには事前定義されたテンプレートが付属しており、ユーザー定義のテンプレートを使用できます。

4
ncuillery

機能が組み込まれているとは思いませんが、すべてのツールが用意されています。 LocationProvider をご覧ください。ナビゲーション要素にこれを使用させ、知りたいことは何でも注入するだけです。

ドキュメント

0
Gent

Ui-routerの内部を深く掘り下げた後、解決されたリソースを使用してブレッドクラムを作成する方法を理解しました。

ここに私の指令への配管工です。

注:このコードをプランカー内で適切に機能させることはできませんでしたが、ディレクティブはプロジェクトで機能します。 routes.jsは、パンくずリストのタイトルを設定する方法の例としてのみ提供されています。

0
Nailer

@egervariが提供するソリューションをありがとう。パンくずリストのカスタムデータにいくつかの$ stateParamsプロパティを追加する必要がある場合。キー 'title'の値の構文{:id}を拡張しました。

.state('courses.detail', {
    url: '/:courseId',
    templateUrl: 'app/courses/courses.detail.html',
    controller: 'CourseDetailController',
    resolve: {
        course: function(Model, $stateParams) {
            return Model.getOne('/courses', $stateParams.courseId);
        }
    },
    breadcrumb: {
        title: 'course {:courseId}'
    }
})

Plunker の例を次に示します。参考までに。

0
Indiana