Angularjs - refresh controller scope on route change - javascript

Was wondering what is the best way to refresh controller scope on route change?
Any help would be appreciated!

I would refactor to have all my initialisation of data properties on the scope in a single function, called something like initScope() that is called when the controller is first run, and also on the $routeChangeSuccess (and probably also $routeChangeUpdate if you want to handle changes to the URL that resolve to the same route) event(s).
e.g.
app.controller('MyCtrl', ['$scope', function ($scope) {
function initScope() {
$scope.foo = 1;
$scope.bar = { ram: 'ewe' };
}
initScope();
$scope.$on('$routeChangeUpdate', initScope);
$scope.$on('$routeChangeSuccess', initScope);
}

Related

Pass data to different controllers using Angularjs

I have the below code in one controller.
$scope.DataModel= [];
$scope.DataModelTexts= { buttonDefaultText: '' };
I will be using the same code in one more controller. Now instead of writing the same code in 2nd controller too, i want to know if there is a way to put this in a common code in some factory or service and use that in both the controllers. I have tried to read about factory and services and i am getting a bit confused of how to use any one of these in my scenario.
Any information would help me gain more knowledge about factory and services in angularjs. Thanks.
You're on the right track, use can use a factory or a service to share code between controllers. Note that in angular services(and factories) are singletons; they are instantiated once when the app starts and then anytime you inject it into a controller, you are referencing the same instance. Consider the following code:
var myApp = angular.module('myApp',[]);
myApp.service('MyService', function() {
let _someValue = 'Initial Value';
this.setValue = function(value){
_someValue = value;
}
this.getValue = function(){
return _someValue;
}
});
//First Controller Run
myApp.controller('ControllerA', function($scope, MyService) {
MyService.getValue(); //Initial Value
MyService.setValue("BRAND NEW VALUE!!!");
});
//Run after ControllerA
myApp.controller('ControllerB', function($scope, MyService) {
MyService.getValue(); //BRAND NEW VALUE!!!
});
Her you'll see that MyService holds the state of someValue. ControllerA get MyService injected to it and can use the methods of that service to set a new value. Now for any subsequent call for that same state, like for instance by ControllerB, the updated value will be returned.
You can use the .config() or a run() blocks (good SO on these here: AngularJS app.run() documentation?) to bind these reused variables to $rootScope, then call them from $rootScope.DataModel and $rootScope.DataModelTexts from within your controllers or services (as long as you inject $rootScope into these controllers and services).

How to use constants defined in one file in another in angularjs

Well I have seen couple of questions in StackOverFlow related to this but couldn't find proper answer.
Say in my app.js file I have defined a constants object which basically has controllers names. As I use "ui-router" I want to attach Controllers to views on state change.
app.js
var app = angular.module('myApp', ['ui.router']);
app.constant('CONTROLLER', {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
Now I want to use those constants to define Controller names in other file, say controller.js. Illustrating couple of ways which I tried but dint work for me.
controller.js
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', function ($scope, myFactory) {
$scope.result = myFactory.someAPI();
}]);
ERROR -> Uncaught ReferenceError: CONTROLLER is not defined
Even I tried injecting that constant module in Controller which gave same error.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
$scope.result = myFactory.someAPI();
}]);
I know that second way is wrong. As I define controller names in my main app.js file and use those constants to attach controllers to views during state change.
I want reuse those constants to define controllers names too.
I may be doing something wrong. Any suggestions ?
Your code shows that you have correctly injected the CONTROLLER constant into the controller constructor function.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
// use constant here
}]);
That being said you can only access the constant except from within the controller function itself. The names of controllers, services, factories etc must be accessible strings, which is not the case until the constant has been injected.
You could use a plain old JavaScript object outside angular to define the names.
var CONTROLLER = {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
I can only guess that you are trying to do this because the controller or directive you are writing will be referenced/used at multiple points throughout your html.

$resolve not added to $scope until after controller creation

I'm trying to take advantage of the (new in 1.5.0) feature that adds the resolve map to the scope of the route. However, it seems like the map isn't actually getting added to the scope until after the controller has loaded.
Here's a very simple module that demonstrates the issue (from this plunk):
var app = angular.module("app", ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider.otherwise({
controller: "editController",
controllerAs: "ec",
templateUrl: "edit.html",
resolve: {
resolveData: function() {
return {
foo: "bar",
blah: "halb"
}
}
}
});
}
]);
app.controller("editController", ["$scope", function($scope) {
// undefined
console.log($scope.$resolve);
setTimeout(function() {
// the object is there, including $scope.$resolve.resolveData
console.log($scope.$resolve);
}, 0)
}]);
If you watch the console, you'll note that $scope.$resolve is undefined when the controller is created. However, it's there immediately afterwards, as demonstrated by the setTimeout.
I can't find anything in the documentation that suggests this should be the case. Is this a bug? Or am I just not getting something fundamental about how angular works?
In case someone else comes across this - it was already reported on the angular github and resolved as intended behavior. $scope.$resolve isn't really meant to be used in controllers. You are supposed to inject it, or use $route.current.locals.
(Or you can wrap the code using $scope.$resolve in a $timeout.)
Notice your object is much bigger than you expect, and actually your object is on $resolve. This is mostly explained in the docs, however, could be more elaborate with examples...
the resolve map will be available on the scope of the route, under
$resolve
No need to dig into this, when you resolve, the named object becomes an injectable you can then place on $scope, in this case, resolveData. Observe the following...
app.controller('editController', ['$scope', 'resolveData', function($scope, resolveData) {
console.log(resolveData);
// -- $scope.data = resolveData; // -- assign it here
}]);
Plunker - updated demo
Investigation of why you are getting undefined is due to the nature of awaiting digest cycles in the framework. An acceptable workaround to get a handle on $resolve would include injecting and wrapping the call in a $timeout, which forces a digest cycle in which your object will be available in the asynchronous callback. If this approach is not ideal, forego injecting $timeout and either call $route.current.locals directly for your resolved object, or inject your object as a named parameter of which it will have resolved immediately as the example above demonstrates.

