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

AngularJS, ui.router, шаблон загрузки и контроллер на основе роли пользователя

Я разработал одностраничное приложение, в котором используется REST api. Пользователи должны войти в систему для доступа к приложению. Когда пользователь входит в систему, они перенаправляются на панель управления. На этом URL/маршруте я хотел бы загрузить другой шаблон и контроллер на основе роли пользователя (например, обычного пользователя пользователя или admin).

Я просмотрел https://github.com/angular-ui/ui-router/wiki в разделе шаблонов, но ни один из вариантов не поддерживает то, что я пытаюсь достичь.

  • Используя templateUrl и функцию (stateParams), я не могу внедрить службу, которая помогает мне определить роль пользователя, чтобы я мог загрузить шаблон, например. views/ пользователь/dashboard.html или представления / admin/dashboard.html
  • Используя templateProvider, я должен ввести службу, которая помогает мне определить роль пользователя, но как я могу загрузить шаблон?

Любое решение должно также загружать разные контроллеры на основе роли пользователя, например UserDashboardController или AdminDashboardController.

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

Я думаю о правильных строках или мне нужно реализовать другое решение?

Любая помощь по этому поводу будет принята с благодарностью.

4b9b3361

Ответ 1

Загрузка шаблона и контроллера на основе роли пользователя

В то время как технически ui-router функция templateUrl не поддерживает инъекционные службы, вы можете использовать templateProvider для ввода service, который содержит переменную role или загружает ее асинхронно, а затем использовать $templateFactory для возврата содержимого HTML. Рассмотрим следующий пример:

var app = angular.module('app', ['ui.router']);

app.service('session', function($timeout, $q){
    this.role = null;

    this.loadRole = function(){
        //load role using axax request and return promise
    };
});

app.config(function($stateProvider, $urlRouterProvider){
    $stateProvider.state('dashboard', {
        url: '/dashboard',
        templateProvider: function(session, $stateParams, $templateFactory){
          return session.loadRole().then(function(role){
              if(session.role == 'admin'){
                return $templateFactory.fromUrl('/admin/dashboard.html', $stateParams);
              } else {
                return $templateFactory.fromUrl('/user/dashboard.html', $stateParams);
              }
          });
        }
      });

    $urlRouterProvider.otherwise('/dashboard');
});

Что касается controller, вы можете указать, что хотите использовать определенный контроллер внутри корневого элемента каждого шаблона с помощью ng-controller. Или аналогичным образом вы можете использовать опцию controllerProvider для ввода service, чтобы уже разрешал role на templateProvider. Взгляните на следующий пример параметра controllerProvider внутри определения состояния ui-router:

controllerProvider: function(session){
  if(session.role == 'admin'){
    return 'AdminCtrl';
  } else {
    return 'UserCtrl';  
  }
}

Конечно, вы можете легко удалить дубликаты из этого кода и определить более доступную микро DSL, чтобы упростить определение различных правил для определенных ролей и представлений.

Следующая демонстрация должна помочь вам понять код.

Правильно ли это?

Как обычно, это сильно зависит от контекста. Чтобы помочь вам придумать ответ, позвольте мне сначала задать следующие вопросы:

  • Сколько представлений для ролей различаются?

Собираетесь ли вы скрыть только пару button и других элементов действий, в основном делая страницу прочитанной только для обычных пользователей и редактируемой для суперпользователей? Если изменения будут небольшими, я бы, вероятно, пошел с использованием одних и тех же представлений и только скрывал определенные элементы, возможно, создавая директиву, похожую на ng-if, которая позволила бы разрешить/отключить определенную функциональность декларативно only-role='operator, admin'. С другой стороны, если представления будут значительно отличаться, то использование разных шаблонов может значительно упростить разметку.

  • Сколько действий доступно на определенной странице в зависимости от роли?

