is it possible to create a nested views in ui router with conditions?
The conditions is assigned to the user roles.
For example I have two types of users: admin and user.
If user is opening the setting page then ui router is adding only this view which is assign to his role.
Here is example of my config code
var app = angular.module('myApp', ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'homeController'
})
.state('settings', {
url: '/settings',
data: {
roles: ['admin', 'moderator', 'user']
},
views:{
'':{
templateUrl:'/settings.html',
},
'piView#settings':{
data: {
roles: ['user']
},
templateUrl:'/personalInformation.html'
},
'permissionsView#settings':{//load this view if user is administrator
//I need some condition for this
data: {
roles: ['admin']
},
templateUrl: '/permissionsView.html'
}
},
controller: 'settingsController'
});
$urlRouterProvider.otherwise( function($injector) {
var $state = $injector.get("$state");
$state.go('/home');
});
});
The view will be injected for each user (admin or anonymous). But we can manage which view. The best vay would be to use templateProvider.
Based on this Q & A:
Confusing $locationChangeSuccess and $stateChangeStart
I used the plunker from above source and adjusted it a bit
So, let's have these two targets (inside of index.html)
<div ui-view="onlyForAdmin"></div>
<div ui-view=""></div>
And a state public, which for Admin will reveal even content of the onlyForAdmin, with settings like this:
.state('public', {
url: "/public",
data: { isPublic: true },
views: {
'#' : {
templateUrl: 'tpl.html',
data: { isPublic: true },
},
'onlyForAdmin#' : {
templateProvider: ['$templateRequest','userService',
function($templateRequest,userService)
{
if(userService.isAdmin())
{
return $templateRequest("justForAdmin.html");
}
return ""; // other than admin will see nothing
}
]
}
}
})
the content of the justForAdmin.html (e.g. <h2>just for admin</h2>) will be injected only of some authorization service will find user as admin...
Check it here
Related
I have AngularJS(1.4.9) project that is build with usage of ui-router and have few states like this:
.state('overview', {
url: '/overview',
parent: 'dashboard',
templateUrl: 'views/dashboard/overview.html'
})
.state('settings', {
url: '/settings',
parent: 'dashboard',
templateUrl: 'settings/views/index.html',
controller: "SettingsCtrl"
})
Now I want to add want to integrate ng-admin(0.9.1) to it. I have tested it as standalone app with standalone html as described in docs and it works. But I couldn't find a way to add it to current project.
You will have to change your app parent state with ng-admin like below
// from
myApp.config(function ($stateProvider) {
$stateProvider.state('send-post', {
parent: 'main',
url: '/sendPost/:id',
params: { id: null },
controller: sendPostController,
controllerAs: 'controller',
template: sendPostControllerTemplate
});
});
// to
myApp.config(function ($stateProvider) {
$stateProvider.state('send-post', {
parent: 'ng-admin', // <= this has changed
url: '/sendPost/:id',
params: { id: null },
controller: sendPostController,
controllerAs: 'controller',
template: sendPostControllerTemplate
});
});
For reference please check updated document ng-admin
My 'just works' solution is to add to main project states external link of ng-admin:
.state('database', {
onEnter: function($window) {
$window.open('/ng-admin/ng-admin.html', '_self');
}
})
I'm working on a web-app with ionic tabs.
On one of my pages i implemented the ion-tree-list from GitHub (Ion-tree-list from GitHub) as treeview.
In services.js i got the source for my tree:
.factory('VerificationTree', function () {
var verificationTree = [
{
id: 'operator',
name: 'Operator'
},
{
id: 'type_of_test',
name: 'Type of test',
},
{
id: 'system',
name: 'System'
},
{
id: 'component',
name: 'Component'
},
{
id: 'component_group',
name: 'Component group',
tree: [
{
id: 'component2',
name: 'Component'
}
]
}
];
return {
all: function () {
return verificationTree;
}
};
})
When i click on one the tree items - p.e. "type_of_test" - i want to open another page.
In controller.js i have defined a controller for the page with the tree.
Inside the Controller is a function, which opens the page by window.open.
.controller('VerificationCtrl', function ($scope, VerificationTree) {
$scope.verificationTree = VerificationTree.all();
$scope.$on('$ionTreeList:ItemClicked', function (event, item) {
if (item.id == 'type_of_test') {
window.open('templates/type-of-test.html');
};
//if (item.id == 'component2') {
// alert('The ion-tree-list item component2 was clicked');
//};
})
})
The page opens in a new tab but the layout is not okay anymore.
Is there a possibility to combine the tree with a navigation view like the following?
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab', {
url: '/tab',
abstract: true,
templateUrl: 'templates/tabs.html'
});
Or another example:
var app = angular.module('myApp', ['ionic']);
app.config(function($stateProvider) {
$stateProvider
.state('index', {
url: '/',
templateUrl: 'home.html'
})
.state('music', {
url: '/music',
templateUrl: 'music.html'
});
});
Not sure if I get you right, but what about doing a simple state change, when an item is clicked?
$stateProvider.state("tabs", {
url: "/tabs",
templateUrl: "templates/TabView.html",
abstract: true,
controller: "TabCtrl as tab"
}).state("tabs.home", {
url: "/overview",
templateUrl: "templates/HomeView.html",
controller: "HomeCtrl"
}).state("tabs.detail", {
url: "/detail/:id",
templateUrl: "templates/DetailView.html",
controller: "DetailCtrl"
})
On HomeView, add your TreeList and define the click function as follows:
.controller('HomeCtrl', function ($scope, $state, VerificationTree) {
$scope.verificationTree = VerificationTree.all();
$scope.$on('$ionTreeList:ItemClicked', function (event, item) {
if (item.id == 'type_of_test') {
$state.go('tabs.detail')
};
})
})
If you want to pass in the item as a param, you can define this in the stateProvider configuration as well. Check the UI-Router docs for additional information. https://github.com/angular-ui/ui-router/wiki/URL-Routing
Also if you don't want to transition to another tab when clicking an item, but stay in the tab and just change the state, you might try nested states. You could define your states like this:
$stateProvider.state("tabs", {
url: "/tabs",
templateUrl: "templates/TabView.html",
abstract: true,
controller: "TabCtrl as tab"
}).state("tabs.home", {
url: "/home",
abstract: true,
views: {
"home": {
template: "<ion-nav-view></ion-nav-view>"
}
}
}).state("tabs.home.overview", {
url: "/overview",
templateUrl: "templates/HomeView.html",
controller: "HomeCtrl"
}).state("tabs.home.detail", {
url: "/detail/:id",
templateUrl: "templates/DetailView.html",
controller: "DetailCtrl"
})
And then transition from Overview to Detail like this:
$state.go('tabs.home.detail')
I'm using ui-router to setup my routes like below:
$stateProvider
.state('root', {
url: "",
templateUrl: 'path/login.html',
controller: 'LoginController'
})
.state('basket', {
url: "/basket",
views: {
'': {
templateUrl: 'path/basket.html',
controller: 'BasketController'
},
'address#basket': {
templateUrl: 'path/address.html',
controller: 'AddressController'
},
'confirmation#basket': {
templateUrl: 'path/confirmation.html',
controller: 'ConfirmationController'
},
'payment#basket': {
templateUrl: 'path/payment.html',
controller: 'PaymentController'
}
}
});
In my basket.html I'm using mgo-angular-wizard to create a "step bar".
<wizard on-finish="finishedWizard()">
<wz-step title="Address">
<div ui-view="address"></div>
</wz-step>
<wz-step title="Confirmation">
<div ui-view="confirmation"></div>
</wz-step>
<wz-step title="Payment">
<div ui-view="payment"></div>
</wz-step>
So far it's working fine. But I need to create buttons that will also navigate between those pages. For example: Address page needs a button which is going to call Confirmation page.
I saw some pages in the web which were using ui-sref="$state.go('basket.address')" but, since I'm not using this structure (with dot), I don't know how to do it (using confirmation#basket isn't working).
I'm new to Angular and learning on the flow. Thanks in advance.
In my opinion you don't need anything else except Ui.Router, because is angular you can navigate between states in a non-stateless way (saving something in services for example).
A good thing could be having nested states...
steps:
Create the parent abstract state: Basket that allows child states to inherit views, resolves, url-fragments.
Create child states: address; confirmation; payment
angular
.module('checkout', ['ui.router'])
.config(function($stateProvider) {
var basket = {
name: 'basket',
url: '/basket/',
abstract: true,
views: {
"main": {
template: "<h1>Bye</h1>",
controller: function() { alert('bye'); }
},
"sidebar": {
template: "<ul><li>LINK</li></ul>",
controller: function() { console.log('sidebar view'); }
}
}
};
var address = {
name: 'basket.address',
url: ''
};
var confirmation = {
name: 'basket.confirmation',
url: 'confirmation/',
views: {
"main#": {
template: "<h1>ss</h1>",
controller: function() { alert('ss'); }
}
}
};
var payment = {
name: 'basket.payment',
url: 'payment/',
views: {
"main#": {
template: "<h1>zz</h1>",
controller: function() { alert('zz'); }
}
}
};
$stateProvider
.state(basket)
.state(address)
.state(confirmation)
.state(payment)
;
})
;
<section ui-view="main"></section>
<aside ui-view="sidebar"></aside>
<ul>
<li><a ui-sref="basket.address">Address</a></li>
<li><a ui-sref="basket.confirmation">Confirmation</a></li>
<li><a ui-sref="basket.payment">Payment</a></li>
</ul>
Create a service via angular.module('checkout').value that stores all checkout progresses.
By the way, if you need to keep all views and states in one you probably need this Ui.Router.extra called STICKY STATES, the link provides many examples...
Hope it helps!
I'm currently working on an app where I have multiple nested views, they sort of look like this:
- ui-view
- ui-view="header"
- ui-view="nav"
- ui-view="body"
My states are defined as follows:
.state('index', {
url: '', // default route
templateUrl: 'welcome.html'
})
.state('app', {
abstract: true,
templateUrl: 'app.template.html' // This template contains the 3 different ui-views
})
// I'm using a different state here so I can set the navigation and header by default
.state('in-app', {
parent: 'app',
abstract: true,
views: {
'nav#app': { '...' },
'header#app': { '...' }
}
})
// In-app routes
.state('dashboard', {
parent: 'in-app',
url: '/app/dashboard'
views: {
'body#app': { '...' }
}
})
.state('users', {
parent: 'in-app',
url: '/app/users'
views: {
'body#app': { '...' }
}
})
.state('settings', {
parent: 'in-app',
url: '/app/settings'
views: {
'body#app': { '...' }
}
})
At the moment this works great, but for the in-app routes I would like to define a title that is displayed in the header#app view.
What would be the best way to do this? At the moment I can only think of either setting a variable on the $rootScope, or sending out an event. But for both of those I would need a controller.
Is there a way I could do this directly from my routes config?
The sample applicaiton of the UI-Router, uses this code:
ui-router / sample / app / app.js
.run(
[ '$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.For example,
// <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
// to active whenever 'contacts.list' or one of its decendents is active.
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])
And that means, that with data : {} feature:
Attach Custom Data to State Objects
You can attach custom data to the state object (we recommend using a data property to avoid conflicts).
// Example shows an object-based state and a string-based state
var contacts = {
name: 'contacts',
templateUrl: 'contacts.html',
data: {
customData1: 5,
customData2: "blue"
}
}
we can do this:
.state('in-app', {
parent: 'app',
abstract: true,
views: {
'nav#app': { '...' },
'header#app': { '...' }
}
data: { title : "my title" },
})
And use it in some template like:
<div>{{$state.current.data.title}}</div>
Some summary.
We can place state and params into $rootScope, so we can access it without any controller anyhwere.
We can declare some more custom stuff via data and use it as a title ... anyhwere
I have now tried whole day and i cant figure out how i shall solve this issue.
My goal is to display a sidebar thats get filled with data based on what state it is.
Lets say i am on the home page i may like this items:
Home item 1
Home item 2
And if i am on the about page i want this items:
About me
About my dog
I would like the data to get fetched from a service that returns data to the view based on what state it is.
I have tried to use ui.router's resolve function but i can't get the correct structure in my head to make it work.
Created a plunkr that shows how i mean but without the solution, what are the best practices and preferred ways when creating a dynamic sidebar with Angular and ui.router?
myapp.config(function($stateProvider) {
$stateProvider
.state('home', {
url: "",
views: {
"content": {
template: "This is the home.content"
},
"sidebar": {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl'
}
}
})
.state('about', {
url: "/about",
views: {
"content": {
template: "This is the about.content"
},
"sidebar": {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl'
}
}
})
})
Plunkr here
Edit
Can't figure out what i am doing wrong, no view is shown with this code:
app.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to root
$urlRouterProvider.otherwise("/");
$stateProvider
.state('root', {
url: '',
'abstract': true,
views: {
'sidebar#': {
templateUrl: '/App/Main/views/shared/sidebars/sidebar.cshtml',
controller: 'app.controllers.views.shared.sidebars.sidebar',
resolve: {
sidebarData: function (sidebarService) {
return sidebarService.getActions();
}
}
}
},
})
.state('root.home', {
url: "/",
views: {
'': {
templateUrl: '/App/Main/views/home/home.cshtml',
controller: 'app.controllers.views.home'
},
'content#root.home': {
templateUrl: '/App/Main/views/home/home-content.cshtml',
controller: 'app.controllers.views.home'
}
}
})
.state('root.about', {
url: "/customers",
views: {
'': {
templateUrl: '/App/Main/views/customers/customers.cshtml',
controller: 'app.controllers.views.customers'
},
'content#root.about': {
templateUrl: '/App/Main/views/customers/customers-content.cshtml',
controller: 'app.controllers.views.customers'
}
}
});
}]);
and here is how my home and customer views look like:
<div data-ui-view="sidebar">
Loading sidebar view.
</div>
<div data-ui-view="content">
Loading content view.
</div>
you could try to implement named and also an abstract view for your sidebar to reuse it among your other routes, also you can use the resolve parameter an return whatever dynamic data you need (from a service, resource, whatever), it could be something like this:
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider) {
$stateProvider
.state('root',{
url: '',
abstract: true,
views: {
'sidebar#': {
templateUrl: 'sidebar.html',
controller: 'sidebarCtrl',
resolve:{
sidebarData:function(){
return [{
state: '/stateHere',
text: 'Need'
}, {
state: '/stateHere',
text: 'to'
}, {
state: '/stateHere',
text: 'fill'
}, {
state: '/stateHere',
text: 'this'
}, {
state: '/stateHere',
text: 'with'
}, {
state: '/stateHere',
text: 'dynamic'
}, {
state: '/stateHere',
text: 'data'
}];
}
}
}
})
.state('root.home', {
url: "/",
views: {
'content#': {
template: "This is the home.content"
}
}
})
.state('root.about', {
url: "/about",
views: {
'content#': {
template: "This is the about.content"
}
}
})
});
myapp.controller('sidebarCtrl', ['$scope','sidebarData'
function($scope,sidebarData) {
$scope.sidebarData= sidebarData,
}
]);
EDIT:
check this working example:
http://plnkr.co/edit/Nqwlkq1vGh5VTBid4sMv?p=preview