I have an older app (Angular 1.5.3) and I would like to take advantage of angular-ui-router and components for a few of my pages.
Is it possible to use the old routeProvider in conjunction with the stateProvider that ui-router provides?
IE I would like to transition a few pages to use components here and there as I get time, while leaving the rest.
I am pretty sure using ng-view and ui-view I am having a hard time getting that to work (probably cause you are not supposed to do that). Does that mean I have to transition my entire project from routeProvider to stateProvider all at once?
Is it possible to use the old routeProvider in conjunction with the
stateProvider that ui-router provides?
Short answer
No. Similar structure but different syntax
Long answer
No, but ... You can easily convert ng-view to ui-view a.e. from $routeProvider to $stateProvider.
Consider example ng-view:
$routeProvider
.when('/Book/Add', {
template: '<div class="box" ng-class="classname">Add</div>',
controller: function($scope) {$scope.classname="add"}
})
.when('/Book/Error', {
templateUrl : 'error.html',
controller: 'ErrorController'
})
.otherwise({redirectTo: '/Book/Error'});
Consider example ui-view:
$stateProvider
.state('book', {
url: '/Book',
abstract: true,
templateUrl: 'views/Book.html'
})
.state('book.add', {
url: '/inbox',
template: '<div class="box" ng-class="classname">Add</div>',
controller: function($scope) {$scope.classname="add"}
})
.state('book.error', {
url: '/Error',
templateUrl : 'error.html',
controller: 'ErrorController'
})
$urlRouterProvider.otherwise(function ($injector, $location) {
return '/Book/Error';
});
Keep in mind that routing syntax will change too.
For example:
if ($state.current.name === 'login') {
$state.go('book.add', {});
}
Instead Add we will write <a ui-sref="book.add">Add</a>
And so on ......
As you can see, the syntax is a bit similar. I'm sure you will find a lot of references about power of $stateProvider. For example https://stackoverflow.com/a/21024270/1631379
Hope I answered on your question
Related
I'm using $state.go() other places in my app but it's not working in a simple function and I can't work out why. It's not showing an error or showing the right state called in $state.go().
I've tested it with $stateChangeStart, $stateChangeError, $viewContentLoading & $stateChangeSuccess. It reaches all the correct stages: $stateChangeStart, $viewContentLoading then $stateChangeSuccess with the right content in each however, doesn't load the page.
I originally tried it with ui-sref and that didn't work so I tried it with $state.go() and now that isn't working I'm really confused. I can't see any difference between this code and what I have in other places where $state.go() works.
HTML link:
<li ng-repeat="thing in things" class="item" ng-click="showThingDetails()">
Code in controller:
$scope.showThingDetails = function () {
$state.go('thingDetails');}
Code in state:
.state('thingDetails', {
url: '/thingDetails',
views: {
'menuContent': {
templateUrl: 'templates/thingDetails.html',
controller: 'thingDetails'
}
}
})
thingDetails.html
<ion-view view-title="Thing Details">
<ion-content>
<h1>{{title}}</h1>
</ion-content>
</ion-view>
thingDetails.js
angular.module('controllers')
.controller('thingDetails', function($scope, $stateParams) {
$scope.title = "thing";
});
I've just found the answer to my own problem. I had copied the state from another place in my app and defining the templateUrl and controller in the views object was causing it to play up. I took templateUrl and controller out of the views object and it worked.
Code in state is now as below and works:
.state('sweepstakeDetails', {
url: '/sweepstakeDetails',
templateUrl: 'templates/sweepstakeDetails.html',
controller: 'sweepstakeDetails'
})
If anyone can add a more detailed answer as to why then I'd appreciate it.
Do you have an ui-view called 'menuContent' where the view should be loaded?
Else you could try to use this state without the menuContent views
.state('thingDetails', {
url: '/thingDetails',
templateUrl: 'templates/thingDetails.html',
controller: 'thingDetails'
})
I am in trouble with a specific requirement here for our Application.
We a are setting-up an angular application inside a pre-existent Rails Application.
As a full refactor of our code-base is currently out-of-the-question we are dealing with some hard customization and hackings on both sides to allow a incremental introduction of Angular.
What we need for now is a way to tell the ui-router to bind only to the links we have the ng-sref attribute and do not bother with all the regular href links within our app.
Is there a way to achieve this behavior ?
Below is my current code:
angular.module('app').config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider.state('test', {
url: "/1/test",
template: 'test'
});
$locationProvider.html5Mode({
enabled: true,
requireBase: false,
rewriteLinks: false
})
}
)
With this approach, all links, even those ones without any route setup for it, and without a ui-sref attribute, are being watched by angular routing service and prevented to work like it`s previous behaviour. I do not want to add every route of our huge app to the angular routing setup (this is a terrible idea) because most of theses links are to non-angular pages. I just want angular routing service to ignore this links, or these locations that are not defined. Maybe a setting for the $location service to let those guys fallow along with its previous behaviour (ajax requests, regular requests, or Turbolinks requests). I am pretty sure this is something really trivial that I might be missing the point.
What happens when I click on theses links is that the window location changes, but nothing happen. The browser request is not triggered.
Thank you very much in advance.
###################################################################################
# My suggestion is use the ui-router to route to specific pages as shown
# in the example below.
# <a ui-sref="/login">
# <a ui-sref="/logout">
# <a ui-sref="/signup">
# <a ui-sref="/profile">
# 'ui-sref' will take you to the specific pages.
# You can also use '$state.go' to take you to specific pages as shown below.
# $state.go('authentication.login');
# $state.go('authentication.logout');
# $state.go('authentication.signup');
# $state.go('authentication.profile');
###################################################################################
(function() {
'use strict';
angular
.module('app.foo.authentication')
.config(moduleConfig);
/* #ngInject */
function moduleConfig($translatePartialLoaderProvider, $stateProvider) {
$translatePartialLoaderProvider.addPart('app/foo/authentication');
$stateProvider
.state('authentication.login', {
url: '/login',
templateUrl: 'app/foo/authentication/login/login.tmpl.html',
controller: 'LoginController',
controllerAs: 'vm'
})
.state('authentication.logout', {
url: '/logout',
templateUrl: 'app/foo/authentication/logout/logout.tmpl.html',
controller: 'LogoutController',
controllerAs: 'vm'
})
.state('authentication.signup', {
url: '/signup',
templateUrl: 'app/foo/authentication/signup/signup.tmpl.html',
controller: 'SignupController',
controllerAs: 'vm'
})
.state('authentication.profile', {
url: '/profile',
templateUrl: 'app/foo/authentication/profile/profile.tmpl.html',
controller: 'ProfileController',
controllerAs: 'vm'
});
}
})();
I had my old code which worked just fine:
$urlRouterProvider.otherwise('/next');
$stateProvider
.state('next', {
url: '/next',
templateUrl: 'partials/next.html',
controller: function($scope, $state){
}
});
But then I got the brilliant idea to better organize my code, so I ended up with:
$urlRouterProvider.otherwise('/next');
$stateProvider
.state('app', {
abstract: true
})
.state('app.next', {
url: '/next',
templateUrl: 'partials/next.html',
controller: function($scope, $state){
}
});
Which is basically the same thing, but uses a dot notation, and an abstract state (not that it matters; even if I remove the abstract state, it still won't work).
The app does take me to /next, however the page is blank (only the base template is shown, not the content of /partials/next.html. The request for it is made, but it's simply not shown.
The relevant HTML code is just:
<div class="container" ui-view>
</div>
I was (somewhat) following the tutorial from https://scotch.io/tutorials/angular-routing-using-ui-router if that helps anything.
What am I doing wrong?
add in abstract state property:
template : '<div ui-view></div>'
It should looks like:
$urlRouterProvider.otherwise('/next');
$stateProvider
.state('app', {
abstract: true,
template : '<div ui-view></div>'
})
.state('app.next', {
url: '/next',
templateUrl: 'partials/next.html',
controller: function($scope, $state){
}
});
of course you can use templateUrl intead template. It is also very usefull to use layout templates in abstract state.
edit to answer comment:
Can I ask why this is necessary?
It is necessary, because angular run first abstract state, and its template. And then your actual state. But angular needs to know where put content of normal state template in parent abstract state template.
This template : '<div ui-view></div>' means that abstract state has simple template with only position of normal state template.
I have an Angular SPA that presents a variety of recommendation lists, and a Google Map of locations, based on different cuts of some restaurant data (see m.amsterdamfoodie.nl). I want each of these lists to have their own URL. In order for Google to crawl the different lists I use <a> tags for the offcanvas navigation.
At present the <a> tag causes a view refresh, which is very noticeable with the map.
I can prevent this using ng-click and $event.preventDefault() (see code snippets below), but then I need to implement a means of updating the browser URL.
But in trying Angular's $state or the browser's history.pushstate, I end up triggering state changes and the view refresh...!
My question is therefore how can I update a model and the URL, but without refreshing the view? (See also Angular/UI-Router - How Can I Update The URL Without Refreshing Everything?)
I have experimented with a lot of approaches and currently have this html
Budget
In the controller:
this.action = ($event) ->
$event.preventDefault()
params = $event.target.href.match(/criteria\/(.*)\/(.*)$/)
# seems to cause a view refresh
# history.pushState({}, "page 2", "criteria/"+params[1]+"/"+params[2]);
# seems to cause a view refresh
# $state.transitionTo 'criteria', {criteria:params[1], q:params[2]}, {inherit:false}
updateModel(...)
And, what is I think is happening is that I am triggering the $stateProvider code:
angular.module 'afmnewApp'
.config ($stateProvider) ->
$stateProvider
.state 'main',
url: '/'
templateUrl: 'app/main/main.html'
controller: 'MainCtrl'
controllerAs: 'main'
.state 'criteria',
url: '/criteria/:criteria/:q'
templateUrl: 'app/main/main.html'
controller: 'MainCtrl'
controllerAs: 'main'
One possible clue is that with the code below if I load e.g. http://afmnew.herokuapp.com/criteria/cuisine/italian then the view refreshes as you navigate, whereas if I load http://afmnew.herokuapp.com/ there are no refreshes, but no URL updates instead. I don't understand why that is happening at all.
This is an example of the way to go if I understand correctly:
$state.go('my.state', {id:data.id}, {notify:false, reload:false});
//And to remove the id from the url:
$state.go('my.state', {id:undefined}, {notify:false, reload:false});
From user l-liava-l in the issue https://github.com/angular-ui/ui-router/issues/64
You can check the $state API here: http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state
Based on our previous discussions, I want to give you some idea, how to use UI-Router here. I believe, I understand your challenge properly... There is a working example. If this not fully suites, please take it as some inspiration
DISCLAIMER: With a plunker, I was not able to achieve this: http://m.amsterdamfoodie.nl/, but the principle should be in that example similar
So, there is a state definition (we have only two states)
$stateProvider
.state('main', {
url: '/',
views: {
'#' : {
templateUrl: 'tpl.layout.html',
controller: 'MainCtrl',
},
'right#main' : { templateUrl: 'tpl.right.html',},
'map#main' : {
templateUrl: 'tpl.map.html',
controller: 'MapCtrl',
},
'list#main' : {
templateUrl: 'tpl.list.html',
controller: 'ListCtrl',
},
},
})
.state('main.criteria', {
url: '^/criteria/:criteria/:value',
views: {
'map' : {
templateUrl: 'tpl.map.html',
controller: 'MapCtrl',
},
'list' : {
templateUrl: 'tpl.list.html',
controller: 'ListCtrl',
},
},
})
}];
This would be our main tpl.layout.html
<div>
<section class="main">
<section class="map">
<div ui-view="map"></div>
</section>
<section class="list">
<div ui-view="list"></div>
</section>
</section>
<section class="right">
<div ui-view="right"></div>
</section>
</div>
As we can see, the main state does target these nested views of the main state: 'viewName#main', e.g. 'right#main'
Also the subview, main.criteria does inject into layout views.
Its url starts with a sign ^ (url : '^/criteria/:criteria/:value'), which allows to have / slash for main and not doubled slash for child
And also there are controllers, they are here a bit naive, but they should show, that on the background could be real data load (based on criteria).
The most important stuff here is, that the PARENT MainCtrl creates the $scope.Model = {}. This property will be (thanks to inheritance) shared among parent and children. That's why this all will work:
app.controller('MainCtrl', function($scope)
{
$scope.Model = {};
$scope.Model.data = ['Rest1', 'Rest2', 'Rest3', 'Rest4', 'Rest5'];
$scope.Model.randOrd = function (){ return (Math.round(Math.random())-0.5); };
})
.controller('ListCtrl', function($scope, $stateParams)
{
$scope.Model.list = []
$scope.Model.data
.sort( $scope.Model.randOrd )
.forEach(function(i) {$scope.Model.list.push(i + " - " + $stateParams.value || "root")})
$scope.Model.selected = $scope.Model.list[0];
$scope.Model.select = function(index){
$scope.Model.selected = $scope.Model.list[index];
}
})
This should get some idea how we can use the features provided for us by UI-Router:
Absolute Routes (^)
Scope Inheritance by View Hierarchy Only
View Names - Relative vs. Absolute Names
Check the above extract here, in the working example
Extend: new plunker here
If we do not want to have map view to be recreated, we can just omit that form the child state def:
.state('main.criteria', {
url: '^/criteria/:criteria/:value',
views: {
// 'map' : {
// templateUrl: 'tpl.map.html',
// controller: 'MapCtrl',
//},
'list' : {
templateUrl: 'tpl.list.html',
controller: 'ListCtrl',
},
},
})
Now our map VIEW will be just recieving changes in the model (could be watched) but view and controller won't be rerendered
ALSO, there is another plunker http://plnkr.co/edit/y0GzHv?p=preview which uses the controllerAs
.state('main', {
url: '/',
views: {
'#' : {
templateUrl: 'tpl.layout.html',
controller: 'MainCtrl',
controllerAs: 'main', // here
},
...
},
})
.state('main.criteria', {
url: '^/criteria/:criteria/:value',
views: {
'list' : {
templateUrl: 'tpl.list.html',
controller: 'ListCtrl',
controllerAs: 'list', // here
},
},
})
and that could be used like this:
<h4>{{main.hello()}}</h4>
<h4>{{list.hello()}}</h4>
The last plunker is here
you can use scope inheritance to update url without refreshing view
$stateProvider
.state('itemList', {
url: '/itemlist',
templateUrl: 'Scripts/app/item/ItemListTemplate.html',
controller: 'ItemListController as itemList'
//abstract: true //abstract maybe?
}).state('itemList.itemDetail', {
url: '/:itemName/:itemID',
templateUrl: 'Scripts/app/item/ItemDetailTemplate.html',
controller: 'ItemDetailController as itemDetail',
resolve: {
'CurrentItemID': ['$stateParams',function ($stateParams) {
return $stateParams['itemID'];
}]
}
})
if child view is inside parent view both controllers share same scope.
so you can place a dummy (or neccessary) ui-view inside parent view which will be populated by child view.
and insert a
$scope.loadChildData = function(itemID){..blabla..};
function in parent controller which will be called by child controller on controller load. so when a user clicks
<a ui-sref="childState({itemID: 12})">bla</a>
only child controller and child view will be refreshed. then you can call parent scope function with necessary parameters.
The short answer ended up being do not put the map inside a view that changes. The accepted answer provides a lot more detail on how to structure a page with sub-views, but the key point is not to make the map part of the view but to connect its behaviour to a view that does change and to use a Controller to update the market icons.
For the past 9 hours I have been trying to implement a page with tabs using Angular and ui-router.
I have the following:
.state('someAbstractParentRoot', {
'abstract': true,
url: '/{id}',
templateUrl: '/admin/templates/rootTemplate',
controller: 'someController1',
data: {
breadcrumbLabel: 'Details'
}
})
.state(someAbstractParentRoot.child, {
url: '',
views: {
details: {
templateUrl: '/admin/templates/details',
controller: 'someController2'
},
videos: {
templateUrl: '/admin/templates/videos',
controller: 'someController3'
},
logs: {
templateUrl: '/admin/templates/logs',
controller: 'someController4',
},
notes: {
templateUrl: '/admin/templates/notes',
controller: 'someController5'
}
}
})
Using bootstrap I simply show the template I need.
so when the videos tab is shown, the url is something like: localhost:8080/users/1#videos.
So, this all works great until I need to send this link to someone, because when I do that, the details tab is automatically opened. I can't just toggle classes on div elements because some of the tabs are using ng-if so jquery doesn't pick up all of the html it should, probably because of angulars digest cycle (it can be solved with setTimeout, but I am trying to avoid that).
Does anyone know how I can use ui-router to define a tab which will support direct linking?
Some example would be very much appreciated.
NB: I need the abstract parent state.
Can urls in ui-router have a '#' symbol?
There is a problem with your states
actually you declare only one state which can be divided in several parts you have to declare a state for each subviews so declare something like. I don't know if you really need an abstract state so i can juste declare:
$stateProvider.state('details', {
templateUrl: '/admin/templates/details',
controller: 'someController2'
})
.state('videos', {
templateUrl: '/admin/templates/details.html',
controller: 'someController3'
})
.state('logs', {
templateUrl: '/admin/templates/logs.html',
controller: 'someController4'
})
.state('notes', {
templateUrl: '/admin/templates/notes.html',
controller: 'someController5'
});
In your rootTemplate juste add this
<div id="tabsContainer">
<a ui-sref="details">Details</a>
<a ui-sref="logs">Logs</a>
....
</div>
<div ui-view> </div> <!-- Where your state template will be inserted -->