Angular -ui.router: update URL without refreshing view

I have a SPA Angular, which presents various lists of recommendations and a Google location map based on different abbreviations of some restaurant data (see m.amsterdamfoodie.nl ). I want each of these lists to have its own URL. For Google to crawl different lists, I use <a> tags to navigate offcanvas.

The <a> tag is currently updating the view, which is very noticeable with the map.

  • I can prevent this with ng-click and $event.preventDefault() (see code snippets below), but then I need to implement a browser URL updater.
  • But when I try Angular $state or in the browser history.pushstate I end up triggering state changes and updating the view ...!

So my question is: how can I update the model and URL, but not updating the view? (See also Angular / UI-Router - How to update URL without updating everything?

I have experimented with a lot of approaches and currently have this html

 <a href="criteria/price/1" class="btn btn-default" ng-click="main.action($event)">Budget</a> 

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, in my opinion, what happens is that I run the code $stateProvider :

 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 hint is that with the code below, if I download, for example. http://afmnew.herokuapp.com/criteria/cocolate/italian , then the view is updated when navigating, whereas if I download http://afmnew.herokuapp.com/ there are no updates, but instead of them there are no URL updates. I do not understand why this is happening at all.

+22
angularjs seo single-page-application angular-ui-router
Dec 14 '14 at 7:11
source share
4 answers

Based on our previous discussions, I want to give you some insight into how to use the UI-Router here. I believe that I understand your challenge correctly ... a working example . If this does not fully fit, please take it as an inspiration.

DISCLAIMER: With the plunker, I could not achieve this: http://m.amsterdamfoodie.nl/ , but the principle should be that the example is similar

So, there is a definition of state (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 will 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 ground state defines these nested representations of the ground state: 'viewName @main ', for example. 'right@main'

Also subview, main.criteria inserts a layout into the views.

Its url starts with the ^ sign ( url : '^/criteria/:criteria/:value' ), which allows you to have a / forward slash for the main rather than a double forward slash for the child

And there are also controllers, they are a little naive here, but they should show that there can be real data loading in the background (based on criteria).

The most important thing here is that PARENT MainCtrl creates $scope.Model = {} . This property will (thanks to inheritance) be shared between parents and children. This is why all this 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 understand how we can use the functions provided by us using the UI-Router:

Check out the above extract here in a working example

Expand: The new plunker is here

If we do not want the map display to be recreated, we can simply lower this form to the def child state:

 .state('main.criteria', { url: '^/criteria/:criteria/:value', views: { // 'map' : { // templateUrl: 'tpl.map.html', // controller: 'MapCtrl', //}, 'list' : { templateUrl: 'tpl.list.html', controller: 'ListCtrl', }, }, }) 

Now our VIEW map will simply receive changes in the model (can be viewed), but the view and controller will not restart

ALSO, there is another plunker http://plnkr.co/edit/y0GzHv?p=preview that uses 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 this can be used as follows:

 <h4>{{main.hello()}}</h4> <h4>{{list.hello()}}</h4> 

The last plunker is here

+11
Dec 17 '14 at 18:33
source share

This is an example of a path, 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 https://github.com/angular-ui/ui-router/issues/64 release

Here you can check the $state API: http://angular-ui.imtqy.com/ui-router/site/#/api/ui.router.state . $ state

+15
Jun 12 '15 at 9:40
source share

you can use area inheritance to update the url without updating

 $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 the child view is inside the parent view, both controllers use the same scope. therefore, you can place a dummy (or necessary) ui-view inside the parent view, which will be populated by the child view.

and paste

 $scope.loadChildData = function(itemID){..blabla..}; 

in the parent controller, which will be called by the child controller when the controller boots. so when the user clicks

 <a ui-sref="childState({itemID: 12})">bla</a> 

only the child controller and child view will be updated. then you can call the function of the parent area with the necessary parameters.

+2
Dec 23 '14 at 15:56
source share

The short answer ended up not putting the map inside a changing view. The accepted answer contains more detailed information on how to structure the page with subviews, but the key point is not to make the map a part of the view, but to associate its behavior with the view, which changes and use the controller to update market icons.

0
Dec 17 '14 at 13:46
source share



All Articles