Angular - Passing data - Services vs URL parameters vs $scope - angularjs

I'm new to Angular and I guess this question may already have a fair few comments however I'd like to know what experienced Angular developers find the best practice on how to handle:
Passing data between pages (assuming each page has its own controller).
So the 3 ways I can see:
1 - In the URL parameters (I don't prefer this - doesnt give great flexibility, also in my opinion doesn't look so good)
2 - Create a service (e.g. as described here AngularJS - Passing data between pages)
3 - Using parent '$scope'
Thanks.

Most of the time you will want to use FACTORY because it has:
ability to use other services (have dependencies)
service initialization
delayed/lazy initialization
Factory example (Fiddle)
<div ng-app="myApp">
<div ng-controller="FirstCtrl">
<input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
<br>Input is : <strong>{{Data.FirstName}}</strong><!-- Successfully updates here -->
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{Data.FirstName}}<!-- How do I automatically updated it here? -->
</div>
</div>
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
return { FirstName: '' };
});
myApp.controller('FirstCtrl', function( $scope, Data ){
$scope.Data = Data;
});
myApp.controller('SecondCtrl', function( $scope, Data ){
$scope.Data = Data;
});
Using $broadcast
You can also use $broadcast to pass data from the high tier of your controller to the end.(Example: jsFiddle)

No 2 is the most preferable way as I always use that in my projects

2 - Create a service (e.g. as described here AngularJS - Passing data between pages)
This is the most Angular-ish way and as you've pointed out, is recommended by their docs.
But there are a few things to ponder, much of which is dependent on a) your development experience and b) what development paradigms you're used to. As a bit of background, I came from a very Java-ish background (I was a Flex/Actionscript developer for nearly 10 years). Many of the typical Java-esque development paradigms do not manifest themselves clearly in Angular. Angular seems to me to come from a more Ruby-like paradigm. This is exemplified in many of their APIs and usage recommendations.
One thing I've found out is that rolling your own solution to bypass some of the things that may be distasteful to your development proclivities is often more work in the long run. I can give you about 6-7 examples (in the comments if you wish) in which my inexperience in using Angular and my hardheadedness in being an experienced developer ended up with me finally refactoring to be more Angular.
So to recap, in the case of construct-to-construct communication, I'd go with option #2. I'd elect for an event bus as a secondary option only if the recommended option doesn't suit your needs.

It depends on what the data you are passing is for.
If there is some data that determines that state of your controller and should be linkable by users, e.g. you have a page that displays a blog article and it needs an ID to get the article info from a database, then that id should be a url parameter e.g. /blog/:id.
If you need some data that belongs to the parent controller, e.g. you have a blog article page and a child part of that page is an author panel that needs to get some information from the blog object itself, then you could access that via the parent $scope, however you should note that this tightly couples the author panel to the blog article controller, and it is almost always a better idea to create a directive and pass the blog object in to the directive isolate scope.
If you have some global data, e.g. a logged in user has a language setting which needs to be accessed by all pages, then you should use a service.
If you need some data that is to do with an event, e.g. a user clicks something and a directive somewhere else needs to know about it, then you can use $emit or $broadcast

Related

I am understanding rootScope and how it works. [duplicate]

