I have a situation where I need to dynamically change the controller, so that the scope variables are influenced accordingly. General structure:
<div ng-controller="GameController">
// some general HTML which fits all types of games
<div ng-controller="someScopeVar"> // Type of game
// some game-type-specific ng-models that should respond to the change of controller, i.e scope
</div
</div>
I saw here that it can be done within an ng-repeat
. Can it be done outside of it?
In other words, can I tell angular to read it as a variable rather than a string literal?
I have a situation where I need to dynamically change the controller, so that the scope variables are influenced accordingly. General structure:
<div ng-controller="GameController">
// some general HTML which fits all types of games
<div ng-controller="someScopeVar"> // Type of game
// some game-type-specific ng-models that should respond to the change of controller, i.e scope
</div
</div>
I saw here that it can be done within an ng-repeat
. Can it be done outside of it?
In other words, can I tell angular to read it as a variable rather than a string literal?
- I think this question is misleading. Are you really trying to set the controller dynamically, or just change the contents of $scope ? – gkalpak Commented Jul 11, 2014 at 5:21
-
1
someScopeVar
should be a controller function, what value you use now? – Grundy Commented Jul 11, 2014 at 5:22 -
@Grundy, you were right. I used a string literal for the controller, which was defined outside the scope, rather than assigning the controller itself to
someScopeVal
. Please post as answer – Matanya Commented Jul 11, 2014 at 5:31 - @Grundy, this works for the initial assignment, but when I try to change it in response to a user click event, it stays as it were – Matanya Commented Jul 11, 2014 at 5:43
-
1
You should try to deeply observe the
ui-router
here are some (up to date) links to supper basic resources stackoverflow./a/20581135/1679310. This feature is in fact a state machine, which is intended to change parts of your app based on state changes (could be dependent on url but does not have to). You will just manage your states, and these could have many inner views with related controllers. Change state - the same view (template) with different controller will be injected... really please, try to observe that ;) – Radim Köhler Commented Jul 11, 2014 at 5:57
2 Answers
Reset to default 3 +100As discussed in the ments, angular
has a really powerful feature/library for handling these scenarios - ui-router
(with its powerful wiki).
The ui-router
is an answer to need to develop functional pieces - states, rather then think in view/url (cite from a home page):
AngularUI Router is a routing framework for AngularJS, which allows you to organize the parts of your interface into a state machine. Unlike the $route service in the Angular ngRoute module, which is organized around URL routes,
UI-Router
is organized around states, which may optionally have routes, as well as other behavior, attached.
There are some very interesting blog posts:
- AngularJS State Management with ui-router (by Ben Schwartz)
...The most interesting thing about AngularJS's new router, isn't the router itself, but the state manager that es with it. Instead of targeting a controller/view to render for a given url, you target a state. States are managed in a heirarchy providing inheritance of parent states and plex position of page ponents, all the while remaining declarative in nature...
- The basics of using ui-router with AngularJS (by Joel Hooks)
...
ui-router
fully embraces the state-machine nature of a routing system. It allows you to define states, and transition your application to those states. The real win is that it allows you to decouple nested states, and do some very plicated layouts in an elegant way.
Exactly the point we need - decouple child states... dynamically change the controller in fact ... could be by url change or just a state change (one sibling child instead of other without url change)
You need to think about your routing a bit differently, but once you get your head around the state-based approach, I think you will like it...
Finally, there are few links, which I would mark as the saint grail of ui-router
Sample application. In action we can see, how the ui-router state machine does work. We can load list as a parent state, then we can select row items, which do represent their own child state... while parent is not reloading (here I tried to explain it in more details)
state.js
- the essential piece of code of the sample application. This is one of the best documented code snippets I've seen... Spent some time to go through and this will give you the 80% percent of answers: Howui-Router
is working
From my experience, this is really suitable for small apps as well as for large scale systems... love it...
The way this works for ngRepeat
is different. It works because it piles each repetition, which is what you need to do here.
For example:
mainCtrl = function($scope, $pile, $element) {
$scope.someScopeVar = ctrl1;
$scope.changeCtrl = function(id) {
$scope.someScopeVar = id === 1 ? ctrl1 : ctrl2;
var elm = $element.find('div');
elm.replaceWith($pile(template)($scope));
}
}
var ctrl1 = function($scope) {
$scope.name = 'World';
}
var ctrl2 = function($scope) {
$scope.name = 'John';
}
We assign each controller function to $scope.someScopeVar
, then we must pile a new element with the new controller and replace the old one.
I don't believe angular has the capability to update a controller on the fly like that. Might be a good idea for a new feature.
FIDDLE
Another option would be to use the mixin pattern to update the main scope. You don't get a new shiny scope but it may work for your purposes.
mainCtrl = function($scope, $pile, $element) {
angular.extend($scope, ctrl1());
$scope.changeCtrl = function(id) {
angular.extend($scope, id === 1 ? ctrl1() : ctrl2());
}
}
var ctrl1 = function() {
var obj = {};
obj.name = 'World';
return obj;
}
var ctrl2 = function() {
var obj = {};
obj.name = 'John';
return obj;
}
FIDDLE