Passing parent scope variables to ui-view component - angularjs

I'm using angular-ui/ui-router and have directives/components with isolate scope defined to be rendered on state change.
$stateProvider
.state('about', {
url: '/about',
template: '<about></about>'
})
.state('blog', {
url: '/blog',
template: '<blog></blog>'
})
My main page is a directive that loads the user with all permissions in its controller
app.directive('main', function() {
return {
...
template: "<div><header></header><div ui-view></div></div>",
controller: [..., function(...) {
$scope.user = // $http load user
}]
);
When navigating to different states, I want the user to be passed to all ui-view directives/components. However I can't find a nice way to realize this without a resolve that would load the user on every state change or using the $rootScope
I'm looking for something similar to the following, where user is the one from the parent scope.
$stateProvider
.state('about', {
url: '/about',
template: '<about user="user"></about>'
})
.state('blog', {
url: '/blog',
template: '<blog user="user"></blog>'
})
Any idea how this can be done?

in the controller, you can assign the user to $rootScope, like $rootScope.user = sth, then you can whether add controller for each state url to get the user like $scope.user= $rootScope.user, or pass the rootScope user as parameter.

you need to use $rootscope in your controller like this
$rootScope.user = sth;
and you can get user like this
$scope.user= $rootScope.user;

Related

Inject a resolve provider from child state to controller

So i'm trying to pull some id from the url parameters.
Here are my states :
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
controller: 'lolController'
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
resolve: {
someId: function ($stateParams) {
// I cant print the id from here
console.log("PARAMS", $stateParams.id)
return $stateParams.id;
}
},
})
Controller
.controller('lolController',
['$scope', 'someId', function ($scope, someId) {
$scope.someId = someId;
}])
But whenever i'm trying to access the url /parent/child-profile/123abc i'm getting the error Unknown provider: someId See error here..
How do I fix this? Thanks.
EDIT
The answer provided by Jay Shukla helped me get this idea.
The parameter is undefined because I declared the controller on the parent state before actually calling the child state which contains the value from it's url. Here's a simple solution I came up with, with Jay Shukla's help.
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
controller: 'lolController'
})
I removed the controller declaration from the parent state and moved it to the child state. Since in my situation the parent state's template only contains a <div ui-view></div>.
The idea is :
Only state is nested but both html are separate so controller models will not be inherited - Jay Shukla
Each state can have their own controller.
Please add/edit more to improve this question.
Try to inject $stateParams then you will get id in that object.
Like this
.controller('lolController',
['$scope', '$stateParams', function ($scope, $stateParams) {
$scope.someId = $stateParams.id;
}])
You can also defined your parameters in different ways as below
url: '/child-profile/:id', // Inside stateparams you will get id as key
OR
url: '/child-profile/{id}',
OR
url: '/child-profile/:{id:int}', // Id with integer value

Working with multiple states and single controller in angular.ui-router

