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.
Related
I got a directive which has a model passed by an attribute:
use strict;
angular.module('ebs-front')
.directive('ebsIa', function() {
return{
restrict: 'A'.
scope: {
opened: '=ebsIaOpened',
model: '=ebsIaModel',
cb: '&ebsIaCb'
},
controller: function($scope, $uibModal){
console.log('check');
$scope.text = { text: 'test'};
$scope.$watch('opened', function(newValue) {
if(newValue === true){
var modalInstance = $uibModal.open({
controller: 'ImpactAnalyseController',
templateUrl: 'common/directive/ebs-ia-template.html'
});
}
});
}
}
});
In this directive, I need to do some operations and then open a modal window. So for so good, but the thing is, I want the $scope.model to be accessible in ImpactAnalysisController as well.
My assumption was that $scope.test and $scope.model will be available in ImpactAnalysisController automatically, but apparently a isolated scope is created which is only valid for the controller: function part.
What would be a good way to pass the model variable of the scope to the ImpactAnalysisController?! And why isn't it default behaviour in angular?
If I define my directive like below, then the removeFromFilters (in this case) IS available in the directive, so I'm kinda puzzled. Any help would be appreciated...
use strict;
angular.module('ebs-front')
.directive('ebsIa', function() {
return{
restrict: 'A'.
scope: {
opened: '=ebsIaOpened',
model: '=ebsIaModel',
cb: '&ebsIaCb'
},
controller: 'ImpactAnalysisController'
};
)};
There are several ways to share data between controllers in Angular. A couple that come to mind:
1- Use a $rootScope.broadcast('keyName', value) and listen for the value with $scope.on('keyName', function(){...} Use with care, not the best approach most of the time
2- Keep the data not in the controller but in a Service or Factory, and inject that into your controllers (preferable)
What would be a good way to pass the model variable of the scope to the ImpactAnalysisController?!
Depends on what the controller has access to and intends to do with it.
And why isn't it default behaviour in angular?
You're asking the wrong question. You chose an Isolate Scope. Why did you choose an Isolate Scope, if you wanted to inherit properties from its parent?
What may solve your problem:
If you're passing a pure model and expect to have some IO where the user is potentially altering the model I recommend reading and implementing: NgModelController
It will make the model and mechanisms to interact with it available to your directive(s) via an injectable Controller, independent of the type of scope you choose. All you have to do is require 'ngModel' according to $compile documentation.
Fixed with uibmodal's resolve functionality:
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
})
Item is passed from the parent scope to the ModalInstanceCtrl and becomes available in the controller as variable. Exactly was I was looking for!
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.
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 am trying to have dynamic routing in Angular 1.3. Something similar to what it described here and here. The examples suggest something like this:
$routeProvider.when('/:group/:pagename', {
controller: 'RouteCtrl',
templateUrl: 'uirouter.html'
})
and then the controller will have access to group and pagename through $routeParams. That works
I am trying to make this routing a little more dynamic and have template and controller to be selected dynamically as well:
$routeProvider.when('/:group/:pagename', {
controller: $routeParams.group + 'Ctrl',
templateUrl: $routeParams.pagename + '.html'
})
When I put a breakpoint on when I can see that there is $get property with a function that has $routeParams as one of parameters. But I can't figure out how to retrieve its values.
The project is in very early stage - I can go with ui-router or with ng-router if any of them has this functionality.
For the dynamic templateUrl portion you could try:
$routeProvider.when('/:group/:pagename', {
controller: "SomeCtrl",
templateUrl: function(params){return params.pagename + '.html';}
})
Not sure if the same could be done with the controller however.
Instead of a direct value you can declare a function that will return a templateUrl string, i.e.:
$routeProvider.when('/:group/:pagename', {
controller: $routeParams.group + 'Ctrl',
templateUrl: function (routeParams) {
return routeParams.pagename + '.html';
}
});
I guess the same may be true for controller, but you have to test that one out, as I've never used it for controllers.
That being said, if you have so much dynamic logic in this place, maybe instead of treating this as different controllers, you could encapsulate those views as different directives and the ng-if certain directive depending on $routeParams which will be set in one wrapping controller?
I have different views each created by a different controller. At a particular time only one of the views is visible.
I want to switch from one view to another view through a function of the controller of the first view and after that I want to call a method of the second view controller.
My problem is how should I call this method in an angular way?
I know the possiblity using $broadcast and $on but that smells a little bit.
The other choice ist to find the scope in the dom and calling the method via scope. But that is even more ugly.
What is the best solution?
You can use services to communicate between controllers. While you could create a generic shared service to have a central point to subscribe to and broadcast events, services are easier to maintain over time.
You can use Angular Routing
Check out the documentation. This is an excerpt from the documentation. You can make links like
Link
For the first route and so on.
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
Okay it is done and simpler as expected.
The idea is to use a service used in both views (controllers), that contains a 'execution boolean'.
The first view set the boolean to true, the second set a watch on this boolean and therefore is called and can call the desired method.
In the service:
trigger: function(name) { model[name] = true; },
setTriggerWatch: function(scope, name, callback) {
scope.$watch(function value() {
return model[name];
}, function listener(newValue, oldValue) {
if (newValue) {
callback();
}
});
},
In the destination controller:
sessionData.setTriggerWatch($scope, 'createSession', function callCreateSession() {
_createSession();
});
In the source controller:
sessionData.trigger('createSession');