I have an Angular app using ui-router with the following states defined in my app.
$stateProvider
.state('classes', {
url: '/classes/:type',
templateUrl: 'classes.html',
controller: 'ClassesCtrl',
})
.state('classes.detail', {
url: '/:id',
views: {
"#": {
templateUrl: 'class.html',
controller: 'ClassCtrl'
}
}
});
and my controllers look like:
app.controller('ClassesCtrl', function($scope, $stateParams){
$scope.foo = "bar";
});
app.controller('ClassCtrl', function($scope, $stateParams){
console.log($scope.foo);
});
As you can see, class.detail hides the parent view by targeting the root level unnamed ui-view. The issue I'm having is that the child is not inheriting the parent's scope (I can't access $scope.foo in class.detail). It seems like the parent state gets destroyed when I go to the child state because if I click back, it has to reload all the data.
How do I effectively hide the parent view but still access the parent data?
From the ui-router documentation:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
Well, you're using # to define your view but your parent state is named, so you should name it #classes
Edit: I made this fiddle to explain about names.
Related
I have a parent controller and two tabs within it, each having its own controller as shown in image below:
Parent and Child View Model
I am broadcasting an event from Tab1/Tab2 controllers using $rootScope and catching it directive's controller scope using $scope. When I am switching between tabs, the parent controller and the directive's controller are getting initialized again and the event is caught twice in directive's controller even though it is broadcasted just once.
I tried to debug and noticed the directive's controller $id when it was being caught. When the event was caught first time, the directive's id was the one which got created with previous tab(tab from which i switched). And second time, directive's id was the one which got created with current tab (tab to which i switched).
Note: 1) Even after using reload: false option, parent controller and directive controller is getting re-initialized.
2) I am not initializing parent controller by using ng-controller in html. It is getting initialized only via $stateProvider routing.
This is how i configured my states:
$stateProvider
.state('app.parent', {
url: 'parent/:paramId',
params: {
paramId: 'sample'
},
views: {
'content-parent#app': {
templateUrl: 'parent.html',
controller: 'ParentController',
resolve: {
defaultParams: function(parentService, $stateParams) {
return parentService.getDefaultParams($stateParams.paramId);
}
}
}
}
})
.state('app.parent.tab1', {
url: '/tab1/',
views: {
'content-child#app.parent': {
templateUrl: 'tab1.html',
controller: 'Tab1Controller'
}
}
})
.state('app.parent.tab2', {
url: '/tab2/',
views: {
'content-child#app.parent': {
templateUrl: 'tab2.html',
controller: 'Tab2Controller'
}
}
});
No, the parent state (and thus controller/component) is not re-initialized when the child states are changed. This includes any resolve functions and state event handlers associated with the parent state.
Here's a Plunker that illustrates that the parent scope remains the same when you navigate between child states.
You can force it to reload the parent though, by setting the reload option of $state.go(..) to true. Like this:
$state.go('parent.childA', null, { reload: true });
The parent State remains unchanged as long as you do not push the changes to the parent controller using emit.
Here is a c-sharpcorner link that might help you!
I have a catalog.html angular application where 2 ui views and they under the same controller categoryCtrl:
catalog.html
<!-- SEARCH -->
<div ui-view="search-catalog"></div>
<!-- CONTENT -->
<div ui-view="result"></div>
app.js
.state('catalog', {
url: '/catalog',
views: {
'search-catalog': {
templateUrl: 'views/search-catalog.html',
controller: 'categoryCtrl'
},
'result': {
templateUrl: 'views/result.html',
controller: 'categoryCtrl'
},
When I change something in search-catalog and can see changes inside categoryCtrl, the changes are not reflected in result. In particular, I have an array $scope.items and ng-repeat does not update its values
As said the categoryCtrl has been duplicated with two separate scopes, thus if you modify $scope.items object in the search-catalog scope will not be reflected in result scope.
If you don’t mind to move the logic on the parent scope (catalog) you could also do the following:
.state('catalog', {
url: '/catalog',
views: {
'': {
templateUrl: 'catalog.html',
controller: 'catalogCtrl'
},
'search-catalog#catalog': {
templateUrl: 'views/search-catalog.html'
},
'result#catalog': {
templateUrl: 'views/result.html'
}
}
.controller("catalogCtrl", function($scope) {
$scope.items = ["item1", "item2", "item3"];
});
In this case the nested views will be bound to the same controller/scope.
If you want to separate logic between your nested views and the parent you should use a singleton like a service or factory and inject that in your nested views.
AngularJS is creating 2 different instances of your controller so, both controllers have separates scopes. The changes you make in one of them is not reflected in the other.
There are different ways to achieve what you are looking for, you could place the code that need to be shared in a parent controller or you could create a factory (which is a singleton ) to share the data among both controllers.
You could also use events (broadcast, emit, on) to communicate controllers.
I am writing an angular app where a parent controller manages the global state, and it transitions between states to display information to users.
I followed this answer and used params to send objects to the child component/controller. It works fine, but if I update the values in the child object, these changes are not reflected in the parent.
My code is organised like this:
index.html
<bodyng-controller="main-loop">
...
<li ui-sref="supernova({data:data})" ui-sref-active="active">
data is an object, {"a":1,"b":2}
routes
.state("supernova", {
component: 'supernova',
params: { 'data': null }
component
component('supernova', {
templateUrl: 'views/supernova.html',
controller: ['$stateParams', supernova],
controllerAs: 'supernova'
});
controller
function supernova($stateParams) {
var ctrl = this;
ctrl.data= $stateParams.data;
I can read the value of data fine from both the view and the controller. However if I make a change like this:
ctrl.stuff = function () {
ctrl.data.a = 20;
The changes are visible in the component but not in the parent controller.
Is there any way to share the same object instance between both using ui-router constructs? That is, changes in one are reflected in the other (in a two-way binding style).
Here's my UI-Router configuration. I have a main products view and two nested states underneath that. I want each nested view to have its own controller, but I also want to be able to inherit some basic information from the parent controller (productsCtrl). My assumption would be that I could access productsCtrl from productShowCtrl, but it keeps coming back as undefined. Do productsCtrl and productShowCtrl not have a parent-child relationship by default?
var myProducts = angular.module('Products', [
'ProductsShow'
])
.config(function($stateProvider, $urlRouterProvider){
$stateProvider.state('store.products', {
url: '/products',
views: {
'subhead#': {
templateUrl:'products/list/subhead.tmpl.html'
},
'content#': {
templateUrl:'products/list/list.tmpl.html',
controller: 'ProductsCtrl as productsCtrl'
}
}
})
.state('store.products.create', {
url: '/create',
views: {
'subhead#': {
templateUrl: 'products/create/subhead.tmpl.html'
},
'content#': {
templateUrl: 'products/create/create.tmpl.html'
}
}
})
.state('store.products.show', {
url: '/:productId',
views: {
'subhead#': {
templateUrl: 'products/show/subhead.tmpl.html'
},
'content#': {
templateUrl: 'products/create/create.tmpl.html',
controller: 'ProductShowCtrl as productShowCtrl',
}
}
});
});
ProductsCtrl (for the main products view):
myProducts.controller('ProductsCtrl', function($stateParams) {
var productsCtrl = this;
productsCtrl.parentTest = "PARENT";
});
ProductsShowCtrl (for the child view):
angular.module('ProductsShow', [])
.controller('ProductShowCtrl', function($stateParams) {
var productShowCtrl = this;
productShowCtrl.childTest = "CHILD";
console.log('parent: ', productsCtrl.parentTest);
});
I can't access productsCtrl.parentTest from ProductShowCtrl. How can I access it?
There are related Q & A:
ui router - nested views with shared controller
How do I share $scope data between states in angularjs ui-router?
I created special working plunker
The example here with controllerAs could be like this:
The state defintion:
.state("main", {
controller:'mainController',
controllerAs:'mainCtrl',
url:"/main",
templateUrl: "main_init.html"
})
.state("main.1", {
controller:'childController',
parent: 'main',
url:"/1",
templateUrl: 'form_1.html'
})
.state("main.2", {
controller:'childController',
parent: 'main',
url: "/2",
templateUrl: 'form_2.html'
})
Main controller will define the Model:
app.controller('mainController', function ($scope) {
var Model = {Name : "xxx"}; // controller property Model
})
And in form_1 or form_2 we can access that model with controllerAs syntax:
<div>
<h5>Child state FORM 2</h5>
<pre>{{mainCtrl.Model}}</pre>
Change the value in a child state FROM 2
<input ng-model="mainCtrl.Model.Name" />
</div>
Where mainCtrl.Model represents the reference to parent controller (inside of our and its $scope)
Check it here
The $scope doesn't work like that. They don't have a parent-child relationship by default like you asked. You can read more about it in the angular api docs.
The $rootScope has, but still it's bad practice in my opinion.
What you should do in angular when you want to access a variable in two different controllers like your trying you should create a service (factory) to do that.
Maybe you should have a products service in which you could declare variables and functions to access those variables in the conrollers.
Please note that from the ui-Router official docs
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain
if the views of your states are nested. Inheritance of scope
properties has nothing to do with the nesting of your states and
everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates
populate ui-views at various non-nested locations within your site. In
this scenario you cannot expect to access the scope variables of
parent state views within the views of children states.
So you can not have the scope variables in above way.
If you wants to share data across the states you can do in following ways :
1). Any time you need to share data across states you will need to create a service/factory that you can instantiate in your controllers associated with those states. The factory will consist of basic getters and setter for the different data you need to share. Just like when you build getters and setters in java to share across classes.
Demo : Working plunker with this approach.
2). You can set a global variable with $rootScope. It will be accessible everywhere since its global, I strongly advise you don't do this but though I would point it out to you anyway.
3).When a state is "active"—all of its ancestor states are implicitly active as well.So you can build your states considering the parent-child relationship and share data across scopes in hierarchical manner.
Demo : Working plunker with mentioned approach.
I have noticed that in one of my controllers I am getting $rootScope and $scope injected, and they both point to the same object.
Furthermore, in all my other controllers, the $scope object is shared. So whenever I inject scope, it contains the properties/methods assigned to it in all the other controllers that have so far been instantiated.
This isn't an app that I worked on from the beginning and it's pretty massive. I haven't seen this behavior before and I don't know where to begin diagnosing it. Any ideas what is causing this?
The way that we are setting up our controllers/directives is pretty standard and it looks like this:
angular.module('myApp')
.directive('mainNav', function() {
return {
restrict: 'A',
templateUrl: 'scripts/directives/mainNav/mainNav.html',
controller: 'mainNavCtrl',
replace: true,
link: function(scope, element) {
//Do DOM-related stuff
});
}
};
})
.controller('mainNavCtrl', function($rootScope, $scope, $state) {
//Do controller stuff
});
We do also configure our app as follows:
angular.module('myApp', ['ui.router', 'kendo.directives'])
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('app', {
url: '/',
templateUrl: 'views/app.html',
resolve: {
//Fetch stuff
}
})
; });
In response to Kursad Gulseven's comment, this is what I'm seeing in Batarang:
The scope with ID 002 gets passed in as $scope and $rootScope to the first controller. When properties are added to $scope, they show up on $rootScope. Then all the other controllers are receiving the scope with ID 00A. So properties added to $scope in those controllers are visible to all other controllers getting $scope injected.
$rootScope and $scope are not the same object.
If you have an object named anObject in root scope and it has an attribute named anAttribute; then you can access that attribute using $scope like this:
$scope.anObject.anAttribute
That's because Angular looks up parent scopes if it cannot find an object in $scope.
UPDATE:
Your mainNav directive should have an inherited child scope by adding scope: true.
When "scope" is false, which is the default setting for directives, the parent controller and directive's controller share the same scope object. Setting scope true will create a new child scope for the directive.