How to pass data between angular ui-router states? - angularjs

I'm developing a simple shopping cart tracking application to expand my knowledge of ui-router. I have eight different possible states, and each state has its own controller. Here is my app.config file where you can see the state definitions:
module.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state("empty", { templateUrl: "app/templates/empty.html", controller: 'emptyController', controllerAs:'vm' })
.state("initial", { templateUrl: "app/templates/initial.html", controller: 'initialController', controllerAs: 'vm' })
.state("shopping", { templateUrl: "app/templates/shopping.html", controller: 'shoppingController', controllerAs: 'vm' })
.state("shipping", { templateUrl: "app/templates/shipping.html", controller: 'shippingController', controllerAs: 'vm' })
.state("billing", { templateUrl: "app/templates/billing.html", controller: 'billingController', controllerAs: 'vm' })
.state("review", { templateUrl: "app/templates/review.html", controller: 'reviewController', controllerAs: 'vm' })
.state("confirmation", { templateUrl: "app/templates/confirmation.html", controller: 'confirmationController', controllerAs: 'vm' })
.state("error", { templateUrl: "app/templates/error.html", controller: 'errorController', controllerAs: 'vm' })
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise("/");
})
I'm trying to track the 'cart' using an object called cart, defined in a factory:
function cart() {
var factory = {};
factory.model = {};
return factory;
}
On my navbar, I have ui-sref's to change the state:
<li ui-sref-active="active"><a ui-sref="empty">Empty</a></li>
<li ui-sref-active="active"><a ui-sref="initial">Initial</a></li>
<li ui-sref-active="active"><a ui-sref="shipping">Shipping</a></li>
<li ui-sref-active="active"><a ui-sref="billing">Billing</a></li>
<li ui-sref-active="active"><a ui-sref="review">Review</a></li>
<li ui-sref-active="active"><a ui-sref="confirmation">Confirmation</a></li>
<li ui-sref-active="active"><a ui-sref="error">Error</a></li>
<li ui-sref-active="active"><a data-toggle="modal" data-target="#login-modal">Log in</a></li>
Whenever I change state, I need to transfer the entire cart.model to the new state. How can I do this? My controllers each look like this:
(function () {
"use strict";
angular.module('ShoppingCart')
.controller('emptyController', ['cart', emptyController]);
function emptyController(cart) {
var vm = this;
vm.model = cart.model;
};
}());
I'm trying to get the cart's model, but I'm not sure how I can update it on state change.

It sounds like you have the right idea of using a factory to keep the model. Since .factory and .service are singletons in angularjs, then you shouldn't have to worry about 'passing' the cart model around. Basically, anytime you enter a state, a controller instance is created that gets the cart factory injected into it. When you change states, a different controller is instantiated, and the SAME cart factory instance is injected, meaning that you should have access to the same cart.model as you had in the previous state.

If you want to move data from one state to another state you can do the following :
$state.go('toStateName', {id1 : 'dataToPass'}); //using javascript
<a ui-sref="toStateName({id1 : 'dataToPass'})">To New State</a>
and in the definition of 'toStateName' you can access these parameters using $stateParams service.
$stateProvider.state('toStateName', {
url: '/tostate',
params: {id1 : {}},
templateUrl: 'templates/send-to.html',
controller: 'SendToCtrl'
})
I hope this will do.
For more information look at the following link :
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref

I believe I figured it out. What I was doing wrong was referring to it as cart.model.whatever in my html pages. by switching to vm.model.whatever and leaving everything else the same, it started working.

Related

Trouble connecting controller using routing in AngularJS