Share selected select with another controller

I am trying to to share a selected option with another controller (And have it update when I select a new option). Something that would work like the 2-way data binding between controllers.
I've attempted this by setting up a factory like so
.factory("shareObjective", function($scope){
var shareObjective = {};
return {
shareObjective: shareObjective,
};
})
Then I inject this into the controller and bind it to the model of the select like so
$scope.selectModel = shareObjective.shareObjective;
I seem to be having some trouble getting this to work. I Basically just want to share the selected option (it's .name to be precise) with another controller and am struggling to do so. My first step was to get it to share into the factory to begin with, but I seem to be having no luck attempting this. Should I be using something like the $broadcast to keep the stream of information open? Thanks!
Edit - here's a plunkr http://plnkr.co/edit/L5lz4etQ7mUEhf9viNOk?p=preview
Yes, this won't work by default because you use two different scopes thus different ngModels.
Using a service also won't help because even if a click on a select with a ngModel in one scope will trigger a digest loop in that scope, it won't trigger it in the other scope.
As you suggest yourself you need to somehow notify the other scope to update itself, and yes you can do this through events ($broadcast and $on):
Both controllers contain
<select
ng-model="foo"
ng-options="foo for foo in foos"
></select>
And this is the JS:
var foos = [
'foo1',
'foo2',
'foo3'
];
function MyCtrl($scope, $rootScope) {
$scope.foo = foos[0];
$scope.foos = foos;
// detect broadcasts from other controllers and set our value to the one we received
$rootScope.$on('foo-event', function(e, data) {
$scope.foo = data;
});
// detect when foo changes and broadcast it's value
$scope.$watch('foo', function() {
$rootScope.$broadcast('foo-event', $scope.foo);
});
}
myApp.controller('MyCtrl1', ['$scope', '$rootScope', MyCtrl]);
myApp.controller('MyCtrl2', ['$scope', '$rootScope', MyCtrl]);
Here's the fiddle.
Notice that my code does not use a single controller, just a single controller implementation. You can write your own MyCtrl1 and Myctrl2 implementations that both have this code inside.
Also, while it would look like this generates an infinite loop between $watch and $on, it does not. $scope.foo = data; does not trigger $watch,

Angular Service Injection

I am working on a trivial task (that I got working) which adds an an .active class to dom elements in a tab nav. Simple. Got it working by following https://stackoverflow.com/a/12306136/2068709.
Reading over the answer provided and comparing their controller to example controllers on angular's website I came across something interesting that I don't quite understand.
On the AngularJS website, $scope and $location (and other services) are injected into the controller along with an anonymous function which defines the controller. But on the answer, the services are not injected, rather they are passed via the anonymous function. Why does this work? Shouldn't the service always be injected?
In terms of an example: Why does this
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
}])
and this
angular.module('controllers', [])
.controller('NavigationController', function($scope, $location) {
$scope.isActive = function(route) {
return route === $location.path();
};
})
both work?
This may be trivial but its throwing my brain for a loop that I can't figure out.
The two examples are equivalent - they just make use of different syntax. The first example uses what they call "inline array annotation" (see here). The purpose of this alternate syntax is just to allow a convenient way to make the injected variable names different than the name of the dependency.
So for example, if you wanted the variable names to be "s" and "l", then you could write:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function(s, l) {
s.isActive = function(route) {
return route === l.path();
};
}]);
Actually they are injected in both cases, the difference between those two cases is in the first scenario you define and name the dependency this could be useful if you minify your js code and that way you are declaring explicitly the dependency for examply it could be:
angular.module('controllers', [])
.controller('NavigationController', ['$scope', '$location', function($s, $l) {
$s.isActive = function(route) {
return route === $l.path();
};
}])
that way angular will know which dependency to inject on which parameter without looking at the naming for each parameter.
the other case you need to be explicit and declare which dependency you'll inject by setting up the name.
I hope that helps.
This is how angular handles code minification. by keeping strings intact it can keep mapping vars when they are renamed.
if you take a look at the code of the controller https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L48
you'll see that the constructor can accept both function and array.

Categories

Resources