Соответствуют ли действия, похожие на поверхности, во внутренней работе для разных ролей? В примере, если у вас есть действие "Правка" доступно как для роли user, так и admin, но в одном случае он запускает мастер, например пользовательский интерфейс, а в другой сложной форме для продвинутых пользователей, тогда наличие отдельного controller имеет больше смысла. С другой стороны, если admin действия являются надмножеством действий user, то с одним контроллером легче следовать. Обратите внимание, что в обоих случаях сохранение controller вещей окупается - они должны только склеить представления на поведение, которое инкапсулируется в сервисы/модели/модели моделей/выбрать имя

  • Будет ли у вас много контекстуально отдельных ссылок, ведущих к определенной странице из разных мест приложения?

Например, возможность предоставления навигации на конкретную страницу путем простого написания ui-sref="dashboard" независимо от текущего пользователя role может быть полезна, если она существует в разных контекстах. Если этот случай, когда их определение в одном маршруте/состоянии кажется более пригодным для обслуживания, тогда условная логика используется для создания разных ui-sref/ng-href на основе роли. Однако вы также можете определять маршруты/состояния динамически на основе роли пользователя, загруженной динамически или не

  • Будет ли просмотр и действия, доступные для разных ролей на определенной странице, развиваться отдельно или вместе?

Иногда мы сначала создаем функции для обычных пользователей, а затем для премиум-класса, а затем для окончательного. Нередко разделить работу на страницах на user и admin между членами команды, особенно если четкие границы можно легко сделать. В этом случае отдельные views и controllers могут просто разработчики избегать конфликтов. Конечно, не все радуги и единороги - команда должна быть очень дисциплинированной, чтобы удалить дублирование, что, скорее всего, произойдет.

Надеюсь, что мои предложения помогут вам решить.

Ответ 2

Я думаю о правильных строках или должен ли я реализовывать другое решение?

ИМО, ты не должен так поступать.

Здесь я предлагаю 2 других решения в зависимости от того, как реализовано ваше приложение.

1) Если права ваших ролей могут быть настроены (у вас может быть отдельная страница для настройки ролей, назначения прав на ваши роли...). Затем используйте только 1 шаблон и 1 контроллер для своих ролей (обычные пользователи, администраторы и т.д.) И используйте ng-show, ng-class,.. для отображения вашего HTML соответственно.

В этом случае нам все равно, является ли пользователь обычным пользователем или администратором, это просто имя нашей роли. Мы заботимся о правах и динамическом = > Поэтому мы должны отображать html динамически на основе настроенных прав (наверняка, есть также проверки на стороне сервера, когда пользователи выполняют действие для запретить пользователю создавать вредоносный HTTP-запрос и отправлять на сервер). Если бы мы использовали отдельные шаблоны для этого, существуют бесчисленные случаи.

Точка этого решения состоит в том, что функции страницы одинаковые для ваших ролей, вам просто нужно показать/скрыть функции страницы на основе пользователя.

2) Если права ролей фиксированы (не могут быть настроены), а функциональность для обычных пользователей и пользователей-администраторов различна. Лучше использовать отдельные состояния для этих представлений и разрешать доступ к этим представлениям на основе зарегистрированного пользователя (конечно, на стороне сервера также есть авторизация, когда пользователи выполняют действие).

Причина: пользовательский вид администратора и обычный пользовательский вид имеют различные функции (которые должны быть отделены друг от друга)

Ответ 3

Если вы используете версию angular больше 1,2, вы можете сделать директиву с templateUrl как функцию.

Итак, основные идеи: у вас есть представление панели управления, на котором есть настраиваемая директива, определяющая шаблон на основе пользовательского уровня. Так что-то вроде этого:

(function () {
  'use strict';
  angular.module('App.Directives')
    .directive('appDashboard', ['UserManager', function (UserManager) {
      return {
        restrict: 'EA',
        templateUrl: function(ele, attr){
            if (UserManager.currentUser.isAdmin){
                return 'admin.html';
            }else{
                return 'user.html';
            }
        }
      };
    }]);
})(); 

Ответ 4

I. Использовать не "... один маршрут, который загружает другой шаблон...", было бы моим предложением, моим ответом.

