REST api。ユーザーはアプリケーションにアクセスするためにログインする必要があります。ユーザーがログインすると、/ dashboardにリダイレクトされます。ユーザーの役割に基づいて異なるテンプレートとコントローラーをロードしたい(例:normaluserまたはadmin ユーザー)。
私は https://github.com/angular-ui/ui-router/wiki をテンプレートセクションの下で見ましたが、どのオプションも私が達成しようとしていることをサポートしていません。
また、すべてのソリューションは、UserDashboardControllerやAdminDashboardControllerなど、ユーザーロールに基づいて異なるコントローラーをロードする必要があります。
だから、私が必要なのは、ユーザーがログインしたときにサービスで設定されるユーザーロール変数に基づいて、異なるテンプレートとコントローラーをロードする単一のルートです
私は正しい線に沿って考えていますか、または別のソリューションを実装する必要がありますか?
これに関する助けは大歓迎です。
技術的にはui-router
templateUrl関数はサービスのインジェクトをサポートしていませんが、templateProvider
を使用してservice
変数を保持するか非同期でロードし、$templateFactory
を使用してrole
をインジェクトできます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
をすでに持っています。 ui-router
状態定義内のcontrollerProvider
オプションの次の例を見てください。
controllerProvider: function(session){
if(session.role == 'admin'){
return 'AdminCtrl';
} else {
return 'UserCtrl';
}
}
もちろん、このコードから重複を簡単に削除し、よりアクセスしやすいマイクロDSLを定義して、特定の役割とビューに異なるルールを簡単に定義できます。
次の demo は、コードを理解するのに役立ちます。
通常、これはコンテキストに大きく依存します。 yoを支援するために、最初に以下の質問を提案させてください。
いくつかのbutton
sとその他のアクション要素のみを非表示にして、基本的にページを通常のユーザーのみが読み取り、スーパーユーザーが編集できるようにしますか?変更が小さい場合は、おそらく同じビューを使用して特定の要素のみを非表示にし、特定の機能を宣言的に有効/無効にするng-if
に似たディレクティブを偽造する可能性がありますonly-role='operator, admin'
。一方、viewsが大幅に異なる場合、異なるテンプレートを使用するとマークアップが大幅に簡素化されます。
表面上で似ているように見えるアクションは、役割ごとに内部の仕組みが異なりますか?例では、Editアクションがuser
とadmin
の両方のロールで使用可能ですが、ある場合にはで開始します/ wizardUIやその他の上級ユーザー向けの複雑な形式の場合は、個別のcontroller
を使用する方が理にかなっています。一方、admin
アクションがuser
アクションのスーパーセットである場合、単一のコントローラーを使用する方がわかりやすいようです。どちらの場合もcontroller
を維持することは効果があることに注意してください。サービス/ビューモデル/モデル/名前の選択にカプセル化された動作にビューを接着するだけです
たとえば、現在のユーザーに関係なくui-sref="dashboard"
と書くだけで特定のpageへのナビゲーションを提供できるrole
は、さまざまなコンテキスト。その場合、それらを単一のルート/状態で定義すると、ロールに基づいて異なるui-sref
/ng-href
を構築するために使用される条件付きロジックの方が保守しやすいように見えます。ただし、ユーザーロールに基づいてルート/状態を動的に定義する-動的にロードするかどうか
最初に通常のユーザー向けに機能を構築し、次にプレミアム向け機能を構築し、次に究極の機能を構築する場合があります。特に明確な境界線を簡単に描くことができる場合、チームメンバー間でuser
とadmin
のページで作業を分けることは珍しいことではありません。このような場合、views
とcontrollers
を別々に用意することで、開発者は競合を避けて作業することができます。もちろん、すべての虹とユニコーンではありません-チームは重複を取り除くために非常に規律を守らなければなりませんそれは起こりそうです。
私の提案があなたの決定に役立つことを願っています。
私は正しい線に沿って考えていますか、または別のソリューションを実装する必要がありますか?
IMO、このようにすべきではありません。
ここでは、アプリケーションの実装方法に応じて、他に2つのソリューションを提案します。
1)ロールの権限設定可能の場合(ロールを設定する別のページがあり、ロールに権限を割り当てます...)。次に、ロール(通常のユーザー、管理ユーザーなど)に1つのテンプレートと1つのコントローラーのみを使用し、ng-show
、ng-class
、..それに応じてHTMLを表示します。
この場合、ユーザーが通常のユーザーであるか管理者ユーザーであるかはあまり気にしません。これは単にロールの名前です。私たちが気にしているのはrightsで、動的です=>したがって、設定された権限に基づいて動的にhtmlを表示する必要があります(確かに、ユーザーがユーザーが悪意のあるHTTPリクエストを作成してサーバーに投稿するのを防ぎます)。そのために別のテンプレートを使用する場合、countlessのケースがあります。
このソリューションのポイントは、ページの機能がロールに対して同じであり、ユーザーに基づいてページの機能をshow/hideするだけでよいことです。
2)ロールの権限固定(設定不可)および機能通常のユーザーと管理ユーザーのビューが異なる場合。これらのビューにseparate状態を使用し、ログインしているユーザーに基づいてこれらのビューへのアクセスを承認することをお勧めします(確かに、ユーザーがアクションを実行するときにサーバー側にも承認があります)。
理由は次のとおりです。管理者ユーザービューと通常のユーザービューには異なる機能(互いに分離する必要があります)
バージョン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';
}
}
};
}]);
})();
I. Donotuse"...異なるテンプレートをロードする単一のルート..."は、私の提案でしょう。私の答え。
可能なら:
後戻りして全体のデザインを再検討し、
アプリケーションusersがurl
に関心があるというweaken senseを試してください。
ではない。そして、url
、アドレスバー...を本当に理解している場合は、copy
、send
、およびpaste
に使用します...その部分を調査しません...
II.提案: i-routerの使用を強制states:
...
UI-Router
はaround statesで構成され、[〜#〜] optionally [〜#〜]にルートやその他の動作が付加される場合があります。 。
つまり、明確に定義された状態のグループ/階層としてアプリケーションを再考しましょう。 url
を定義できますが、ではなく、/(例: エラー状態 URLなし)
III.州周辺でアプリケーションを構築することでどのように利益を得ることができますか?
懸念の分離-私たちの目的であるべきです。
stateは、いくつかのビュー/コントローラー、リゾルバー、カスタムデータを収集するユニットです。 ..
つまり、より多くのstatesが再利用できることを意味しますviews、controllersなど。そのようなstatesは本当に異なる(同じビュー、異なるコントローラー)。しかし、それらは単一の目的です-それらはいくつかのシナリオを処理するためにあります:
また、多くのstatesが存在する可能性があります。州が100であっても、パフォーマンスの問題にはなりません。これらは単なる定義であり、他の部分への参照のセットであり、本当に必要な場合は後で使用する必要があります。
ユースケース、ユーザーストーリーをstateのレベルで定義したら、それらをセット/階層にグループ化できます。
これらのグループは、後で異なる形式(異なるメニュー項目)で異なるユーザーロールに表示できます。
しかし、最終的には、多くの自由が得られ、保守性が簡素化されました。
IV.アプリケーションを実行し続けるおよび成長中
状態がほとんどない場合、maintainanaceは問題ではないようです。しかし、アプリケーションが成功する可能性があります。成功し、成長します...そのデザインの内側。
状態定義(作業単位として)とその階層(どのユーザーロールがどの状態にアクセスできるか)を分割すると、管理が簡単になります。
状態の外側にセキュリティを適用する(イベントリスナーala '$stateChangeStart'
)の方がはるかに簡単で、テンプレートプロバイダーのリファクタリングが終了することはありません。また、セキュリティの主要部分は、UIが許可するものに関係なく、サーバーに適用する必要があります
V.要約:
templateProvider
のような素晴らしい機能がありますが、それは私たちのためにいくつかの興味深いことをすることができます(例: AngularJsでUI-Routerを使用してナビゲーションメニューを変更する )...
...セキュリティのために使用しないでください。これは、現在の役割に基づいて、既存の状態から構築されたメニュー/階層として実装できます。イベントリスナーは、ユーザーが許可された状態になっているかどうかを確認する必要がありますが、メインチェックはサーバーに適用する必要があります...
あなたは本当にルーターでそれを行う必要はありません。
最も簡単なことは、すべてのロールに対して1つのテンプレートを使用し、その中に動的な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>
私は次のソリューションを採用しました(これは理想的ではないかもしれませんが、そのようなシナリオではうまくいきました)。
ngController
を使用して、テンプレート自体にコントローラーを指定します。
汎用ビュー名(_views/dashboard.html
_など)を使用してテンプレートをロードします。
ログインしているユーザーロールが変更されるたびに、$templateCache.put(...)
を使用して_views/dashboard.html
_が参照するものを変更します。
アプローチの簡潔な例を次に示します。
_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'
}).
...
});
_
こちらもご覧くださいショートデモ。
(簡単にするためにngRoute
を使用していますが、すべての作業はUser
サービスによって行われるため、anyの違いはありません。)
この質問が投稿されてからしばらく経ちましたが、使用する方法が他の回答とは異なるため、回答を追加しています。
この方法では、ユーザーのロールに基づいてルートとテンプレートのURLを完全に分離し、ユーザーが表示を許可されていないルートにある場合は、ユーザーをインデックスページにリダイレクトします。
UIルーターでは、基本的にこのようなデータ属性を状態に追加します。
.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's 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;
}
}
});
上記に加えて、ユーザーにデータを表示する前に、サーバー側の認証チェックを行う必要があります。
ここで長い説明は必要ありません。
解決して$ 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();
}
});
});
素晴らしいプロジェクトがあります https://github.com/Narzerus/angular-permission ui-routerが必要です。それにもかかわらず、このプロジェクトはうまく機能しています。