We have a large Angularjs 1.6 application that has $rootscope scattered throughout the app in over 200 places in filters, services, routes, etc.. so it needs to be refactored, but I'm not sure how to know when to remove it. When is it a best practice to use $rootscope in the application?
I've read everything from never, to using it for storing variables, which I assumed was for sharing data between controllers. I've since read that it's better to use factories/services for this use case instead and I also read that one valid use case is to use $rootscope as a global event bus.
I didn't really see this explained in the Angularjs docs.
From ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app
element to the $rootScope. This $rootScope is the eventual parent of all $scope objects.
The $rootScope object is the closest object we have to the global context in an
Angular app. It’s a bad idea to attach too much logic to this global context, in the
same way that it’s not a good idea to dirty the JavaScript global scope.
You are right, you should definitely use Services to share data and logic between your modules.
Putting a lot of logic in your $rootScope means having bad maintainability and modularity in your application, it is also very difficult to test issues.
I highly suggest you to take a look at:
Services AngularJS Documentation
Thinkster brilliant article on how to share data between controllers
Screencast by Simpulton
#Breck421 answer to this question
I know it may be easy to attach everything to $rootScope, but It is just difficult to work on it, make little changes, reusing your code for other applications or modules and test your application in general.
EDIT
Recently I had to fetch some items from API and catch these items in order to show them in a certain view. The item fetching mechanism was in a certain Factory, while the mechanism to format and show the items was in a Controller.
So, I had to emit an event in the Factory when items got fetched and catch this event in the Controller.
$rootScope way
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
It clearly worked but I didn't really like to use $rootScope and I've also noticed that the performance of that task were pretty miserable.
Then I tried giving a shot to Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP -
written in JavaScript. Postal.js runs in the browser, or on the server
using node.js. It takes the familiar "eventing-style" paradigm (of
which most JavaScript developers are familiar) and extends it by
providing "broker" and subscriber implementations which are more
sophisticated than what you typically find in simple event
emitting/aggregation.
I tried using Postal.js for this kind of needs and I found out that it is really faster than using $rootScope for this purpose.
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
I hope I've been helpful.
From Angluar docs: Every application has a single root scope. All other scopes are descendant scopes of the root scope. Scopes provide separation between the model and the view, via a mechanism for watching the model for changes.
Of course this is going to come down to a matter of opinion and style. I tend to follow a style very close to John Papa's Angular Style Guide.
In keeping with the two, and following a good separation of concerns strategy my architecture contains factory models that are shared across the application. My controllers in turn are all bound to the services that hold the shared data.
Using $rootScope as the global event bus is exactly how Angular uses it. Should you tag along and do the same? I don't see why not. But if you are, make sure that the purpose is clearly defined and maybe even use your own service to register events to the global event bus. That way you are decoupling your app from Angular, and if you ever decide that you want to change the framework in which your global event bus lives then you can change it in one place.
This is what I'm suggesting:
Global event bus
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
Global data models
My data models are smart and tend to contain functions that provide information about themselves or retrieve/return specific data.
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
The controller
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here is a fun post about binding to services and not to service properties.
All in all you have to be the judge of what works best for you. A good system architecture and good style have saved me countless hours of solving completely avoidable problems.
After doing some more work with Angular and more reading I found this basic rule of thumb for using $rootscope that I wanted to add to the other answers:
Only add properties that are static or constant. Anything else that
represents a changing state or a mutable value should have a
corresponding directive or controller to handle it.

Best practice for using $rootscope in an Angularjs application?