Если возможно:

Попробуйте отступить и пересмотреть весь дизайн и Попробуйте ослабить смысл, что наше приложение заинтересовано в url.

Это не так. И если они действительно понимают, что такое url, адресная строка... они используют его для copy, send и paste... не исследовать его части...

II. Предложение: Обеспечить использование ui-router состояния

... UI-Router организован вокруг состояний, который может OPTIONALLY иметь маршруты, а также другое поведение, прикрепленное...

Это означает, что пересмотреть наше приложение как группу/иерархию четко определенных состояний. Они могут иметь url, , но не должны (например, состояние ошибки без URL-адреса)

III. Как мы можем извлечь выгоду из создания нашего приложения вокруг состояний?

Разделение беспокойства - должно быть нашей целью.

Состояние - это единица, которая собирает некоторые виды/контроллеры, преобразователи, пользовательские данные...

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

  • администрирование записи User/Emplyoee
  • список пользователей/сотрудников - информация ala PhoneList (только электронная почта, телефон...)
  • Управление безопасностью - каковы права пользователя ...

И снова может быть много состояний . Даже сто штатов не будет проблемой выбора. Это просто определения, набор ссылок на другие части, которые следует использовать... позже... если это действительно необходимо.

Как только мы определили примеры использования, истории пользователей на уровне состояния , мы можем сгруппировать их в группы /heirarchies.
Эти группы могут быть позже представлены различным ролям пользователей в другом формате (разные пункты меню).

Но в конце мы получили много свободы и упрощенной поддержки

IV. Поддерживайте приложение и увеличивайте

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

Разделение определений sate (как единицы работы) и их иерархии (какая пользовательская роль может получить доступ к этим состояниям) упростит управление.

Применение безопасности за пределами штатов (прослушиватели событий ala '$stateChangeStart') намного проще, а затем никогда не заканчивается рефакторинг провайдеров шаблонов. Кроме того, основная часть безопасности должна по-прежнему применяться на сервере, независимо от того, какой пользовательский интерфейс позволяет

V. Резюме:

Пока есть такая замечательная функция, как templateProvider, которая может сделать для нас интересный материал (например, здесь: Изменение меню навигации с использованием UI-маршрутизатора в AngularJs)...

... мы не должны использовать его для обеспечения безопасности. Это может быть реализовано как некоторое меню/иерархия, построенная из существующих состояний на основе текущей роли. Слушатели событий должны проверить, подходит ли пользователь для предоставления статуса, но основная проверка должна быть применена на сервере...

Ответ 5

Я использовал следующее решение (которое может быть не идеальным, но оно сработало для меня в таких сценариях):

  • Укажите контроллер в самом шаблоне, используя ngController.

  • Загрузите шаблон с использованием общего имени представления (например, views/dashboard.html).

  • Измените то, что views/dashboard.html ссылается, используя $templateCache.put(...), когда изменяется измененная пользовательская роль.


Вот пример использования подхода:

app.controller('loginCtrl', function ($location, $scope, User) {
    ...
    $scope.loginAs = function (role) {
        // First set the user role
        User.setRole(role);

        // Then navigate to Dashboard
        $location.path('/dashboard');
    };
});

// A simplified `User` service that takes care of swapping templates,
// based on the role. ("User" is probably not the best name...)
app.service('User', function ($http, $templateCache) {
    var guestRole = 'guest';
    var facadeUrl = 'views/dashboard.html';
    var emptyTmpl = '';
    var errorTmpl = 'Failed to load template !';
    var tempTmpl  = 'Loading template...';

    ...

    // Upon logout, put an empty template into `$templateCache`
    this.logout = function () {
        this.role = guestRole;
        $templateCache.put(facadeUrl, emptyTmpl);
    };

    // When the role changes (e.g. upon login), set the role as well as the template
    // (remember that the template itself will specify the appropriate controller) 
    this.setRole = function (role) {
        this.role = role;

        // The actual template URL    
        var url = 'views/' + role + '/dashboard.html';

        // Put a temporary template into `$templateCache`
        $templateCache.put(facadeUrl, tempTmpl);

        // Fetch the actual template (from the `$templateCahce` if available)
        // and store it under the "generic" URL (`views/dashboard.html`)
        $http.get(url, {cache: $templateCache}).
              success(function (tmpl) {
                  $templateCache.put(facadeUrl, tmpl);
              }).
              error(function () {
                  // Handle errors...
                  $templateCache.put(facadeUrl, errorTmpl);
              });
    };

    // Initialize role and template        
    this.logout();
});