I am learning angularJS and creating a web application which uses ui-router.
I have defined states as follows:
angular.module('test', [])
.config(function($stateProvider){
$stateProvider.
state('root',{
url: '/',
abstract:true,
templateUrl: '/root.html',
controller: 'MyController'
})
.state('root.route1',{
url: '/route1',
parent: 'root',
views:{
'':{
templateUrl: '/route1.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
.state('root.route2',{
url: '/route2',
parent: 'root',
views:{
'':{
templateUrl: '/route2.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
});
While navigating back and forth between route1 and route2, I want to share scope variables from MyController. When I navigate to route2 from route1, it is loosing value of scope variable.
I am not sure what I am doing wrong.
Can anyone help me?
Thanks in advance.
I have yet to work with the ui-router, but I have worked with AngularJS for the last couple of years and this is how the language generally has worked in the past.
A controller's main purpose is to control the data on a single template. These controllers can communicate to each other through an AngularJS factory, often known as a service. In your case, you probably want to use a service as the controllers are getting destroyed on successful route change.
angular.module('test', [])
.factory('myFactory', function() {
var info = "Hello World";
return {
info: info
};
})
.controller('CtrlOne', function($scope, myFactory) {
$scope.info = myFactory.info;
})
.controller('CtrlTwo', function($scope, myFactory) {
$scope.info = myFacotry.info;
});
You can then use the two controllers on the two different views and they share the variables from the service that connects them.
Use $stateParams to pass parameters between two states.
Fallow the below steps :
Define your state with params object.
.state('route.route1', {
url: 'your url name',
params: {
nameOfParamObj: null
},
controller: 'your controller',
templateUrl: 'your template url',
})
From the controller where you want to send scope data use as fallows
$state.go(toState, params, options);
In toState controller catch state params using $stateParams
$stateParams.yourParamObjectName
Make sure $stateParams, $state services as dependency in your regarding controller
Have a look into the official ui-router documentation Here.

Wanting to Run some init code in AngularJS with ui-router before menu displays

I'm wanting to run some initialization code before my actual menu shows. That is, in my real app, I have an http resource I want to load that will effect what choices are shown in the menu choice list. More specifically, I want to put up a "waiting" message when my program first runs until the http resource completely loads. I've been experiminent with resolves but that seems to only help me after the menu has loaded.
I'm hoping not to have to use the bootstrap pattern since I want to do this in multiple places in my app, not just the first time it runs. I'm also not a big fan of the bootstrap pattern. It seems a little contrived to me.
I'm trying to do this with a very simple example first I have loaded here:
http://plnkr.co/edit/eehhA1CR2sEMQZrQBo3E?p=preview
var app = angular.module('svccApp', [
'ui.router'
]);
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider,$q,$timeout) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
template: '<p>HOME HERE</p>',
controller: 'HomeController as vm'
}).
state('about', {
url: '/about',
template: '<p>about here title: {{vm.title}}</p>', //'index4template.html',
controller: 'AboutController as vm',
resolve: {
title: function(){
return 'from title function';
}
}
});
}]);
var injectParamsAbout = ['title'];
var AboutController = function (title) {
var vm = this;
vm.title = title;
};
AboutController.$inject = injectParamsAbout;
angular.module('svccApp').controller('AboutController', AboutController);
var injectParamsHome = [];
var HomeController = function () {
};
HomeController.$inject = injectParamsHome;
angular.module('svccApp').controller('HomeController', HomeController);
What about using parent and child (not sure if this is not your not-liked bootstrap pattern). I created/updated working plunker here
// this is loaded immediately
// and that means, that user will see ...loading... ASAP
.state('about', {
url: '/about',
template: '<p ui-view="">...loading...</p>', //'index4template.html',
controller: function($state){ $state.go("about.child"); },
//controller: 'AboutController as vm',
})
// this state is triggered in the controller above
// and it will take 2,5 second to do all the resolve stuff
.state('about.child', {
template: '<p>about here title: {{vm.title}}</p>',
controller: 'AboutController as vm',
resolve: {
title: function($timeout){
return $timeout(function(){
return 'from title function';
}
, 2500 // wait 2,5 second - and then replace ...loading...
);
}
}
The idea behind is to load some template (e.g. ...loading...), as a parent view. Once instantiated (loading is displayed) we redirect to our child. In the example above, there is 2,5 second delay (loading form server), and then the child replaces parent fully...
Check it here
Based in #Radim example, a more clearer aproach that worked for me:
If you are using ui-router then you could resolve this using nested states. For example:
$stateProvider
.state("main", {
url: "/",
template: '<div ui-view></div>',
controller: 'InitController'
})
.state("main.landing", {
url: "landing",
templateUrl: "modules/home/views/landing.html",
controller: 'LandingPageController'
})
.state("main.profile", {
url: "profile",
templateUrl: "modules/home/views/profile.html",
controller: 'ProfileController'
});
In this example you have defined 3 routes: "/", "/landing", "/profile"
So, InitController (related to "/" route) gets called always, even if the user enters directly at /landing or /profile
Important: Don't forget to include <div ui-view></div> to enable the child states controller load on this section

undefined stateParams using ui-router

Question: For some reason I can't get my controller to recognize my url parameters across sessions.
Background: I have a nested view called modal that takes a parameter, whose url is /modal/:id (eg: /#/modal/1/ or /#/floorplan/1+2/). Ideally, when the user goes to this url, a modal will automatically open with the resource(s) with the given id.
Since the parent state and the child state(modal) are being handled by the same controller, the modal state has a custom data attribute (modalStatus) in its configuration set to true. When this custom attribute is enabled the modal is displayed.
I can currently go from the parent state to the nested state and trigger the modal but when I start a new session or refresh the page with a url like /modal/3, the application fails to read the parameters ($stateParams), which is being logged as an empty object.
I have tried using onEnter and Resolve but I'm not exactly clear on how to use them in this scenario.
Router
$stateProvider
.state('home', {
name: 'home',
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
})
Relevant part of controller:
$scope.init = function() {
console.log($stateParams);
if ($state.current.data) {
if ($state.current.data.modalState === true) {
$scope.openModal();
}
}
};
$scope.init();
edit: plunkr
You could try adding the params option:
$stateProvider
.state('home', {
name: 'home',
params: {
id: null
},
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
});
The child state home.modal should inherit the params of the parent. On your ui-sref from one state to another, pass the param like so:
<a ui-sref="home.modal({ id: x })"></a>
Then your url would turn out to be /modal/:x (where x is the number).
Also the $stateParams should then show the params as you wished.

How to access data from my controller in a state

What is the best way to stop a user from going to a state if there is no data for that state? I need to redirect the user to /featured if there is no data in one of the other states.
I thought that I could check the controller for data first and then redirect using $state.go if the data was null, but I can't find an example of how to access data in a controller inside of an onEnter event
I think a code example should sum it up:
fseControllers.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/featured");
$stateProvider
.state('featured', {
url: "/featured",
templateUrl: "featured-template"
})
.state('friends', {
url: "/friends",
templateUrl: "friends-template",
controller: 'FseController',
onEnter: function(){
// Here I want to redirect to /featured if no data in controller
}
})
.state('stories', {
url: "/stories",
templateUrl: "stories-template"
})
.state('events', {
url: "/events",
templateUrl: "events-template"
})
})
You could refactor your controller to use a service , that can be injected into onEnter function and to the Controller.
AngularJS Services
onEnter:function(FriendsService, $location){
if(!!FriendsService.getFriends()){
$location.path("/featured");
}
}

Resources