We have a large Angularjs 1.6 application that has $rootscope scattered throughout the app in over 200 places in filters, services, routes, etc.. so it needs to be refactored, but I'm not sure how to know when to remove it. When is it a best practice to use $rootscope in the application?
I've read everything from never, to using it for storing variables, which I assumed was for sharing data between controllers. I've since read that it's better to use factories/services for this use case instead and I also read that one valid use case is to use $rootscope as a global event bus.
I didn't really see this explained in the Angularjs docs.
From ng-book:
When Angular starts to run and generate the view, it will create a binding from the root ng-app
element to the $rootScope. This $rootScope is the eventual parent of all $scope objects.
The $rootScope object is the closest object we have to the global context in an
Angular app. It’s a bad idea to attach too much logic to this global context, in the
same way that it’s not a good idea to dirty the JavaScript global scope.
You are right, you should definitely use Services to share data and logic between your modules.
Putting a lot of logic in your $rootScope means having bad maintainability and modularity in your application, it is also very difficult to test issues.
I highly suggest you to take a look at:
Services AngularJS Documentation
Thinkster brilliant article on how to share data between controllers
Screencast by Simpulton
#Breck421 answer to this question
I know it may be easy to attach everything to $rootScope, but It is just difficult to work on it, make little changes, reusing your code for other applications or modules and test your application in general.
EDIT
Recently I had to fetch some items from API and catch these items in order to show them in a certain view. The item fetching mechanism was in a certain Factory, while the mechanism to format and show the items was in a Controller.
So, I had to emit an event in the Factory when items got fetched and catch this event in the Controller.
$rootScope way
//Factory
$rootScope.$broadcast('refreshItems', items);
//Controller
$scope.$on('refreshItems', doSomething());
It clearly worked but I didn't really like to use $rootScope and I've also noticed that the performance of that task were pretty miserable.
Then I tried giving a shot to Postal.js:
Postal.js is an in-memory message bus - very loosely inspired by AMQP -
written in JavaScript. Postal.js runs in the browser, or on the server
using node.js. It takes the familiar "eventing-style" paradigm (of
which most JavaScript developers are familiar) and extends it by
providing "broker" and subscriber implementations which are more
sophisticated than what you typically find in simple event
emitting/aggregation.
I tried using Postal.js for this kind of needs and I found out that it is really faster than using $rootScope for this purpose.
//Factory
$scope.$bus.publish({
channel : 'reloadItems',
topic : 'reloadItems'
data : items
);
//Controller
$scope.$bus.subscribe({
channel : 'reloadItems',
topic : 'reloadItems',
callback : function () {
resetAndLoadItems();
}
});
I hope I've been helpful.
From Angluar docs: Every application has a single root scope. All other scopes are descendant scopes of the root scope. Scopes provide separation between the model and the view, via a mechanism for watching the model for changes.
Of course this is going to come down to a matter of opinion and style. I tend to follow a style very close to John Papa's Angular Style Guide.
In keeping with the two, and following a good separation of concerns strategy my architecture contains factory models that are shared across the application. My controllers in turn are all bound to the services that hold the shared data.
Using $rootScope as the global event bus is exactly how Angular uses it. Should you tag along and do the same? I don't see why not. But if you are, make sure that the purpose is clearly defined and maybe even use your own service to register events to the global event bus. That way you are decoupling your app from Angular, and if you ever decide that you want to change the framework in which your global event bus lives then you can change it in one place.
This is what I'm suggesting:
Global event bus
// Angular specific: add service to module
angular.module('app').factory('globalEventBus', GlobalEventBus);
// Angular specific: inject dependencies
GlobalEventBus.$inject(['$rootScope']);
// Non framework specific.
// param: fameworkEventBus will be $rootScope once injected
function GlobalEventBus(fameworkEventBus) {
var globalEventBus = this;
globalEventBus.registerEvent(params...){
fameworkEventBus.
}
return globalEventBus;
}
Global data models
My data models are smart and tend to contain functions that provide information about themselves or retrieve/return specific data.
// Angular specific: add service to module
angular.module('app').factory('dataModel', DataModel);
function DataModel() {
var dataModel= this;
dataModel.myData = {};
dataModel.GetSpecificData = funtion(param){
return ...
}
return dataModel;
}
The controller
// Angular specific
angular.module('app').controller('MyController', MyController);
// Angular specific: inject dependencies to controller
MyController.$inject = ['dataModel'];
// By convention I use the same parameter name as the service.
// It helps me see quickly if my order of injection is correct
function MyController(dataModel) {
var myController = this;
// Bind to the service itself, and NOT to the service data property
myController.myData = dataModel;
myController.doStuff = function(){
}
}
Here is a fun post about binding to services and not to service properties.
All in all you have to be the judge of what works best for you. A good system architecture and good style have saved me countless hours of solving completely avoidable problems.
After doing some more work with Angular and more reading I found this basic rule of thumb for using $rootscope that I wanted to add to the other answers:
Only add properties that are static or constant. Anything else that
represents a changing state or a mutable value should have a
corresponding directive or controller to handle it.

Correct method for using/implementing services

We're trying to lock down the best method for using services and have come up with three possibilities. This is a much smaller example of a much bigger problem but I hope it gets the point accross. The service in these examples is very simple:
.service("markerService", function(){
var self = this;
self.showMarkers = false;
self.toggleMarkers = function(value){
self.showMarkers = angular.isDefined(value) ? !!value : !self.showMarkers;
};
})
And the dom after it's compiled is also very simple:
<div ng-controller="ParentCtrl" class="ng-scope">
<div class="assetsDirective">
<div>
<div class="assetDirective">Hidden Name: 1</div>
<div class="assetDirective">Hidden Name: 2</div>
</div>
</div>
<div class="toolBarDirective">
<div>
<button>Toggle Markers</button>
</div>
</div>
</div>
The options we've come up with are:
Option A: http://jsfiddle.net/nxhn31yv/ (both assets and toolbar have service)
Option B: http://jsfiddle.net/oszop5j4/ (assets has service, parent of toolbar has service and toolbar updates it)
Option C: http://jsfiddle.net/a7nf31d3/ (Parent of all of them has service, passes it down)
The first one injects the server into two sibling controllers and one of them calls a toggle function to update the value stored within that service.
The second option has it injected into two services that have a parent/child relationship. The parent passes the value to one child which can update it (like an ngmodel) while the child controller with the injected service passes it's value onto it's children which use it.
The third option has the parent pass the value all the way through to both siblings, which in turn has the one sibling pass it to it's children as well.
Which of these fall in line with angulars methodology of how to implement services?
Option A is the clear winner for me. It more closely matches the idea of discrete, decoupled web components - the direction in which Angular and web development generally seems to be heading.
For Angular 1.x this means using directives to encapsulate all markup/logic that is bound together. Typically ng-controller in your markup should NOT be required. It should be possible to identify the dependencies (injected or related directives) for any directive by looking ONLY at the declaration of that directive.
Option B and C violate these simple principles and therefore (assuming actual implementations are more complex than these simple examples) are likely to be much more painful in terms of maintenance and debugging.
I would say option A is the best way to do it.
I believe one of the greatest things Angular teaches you is how to modularize your code. Programming in such a way where if you wanted to, you could remove, add any piece anywhere. So with that, you would try to avoid using anything that is dependent upon another piece. Your Option A succeeds in this way.
Of course sometimes it is unavoidable, but I use that as a general rule. It makes things easier to understand and follow, keeps things clean.
edit: It also protects yourself from changes to scope in the future.
According to Pro Javascript Development by Den Odell,
applying a set of methods and properties from one Object directly to another or a prototype of a Class can be a "hack", especially for that developers approaching Angular from Object Oriented Background.
Neverthless, when it is being used carefully, can simplify development and code mainteance.
That is a mixin design pattern, which avoids extensing subclassing and inheritance chains.
Injecting service into encapsulated directives, will help you avoid parent controllers and inheritance.
That is why i choose option A, but use it carefully.
More about mixin.
Another article

Do native (non-$scope, etc) members of angularjs controller objects do anything?

Learning angularjs, the curve is steep but I can see that it's going somewhere.
Given the following:
var app = angular.module('theApp',[]);
app.controller('Controller1', function($scope) {
var self=this;
$scope.thing = "hi.";
this.thang = "yo.";
$scope.doIt=function(){
return self.thang;
};
});
thing and doIt() are exposed through $scope, but thang is not:
<body ng-app="theApp">
<div ng-controller="Controller1">
<div>{{thing}}</div>
<div>{{thang}}</div>
<div>{{doIt()}}</div>
</div>
</body>
Is there any way to bind or otherwise expose thang? Or is this. completely pointless inside an ngController?
No problem if it is, just trying to get it straight in my head. I'm getting the idea that and ngController only communicates through $scope (and other ng service objects), they aren't independently accessible to other code; and I see that as a basically good thing, within anjularjs.
But it means you have to go all-in with angularjs; legacy/external code has to be wrapped in angularjs services and stuff in order to communicate across an ngController, would that be a fair statement?
I'm using angularjs 1.2.13, does the behavior change across versions?
Is there any way to bind or otherwise expose thang?
It depends how you want it to be exposed. If you want it to be available in the view; you'll have to put it into the $scope.
If you want to share the value with other controllers, you'll probably want to use a service or to store the value.
Or is this. completely pointless inside an ngController?
I consider variables, or methods, not in the $scope to be similar to a protected variable [or method]. Depending what your building their can be uses for that. I would not consider it pointless.
But it means you have to go all-in with angularjs; legacy/external
code has to be wrapped in angularjs services and stuff in order to
communicate across an ngController, would that be a fair statement?
No, it doesn't mean that. For example, I once wrote an app with a login form, and needed to hash the password before calling a remote service. I just Googled and found a JAvaScript hash library; then imported it into the html page (AKA Used the script tag), and was able to access the hash function from within the controller without doing any other work. This is due to the nature of JavaScript and how browsers work.
However, by doing this I added an external dependency into my controller which would make it difficult to write tests against. If I had wrapped the library in an Angular service, then passed it into the controller I would have built a semi-self-documenting API for the controller that could be tested with known dependencies.
So, you don't have to wrap external services in AngularJS. But, you may want to. As long as you understand the trade offs your making, you can make good decisions for your use case. (Just like any 'best practice').
I'm using angularjs 1.2.13, does the behavior change across versions?
All versions of Angular I've used have the same "Controller / Scope / Dependency Injection" approach.
The ng-controller tag can put the controller on the scope. These are equivalent:
<body ng-app="theApp">
<div ng-controller="Controller1 as c">
<div>{{thing}}</div>
<div>{{c.thang}}</div>
<div>{{doIt()}}</div>
</div>
</body>
And:
app.controller('Controller1', function($scope) {
var self=this;
$scope.thing = "hi.";
$scope.c = this;
this.thang = "yo.";
$scope.doIt=function(){
return self.thang;
};
});
Personally I think putting the name in the tag usually makes more sense as the template reads better.
Remember you can also access controllers which aren't on $scope from the link function of a directive. When you do that you refer to the name you gave the controller when you registered it, Controller1 in this case. See the require attribute here: http://docs.angularjs.org/guide/directive

AngularJS : Service vs Inheritance for Access Parameters Outside `ng-view`

Is it more effective to use a Service or rely on Scope Inheritance when updating parameters outside an AngularJS ng-view?
I have an AngularJS page with an ng-view and a common header. When moving between the ng-views I would like to:
update the common header title
select the appropriate navigation menu item.
In order to do this I've read two common solutions. The first is to provide a Service that can pass variables between controllers. A simple and straight-forward solution described in many places, including here: AngularJS: How can I pass variables between controllers?
The second method I've found was to take advantage of Scope Inheritance and wrap my ng-view in a parent controller, like so:
<body ng-controller="MainCtrl">
<!-- stuff -->
<h1>{{ common.pageTitle }}</h1>
<ng-view></ng-view>
<!-- more stuff -->
</body>
Then in my controllers I can do the following:
myApp.controller('MainCtrl', function ($scope)
{
$scope.common = [];
$scope.common.pageTitle = "Set by MainCtrl";
});
myApp.controller('SplashCtrl', function ($scope)
{
$scope.common.pageTitle = "Set by SplashCtrl!"
});
Is there a performance hit between using one over the other? Am I going to discover a hidden "gotcha" if I use one, that I haven't realized yet?
I recommend using services or an even pattern over inheritance. It's more difficult to 'figure out' where things are coming from and what is modifying what unless you have some explicit way of stating that
As far a performance goes, I'm not sure what would be faster, however don't worry about performance until you need to, then look for a solution to your specific performance issue
If you wanted to go the event pattern route then you could have your controller broadcast it did something
$rootScope.broadcast('eventname', { event data });
and consume the message like this in your menu/main controller
$scope.$on('eventname', function(event, data){
// update menu state...
});
The hidden 'gotcha' for me was after going several levels deep in scope inheritance I found that when I started writing my test code I had to instantiate all of the 'parent' controllers and my tests got overly complex very quick

Resources