// When the user navigates to '/dashboard', load the `views/dashboard.html` template.
// In a real app, you should of course verify that the user is logged in etc...
// (Here I use `ngRoute` for simplicity, but you can use any routing module.)
app.config(function ($routeProvider) {
    $routeProvider.
        when('/dashboard', {
            templateUrl: 'views/dashboard.html'
        }).
        ...
});

См. также эту

Ответ 6

Вам действительно не нужно делать это с маршрутизатором.

Простейшим является использование одного шаблона для всех ролей и использование динамического ng-include внутри него. Предположим, что у вас есть инжектор в $scope:

<div ng-include="injector.get('session').role+'_dashboard.html'"></div>

Итак, у вас должны быть представления user_dashboard.html и admin_dashboard.html. Внутри каждого из них можно применить отдельный контроллер, например user_dashboard.html:

<div id="user_dashboard" ng-controller="UserDashboardCtrl">
    User markup
</div>

Ответ 7

Нет необходимости в подробном объяснении здесь.

Используйте решение и измените $route. $$ route.templateUrl или используйте routeChangeError, передав новый маршрут или соответствующий параметр в обещание.

var md = angular.module('mymodule', ['ngRoute']);
md.config(function($routeProvider, $locationProvider) {
    $routeProvider.when('/common_route/:someparam', {
        resolve: {
            nextRoute: function($q, $route, userService) {
                defer = $q.defer()
                userService.currentRole(function(data) { defer.reject({nextRoute: 'user_based_route/'+data) });
                return defer.promise;
            }
        }
    });
    $rootScope.$on("$routeChangeError", function(evt, current, previous, rejection) {
      if (rejection.route) {
        return $location.path(rejection.route).replace();
      }
    });
});

Ответ 9

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

В этом методе я полностью разделяю URL маршрута и шаблона на основе этой роли пользователя и перенаправляю пользователя на страницу индекса, если они находятся на маршруте, на который они не разрешены.

С UI Router я в основном добавляю атрибут данных, как это, в состояние:

.state('admin', {
            url: "/admin",
            templateUrl: "views/admin.html",
            data: {  requireRole: 'admin' }
        })

Когда пользователь аутентифицируется, я сохраняю их данные роли в localstorage и $rootscope с контроллера следующим образом:

var role = JSON.stringify(response.data); // response from api with role details

// Set the stringified user data into local storage
localStorage.setItem('role', role);

// Putting the user role on $rootScope for access by other controllers
$rootScope.role = response.data;

Наконец, я использую $stateChangeStart для проверки роли и перенаправления пользователя, если пользователь не должен просматривать страницу:

.run(['$rootScope', '$state', function($rootScope, $state) {

        // $stateChangeStart is fired whenever the state changes. We can use some parameters
        // such as toState to hook into details about the state as it is changing
        $rootScope.$on('$stateChangeStart', function(event, toState) {

                var role = JSON.parse(localStorage.getItem('role'));
                $rootScope.role = role;

                // Redirect user is NOT authenticated and accesing private pages
                var requireRole = toState.data !== undefined
                                  && toState.data.requireRole;

                 if( (requireRole == 'admin' && role != 'admin')) )
                 {
                   $state.go('index');
                   event.preventDefault();
                   return;
                 }
     }

});

В дополнение к вышесказанному вы все равно должны будете выполнить проверку авторизации на стороне сервера, прежде чем вы сможете отобразить какие-либо данные пользователю.