Ill try and do my best to explain...
It doesn't seem like my controller is connecting/working properly. I'm not sure exactly why, every time I check my syntax seems correct. Here is my route declaration:
angular.module('portfolio', ['ngRoute']).config(function ($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl: '/public/app/views/main.html',
controller: 'MainCtrl',
controllerAs: 'main'
}).when('/about', {
templateUrl: '/public/app/views/about.html',
controller: 'AboutCtrl',
controllerAs: 'about'
}).when('/Resume', {
templateUrl: '/public/app/views/resume.html',
controller: 'ResumeCtrl',
controllerAs: 'Resume'
}).when('/Samples', {
templateUrl: '/public/app/views/samples.html',
controller: 'SamplesCtrl',
controllerAs: 'Samples'
}).otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(false).hashPrefix('');
});
Just as an example here is my resume view:
<paper-button ng-click="changeEx()" raised>Experience</paper-button>
<paper-button ng-click="changeSkills()" raised>Skills</paper-button>
<paper-button ng-click="changeEdu()" raised>Education</paper-button>
<paper-button ng-click="changeMisc()" raised>Misc.</paper-button>
<div ng-hide="exp">
<p>Experience</p>
</div>
<div ng-show="skills">
<p>Skills</p>
</div>
<div ng-show="education">
<p>Education</p>
</div>
<div ng-show="misc">
<p>Misc.</p>
</div>
Finally, here is the declaration of the controller for the resume view:
angular.module('portfolio', []).controller('ResumeCtrl', function () {
this.exp = true;
});
Obviously, this controller is incomplete but I am simply trying to test it by using this variable.
Do not use square brackets when you are referencing a module that has already been declared. This is interpreted by Angular as declaring a new module:
angular.module('portfolio').controller('ResumeCtrl', function () {
this.exp = true;
});
The next thing I would check is your main HTML page. Do you have this directive somewhere on your page:
<div ng-view></div>
Another problem is that you are declaring controllerAs in your route as Resume, but you are using this.exp to assign your variable and also trying to reference exp in your view. You need to follow this pattern instead, or use $scope:
angular.module('portfolio').controller('ResumeCtrl', function () {
var Resume = this;
Resume.exp = true;
});
And in your view you should reference the variable using the same name you defined in controllerAs:
<div ng-hide="Resume.exp">
<p>Experience</p>
</div>
Or, alternatively, you can keep your view the same, but assign your variable using $scope:
angular.module('portfolio').controller('ResumeCtrl', function ($scope) {
$scope.exp = true;
});

Angular ui router controlleras syntax not working

I an trying to develop an angular app using ui router, however I am stuck trying to get the controllerAs syntax working correctly.
my stateProvider looks like this
$stateProvider
.state('microsite', {
url: "/",
templateUrl: "microsite.tmpl.html",
abstract: true
})
.state('microsite.home', {
url: "",
templateUrl: "home.tmpl.html",
controller: 'MicrositeController as vm',
data: {
page_name: 'Introduction'
}
})
.state('microsite.features', {
url: "/features",
templateUrl: "features.tmpl.html",
controller: 'MicrositeController as vm',
data: {
page_name: 'Features'
}
})
.state('microsite.about', {
url: "/about",
templateUrl: "about.tmpl.html",
controller: 'MicrositeController as vm',
data: {
page_name: 'About'
}
});
As you can see I setup an abstract default view, and three pages.
I have also assigned a data object with a page_name for each page. This feeds into my controller
myapp.controller('MicrositeController', ['$state', function($state) {
var vm = this;
vm.page_name = $state.current.data.page_name;
vm.sidenav_locked_open = false;
vm.toggleSideNav = function() {
if ($mdMedia('gt-sm')) {
vm.sidenav_locked_open = !vm.sidenav_locked_open;
} else {
$mdSidenav('left').toggle();
}
}
}]);
and then delivers the name to the page via the vm.page_name variable.
However this is not happening. The variable never makes it to the page.
Also I have a vm.toggleSideNav function that is suppose to open and close the sidenav, but that never gets called.
the toolbar with the sidenav button is this
<md-toolbar layout="row" class="md-whiteframe-glow-z1 site-content-toolbar">
<div class="md-toolbar-tools docs-toolbar-tools" tabIndex="-1">
<md-button class="md-icon-button" ng-click="vm.toggleSideNav()" aria-label="Toggle Menu">
XXX
</md-button>
<h1>{{vm.page_name}}</h1>
</div>
</md-toolbar>
here is a plnkr example http://plnkr.co/edit/Na5zkF?p=preview
I am looking for someone to help me figure out the last piece on how to get the toggleSideNav function to get called when I click on the xxx button, and how to get the title to display in the toolbar.
From the Docs:
controller
(optional)
string
function
Controller fn that should be associated with newly related scope or the name of a registered controller if passed as a string. Optionally, the ControllerAs may be declared here.
controller: "MyRegisteredController"
controller:
"MyRegisteredController as fooCtrl"
controller: function($scope, MyService) {
$scope.data = MyService.getData(); }
— UI Router $stateProvider API Reference.
According to the Docs, your controller declaration is correct.
controller: 'MicrositeController as vm'
You need to look for your problem elsewhere.
UPDATE
Put the controller in the root state:
$stateProvider
.state('microsite', {
url: "/",
templateUrl: "microsite.tmpl.html",
//IMPORTANT == Put controller on root state
controller: 'MicrositeController as vm',
abstract: true
})
.state('microsite.home', {
url: "",
templateUrl: "home.tmpl.html",
̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶:̶ ̶'̶M̶i̶c̶r̶o̶s̶i̶t̶e̶C̶o̶n̶t̶r̶o̶l̶l̶e̶r̶ ̶a̶s̶ ̶v̶m̶'̶,̶
data: {
page_name: 'Introduction'
}
})
.state('microsite.features', {
url: "/features",
templateUrl: "features.tmpl.html",
̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶:̶ ̶'̶M̶i̶c̶r̶o̶s̶i̶t̶e̶C̶o̶n̶t̶r̶o̶l̶l̶e̶r̶ ̶a̶s̶ ̶v̶m̶'̶,̶
data: {
page_name: 'Features'
}
})
.state('microsite.about', {
url: "/about",
templateUrl: "about.tmpl.html",
̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶:̶ ̶'̶M̶i̶c̶r̶o̶s̶i̶t̶e̶C̶o̶n̶t̶r̶o̶l̶l̶e̶r̶ ̶a̶s̶ ̶v̶m̶'̶,̶
data: {
page_name: 'About'
}
});
})
The DEMO on PLNKR
Try adding the option controllerAs: 'vm' to the state params instead defining the controller as in the controller option.
Try adding the option controllerAs: 'vm' to the state params instead defining the controller as in the controller option.
or, if I'm not mistaken, you can add
myapp.controller('MicrositeController as vm' ...

AngularJs : When does the controller get executed

In case of angular, in its life cycle when does the controller that we define using the .controller method get executed?
First, when you are accessing a DOM with ng-controller attached to it.
E.g.
<ul ng-controller="YourCtrl">
<li ng-repeat="name in names">
{{name.firstName}}
</li>
</ul>
Documentation: https://docs.angularjs.org/guide/controller
Second, when you are accessing a URL route using $routeProvider / $stateProvider that has the method when() / state() with the parameter controller.
E.g.
Using ngRoute:
$routeProvider
.when('/', {
templateUrl: 'app/views/home.html',
controller: 'homeCtrl'
})
.otherwise({
redirectTo: '/'
});
Using ui.router:
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'partial-home.html',
controller: 'homeCtrl'
});
Hope it helps.

