I am building a application which has different modules. and two modules can have same pages. So based on url i am making the appropriate ajax call to load data. So I am tring to setup my states in below way:
$stateProvider.state('login', {
url: '/login',
templateUrl: 'login.html',
controller: 'LoginController as LoginController'
}).state('logout', {
url: '/logout',
templateUrl: '',
controller: 'LogoutController as LogoutController'
}).state('module', {
url: '/:module',
params: {
module: DataService.getCurrentModule()
}
}).state('module.cover', {
url: '/cover',
templateUrl: 'cover.html',
params: {
module: 'leads'
}
}).state('module.leads', {
url: '/leads',
templateUrl: 'leads.html',
controller: 'LeadsController as ctrl',
abstract: true
})
Given that at the time of login I will fetch all modules and save it in DataService, which is happening. Then after login two things will be done. One navigation urls which i have formatted in below way:
<a href={'#/'+ module.code +"/" + (menu.type|| menu)}>
<i className={classes}></i> <span >{menu.name || menu }</span>
</a>
which is setting the correct url, and second in app.js in "run" I am checking if login is done them I am doing :
$location.path(DataService.getCurrentModule() + "/" + (home.type || home) );
which is also happening, but issue is desired controller and html page is not being loaded. Am I missing something here. Or should I have done things little differently?
Thanks for help in advance.
Avoid href when working with ui.router. To navigate to the required states use:
In HTML: ui-sref="myStateName({param1: 1, param2: 2})"
In Javascript inject the service $state and do: $state.go('myStateName', {param1: 1, param2: 2});
In your case, lets assume that there are 2 modules in an array in the $scope:
$scope.myModules = [{code: 'modA'},{code: 'modB'}];
Now in the HTML, to go to the module.cover state you would do:
<a ui-sref="module.cover({module: myModules[0].code})">My Link</a>
If you want to do it for all modules, put it inside an ng-repeat:
<a ng-repeat="mod in modules" ui-sref="module.cover({module: mod.code})">My Link</a>
Also, for state configuration, consider:
ALL STATES NEED A TEMPLATE: even if they are abstract states, they require a template to work properly. If the parent state doesn't have a template, not even one of its childs is gonna show. In this case, the state module doesn't have a template, so it will never work. define a template for it as simple as template: '<div ui-view></div>'
When you define a parameter in the URL, there's no need to define it again in with a params property. That is used only when you need parameters that you don't want to show in the URL
Related
I am using angular ui-router to retrieve a page, but on the documentation, it only shows how to pass in parameters in the url, not the template url.
Is there anyway where it can be passed in the templateUrl?
Below is my code
<div ui-view></div>
<!-- We'll also add some navigation: -->
<a ui-sref="contacts({contactId: 1})">State 1</a>
And the angular config:
myApp.config(function($stateProvider) {
$stateProvider
.state('contacts', {
url: "/states?myParam={contactId}",
templateUrl: "states1.html"
});
});
templateUrl can be a function that takes stateParams parameter:
.state('contacts', {
url: "/states?myParam={contactId}",
templateUrl: function(stateParams) {
return "states" + stateParams.contactId + ".html";
}
})
Why would you want it?
The only reason I can think about is that you want to customize the template on the server-side based on the passed arguments. Which goes against the idea of templates :)
Templates should be generic enough.
Edit: Here is the complete code at Plunker. Though I can not c anything in execution but same code working at local. However gives a console error though
It all works perfect. But due to :id in /news/:id/, i am getting jquery/angular errors in console which can not be tracked anywhere in my code
I can not c What i am doing wrong.
Edit: Solved plunker https://plnkr.co/edit/FWcuBgGpVdMj3CroFrYJ
First of all you are trying to use ui-router but you're including ngRoute script in your plunker. Change it to
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
Then everything should work fine!
I suggest you a few changes...
1. Use ui-sref instead of href because it's much easier to define
ui-sref="post({id:1})" which turns into href="#/news/1"
If you would like to change url some day, then you will have to just change your route file, not each href.
$stateProvider
.state('post', {
url: "news/:id"
or
$stateProvider
.state('post', {
url: "archive/:id"
or
$stateProvider
.state('post', {
url: "whatever/:id"
2. Use abstract state
In your example it's a way better to define abstract state which holds header, content and footer - it's a typical use case.
ui-router
Abstract States
An abstract state can have child states but can not get activated
itself. An 'abstract' state is simply a state that can't be
transitioned to. It is activated implicitly when one of its
descendants are activated.
Some examples of how you might use an abstract state are:
To prepend a url to all child state urls. To insert a template with
its own ui-view(s) that its child states will populate. Optionally
assign a controller to the template. The controller must pair to a
template. Additionally, inherit $scope objects down to children, just
understand that this happens via the view hierarchy, not the state
hierarchy. To provide resolved dependencies via resolve for use by
child states. To provide inherited custom data via data for use by
child states or an event listener. To run an onEnter or onExit
function that may modify the application in someway. Any combination
of the above. Remember: Abstract states still need their own
for their children to plug into. So if you are using an
abstract state just to prepend a url, set resolves/data, or run an
onEnter/Exit function, then you'll additionally need to set template:
"".
Here's a plunker which shows how I would do it.
https://plnkr.co/edit/5FvJaelyxdl5MuALt5VY?p=preview
Hope it helps.
Look at the documentation for ui router named views,
You can use following syntax for using multiple views
$stateProvider
.state('state',{
url: '',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'headerCtrl'
},
'content': {
template: '<div ui-view=" "></div>', //<-- child templates loaded to here
},
'footer': {
templateUrl: 'views/footer.html',
controller: 'footerCtrl'
}
}
})
.state('state.post', {
url: 'news/:id/:KeyWords'
templateUrl: 'views/post.html' //<-- This goes into content's ui-view
});
I'm guessing you want to keep the header and footer and change content views.
You can achieve this by making this state as parent to all other states
suppose
.state('main',{
abstract: true,
views: {
'header': ... ,
'content': {
template: '<ui-view></ui-view>',
}
'footer': ...
}
})
then all the child views will load their views in the ,
ex: in main.child etc, your template will load in the content's <ui-view></ui-view> tag
If you need to use a custom template depending on keywords you can do the following:
.config(['$routeProvider',
function($routeProvider, $routeParams) {
$routeProvider
.when('/news/:id/:keyWords', {
template: '<div ng-include="url"></div>',
controller: "exampleController"
})
then in the exampleController
function($routeParams, $scope) {
$scope.url = $routeParams.keyWords;
}
What I am trying to achieve:
In the template of the main state (/model), I have links to a child state phase (/phase), but I also want to be able to link from the main state directly to a given child state of a given phase (/phase/1/task1).
My config:
$stateProvider
.state('model', {
url: '/model',
views: {
'': {
templateUrl: 'model.tpl.html',
controller: 'modelController'
}
},
})
.state('model.phase', {
url: '/phase/{phaseId:int}',
views: {
'#main.model': {
templateUrl: 'model.phase.tpl.html',
controller: 'phaseController'
}
}
})
.state('model.phase.task', {
url: '/task/{taskId:int}',
controller: 'taskController'
})
};
In model.tpl.html I list out all the phases with general information, and for each phase, I list out tasks from that phase like so:
<div ng-repeat="task in tasks | filter:{primary:true, knowledgeAreaId:area.id, phaseId:phase.id}:true" class="task" ng-class="{ active: task.active }">
<div class="task__content" ui.sref="LINKHERE"></div>
</div>
And it is here I want to build a link that goes directly from the main model page to a given child page for a given phase.
What I have tried in model.tpl.html is to add ui.sref like so
<a ui.sref=".phase({ phaseId: task.phaseId}.task({ taskId: task.taskId})">
But that gives me an error, so it seems that you cant 'chain' states with properties.
If I only wanted to link to a phase, then the following works just fine:
<a ui.sref=".phase({ phaseId: task.phaseId}">
Is there any 'correct' soloution in ui.router for this scenario, or do I have to manually build a string for the href value. That way I change the url to the route I want instead of changing to a given state. It seems wrong to do that, when everything else works by switching states.
Something like:
http://localhost/#/model/phase/1/task1
This should work:
ui.sref=".phase.task({ phaseId: task.phaseId, taskId: task.taskId })"
I'm having trouble with a simple ui-router sample I have set up. I have a company page, whose default sub-state should show CompanyProfile, but it defaults to nothing until I click profile. Once I clicked employees, I have to click profile twice to get it to show again. Ideally I want ui-sref="company()" and ui-sref="company.profile()" to display the same screens. It seems like I'm missing something small..
Here's the plnkr:
http://plnkr.co/edit/A3LHGqQIuRlK1QdjuzrP?p=preview
HTML:
<a ui-sref="company()">company</a>
| <a ui-sref="company.profile()">profile</a>
| <a ui-sref="company.employees()">employees</a>
JS:
$stateProvider
.state('company', {
url: '/',
templateUrl: 'company.html',
controller: 'CompanyCtrl as CompanyCtrl'
})
.state('company.profile', {
url: '',
templateUrl: 'profile.html',
controller: 'CompanyProfileCtrl as CompanyProfileCtrl'
})
.state('company.employees', {
url: '/employees',
templateUrl: 'employees.html',
controller: 'CompanyEmployeesCtrl as CompanyEmployeesCtrl'
});
btw, I'm writing everything as components and decided to define the routes in each component, so you'll find the 3 state definitions in the 3 controllers. I'm not entirely sure this is the best approach or not yet.
The default state is entirely dependent on how you call $urlRouterProvider.otherwise(), passing it a url transitions the application to the particular url, wherein ui-router detects and looks for the very first state it sees.
In your main.js configuration, defines the / url as the default url for the application, which is technically the company state's url and is the very first state in the chain of parent states and children states, making it the default state. This in fact, is also the resulting url for the company.profile state that you wanted your application to default to.
To solve this problem, depends on the use cases for your application.
Use case: If your application defines the company state as a non-navigational state, then setting it to an abstract state solves the problem.
DEMO
CompanyCtrl.js
$stateProvider
.state('company', {
abstract: true,
url: '/',
templateUrl: 'company.html',
controller: 'CompanyCtrl as CompanyCtrl'
});
Use case: If the company state is nagivational, then simply remove the url definition in the company state and change the url defintion for the company.profile state to '/'. The only caveat for this solution would be the loss of the href attribute to be applied for for any anchor tags defined with the ui-sref="company" state which also implies the application of the text cursor. To mitigate this problem you might as well define all anchor tags with ui-sref attribute with a pointer cursor.
DEMO
CompanyCtrl.js
$stateProvider
.state('company', {
templateUrl: 'company.html',
controller: 'CompanyCtrl as CompanyCtrl'
});
CompanyProfileCtrl.js
$stateProvider
.state('company.profile', {
url: '/',
templateUrl: 'profile.html',
controller: 'CompanyProfileCtrl as CompanyProfileCtrl'
})
style.css
a[ui-sref] {
cursor: pointer;
}
UPDATE:
Use Case: The same with use case #2 but making the company state an abstract state.
DEMO
CompanyCtrl.js
$stateProvider
.state('company', {
abstract: true,
templateUrl: 'company.html',
controller: 'CompanyCtrl as CompanyCtrl'
});
I have a request to add in another URL parameter that directs to a state that I already have set up. For efficiency purposes, I'm trying to see if I can add multiple URLs to point to the same state, or should I just use the $UrlRouterProvider.when() method to re-direct to that state in this new case.
Ex. this is what already exists
.state('site.link1',
{
url: '/link1',
templateUrl: '/views/link1.html',
controller: 'link1Ctrl'
})
and the request is to add www.site.com/newlink that points to the link1 page. Is there something like this;
.state('site.link1',
{
url: '/link1, /newlink',
...
Try using the Regex and a parameter in the url. It is not optimal but works.
.state('site.link1',
{
url: '/{path:link1|newlink}',
templateUrl: '/views/link1.html',
controller: 'link1Ctrl'
})
More information on regex in Urls.
To generate links with ui-sref pass the same parameter with the state name as a function
<a ui-sref="site.link1({path:'link1'})" >site link 1</a>
<a ui-sref="site.link1({path:'newlink'})">site new link</a>
You use params:
https://github.com/angular-ui/ui-router/wiki/URL-Routing
.state('site.link',
{
url: '/{link}'
..
}
so when you use the same state like this
$state.go('site.link', {link: 'link1'})
$state.go('site.link', {link: 'link2'})
you can used when() function
.state('site.link1',
{
url: '/link1',
templateUrl: '/views/link1.html',
controller: 'link1Ctrl'
})
then on root config
angular.module('myApp', [...])
.config(function ($urlRouterProvider) {
$urlRouterProvider.when(/newlink/, ['$state','$match', function ($state, $match) {
$state.go('site.link1');
}]);
});
I found this approach to be quite simple and clean: create two equal states, just changing the url property
//Both root and login are the same, but with different url's.
var rootConfig = {
url: '/',
templateUrl:'html/authentication/login.html',
controller: 'authCtrl',
data: {
requireLogin: false
}
}
var loginConfig = Object.create(rootConfig)
loginConfig.url = '/login'
$stateProvider
.state('root', rootConfig)
.state('login', loginConfig)
I had almost the same problem, only with another constraint - I didn't want to use a redirect, since I wanted the url in the browser to stay the same, but display the same state.
This was because I wanted the chrome saved passwords to work for users that already saved the previous url.
In my case I wanted these two urls :
/gilly and
/new/gilly
to both point to the same state.
I solved this by having one state defined for /gilly, and for the second url, I defined an abstract state called /new.
This should be set up like this :
$stateProvider.state('new', {
abstract: true,
url: '/new'
template: '',
controller: function() { }
}).state('gilly', {
url: '/gilly',
template: 'gilly.html',
controller: 'GillyController'
}).state('new.gilly', {
url: '/gilly', // don't add the '/new' prefix here!
template: 'gilly.html',
controller: 'GillyController'
});