Подтвердить что ты не робот

Как сделать автоматизированные динамические палочки с помощью AnguleJS + Angular UI Router

Одним из ключевых компонентов веб-приложений является панихида/навигация. С Angular UI Router было бы целесообразно помещать метаданные с отдельными состояниями, а не в контроллеры. Ручное создание объекта панировки для каждого контроллера там, где это необходимо, - задача с прямым переходом, но также очень грязная.

Я видел некоторые решения для автоматизированных Breadcrumbs с Angular, но, честно говоря, они довольно примитивны. Некоторые состояния, такие как диалоговые окна или боковые панели, не должны обновлять панировочные сухари, но с текущими аддонами до Angular, нет способа выразить это.

Другая проблема заключается в том, что заголовки панировочных сухарей не являются статическими. Например, если вы перейдете на страницу "Сведения о пользователе", заголовок заголовка должен, вероятно, быть полным именем пользователя, а не "Детальной информацией пользователя".

Последняя проблема, которая должна быть решена, - использовать все правильные значения параметров состояния для родительских ссылок. Например, если вы просматриваете страницу сведений о пользователе от компании, очевидно, вам нужно знать, что для родительского состояния требуется :companyId.

Есть ли какие-либо дополнения к Angular, которые обеспечивают этот уровень поддержки панировочных сухарей? Если нет, каков наилучший способ сделать это? Я не хочу загромождать своих контроллеров - у меня их будет много - и я хочу сделать его максимально автоматическим и безболезненным.

Спасибо!

4b9b3361

Ответ 1

Я решил это самостоятельно некоторое время назад, потому что ничего не было. Я решил не использовать объект 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 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;
        }
    };
})

Фактическая директива breadcrumb становится очень простой:

.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 Команда пользовательского интерфейса: добавьте что-то вроде этого из коробки!

Ответ 2

Я хочу поделиться своим решением. Преимущество этого заключается в том, что вы не должны вводить какие-либо инъекции в ваши контроллеры и поддерживать названные ярлыки с надписью, а также использовать функции 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, вы можете включить любой тип допустимого выражения Angular в поле displayName - как в приведенном выше примере, где мы применяем к нему фильтр.

Demo

Вот рабочая демонстрация на Plunker: http://plnkr.co/edit/bBgdxgB91Z6323HLWCzF?p=preview

код

Я подумал, что здесь нужно добавить код, поэтому здесь он находится на GitHub: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/uiBreadcrumbs

Ответ 3

Я создал модуль Angular, который генерирует корневую папку на основе состояний ui-router. Все функции, о которых вы говорите, включены (недавно я добавил возможность игнорировать состояние в палитре при чтении этого сообщения:-)):

Вот github repo

Он позволяет динамическим ярлыкам, интерполированным по области контроллера ( "самый глубокий" в случае вложенных/множественных представлений).

Цепочка состояний настраивается с помощью опций состояния (см. ссылка на API)

Модуль поставляется с заранее определенными шаблонами и позволяет создавать пользовательские шаблоны.

Ответ 4

Я не верю, что есть встроенная функциональность, но все инструменты есть для вас, посмотрите LocationProvider. Вы можете просто использовать навигационные элементы и все, что вы хотите знать, просто вставляете его.

Документация

Ответ 5

После глубокого углубления в внутренние части ui-router я понял, как я могу создать пакет, используя разрешенные ресурсы.

Вот плункер к моей директиве.

ПРИМЕЧАНИЕ. Я не мог заставить этот код работать правильно внутри плунжера, но директива работает в моем проекте. routes.js предоставляется просто для примера, как вы можете устанавливать заголовки для ваших панировочных сухарей.

Ответ 6

Спасибо за решение, предоставленное компанией @egervari. Для тех, кому нужно добавить некоторые свойства $stateParams в пользовательские данные панировочных сухарей. Я добавил синтаксис {: id} для значения ключа 'title'.

.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. FYI.