When using angular-ui-router, what is the suitable structure for controller?

Before use angular-ui-router, one controller always support several router&views, such as:
$routeProvider.
when('/posts', {
templateUrl: 'views/posts/list.html'
}).
when('/posts/create', {
templateUrl: 'views/posts/create.html'
}).
all views for one object share one controller:
app.controller('PostsCtrl', function($scope) {
$scope.create = function() {
// ...
}
$scope.list = function() {
// ...
}
});
and init data in view:
<div data-ng-controller="PostsController" data-ng-init="list()">
...
</div>
But in angular-ui-router, we use state, so we need create several controller for each state, such as:
$stateProvider
.state('posts', {
abstract: true,
url: '/posts',
templateUrl: 'views/posts/list.html',
controller: 'PostsCtrl'
})
.state('posts.detail', {
url: '/:postId',
templateUrl: 'views/posts/detail.html',
controller: 'PostsDetailCtrl'
})
Separating controller seems not a good design pattern.
So, is there any better suggestion to structure controllers?
It is a little late for an answer, but as I was struggling with it and found an answer, I might as well post it.
I agree with Nate's comment that in most circumstances you should keep the controllers as small as possible, i.e. you should write a separate controller for every state. However, if you find yourselves in a situation that really think that using the same controller for both the parent and child views is preferable you can simply omit the controller option in the child view. This view will then use the state of the parent. This can be read in the documentation. A little example:
app.js
app.controller('testController', ['$scope', function($scope){
$scope.message= 'This comes from testController';
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider ,$urlRouterProvider) {
$stateProvider
.state('test', {
url: '/test',
templateUrl: 'partials/test.html',
controller: 'testController'
})
.state('test.child', {
url: '/child',
templateUrl: 'partials/test2.html'
});
}]);
partials/test.html
<div>
Test.html: {{message}}
<div data-ui-view></div>
</div>
partials/test2.html
<div>
Test2.html: {{message}} as well.
</div>
This will produce the following (with url #/test/child)
Test.html: This comes from testController
Test2.html: This comes from testController as well.
I hope this helps anyone

AngularJS UI-Router navigate to a child URL

Trying to link to a child URL using Angular UI-Router (very new to this)
I have:-
app.config(function($stateProvider, $urlRouterProvider, $locationProvider){
$urlRouterProvider.otherwise("/products")
$stateProvider
.state('products', {
url: "/products",
templateUrl: "products.html",
})
.state('products.edit', {
url: "/:id",
templateUrl: "products.edit.html",
controller: function ($scope, $stateParams) {
$scope.id = $stateParams.id;
console.log($stateParams.id)
}
})
.state('customers', {
url: "/customers",
templateUrl: "customers.html",
});
//$locationProvider.html5Mode(true);
});
I can navigate to products and customers, but not the products.edit state.
Here is a PLUNKER
Because products.edit is the child state of products, the view of the former is to be embedded into the view of the latter. So in products.html, you need to include a <ui-view> where ui-router will place the child view. Like this:
<div>
<h4>Products Page</h4>
<ui-view></ui-view>
</div>
See this updated plunker.

Resources