i been reading about services or factories in angularjs, but im having some trouble finding the best solution, basically i need to pass some data from one controller to another controller, it is a boolean value, but i dont want to create a service for just a small task.
I believe that wouldnt make much sense for just a small job. is there other way in angularjs for this type of situations? Where i could pass small data between controllers. I been looking around in angularjs documentation, but cant figure out the best solution.
Use $rootScope.emit()
in Controller A
$rootScope.emit('toggle',true);
in Controller B
$rootScope.on('toggle', function(value){
})
For tiny items, you could use parameters. The the end user, they'd look like a part of the URL string. This is avaible with and without using ngRoute. Have a look at this stack question.
For more complex data or data that's unsuitable to displaying to an end user, use a service or factory. Seems like overkill, but it gives you greater flexibility.
A $broadcast/$on pattern just gets messy and harder to troubleshoot.
If it's parent/children controller, you can transfer your value with $scope or $emit/$broadcast.
If controllers are on the same level(siblings) you can make wrapper controller for them to store shared data.
If you think there would be similar requirements for other tasks you can consider service, and making it abstract and reusable(some sort of helper).
In any way, you didn't provide enough of info, and there could be a lot of solutions until we see real example.
If your controllers have a child parent relationship (in any level) then you can try with $broadcast or $emmit to throw the data and $on to catch that with a a event. Otherwise inject $rootScope to one of them and trigger either $broadCast on the monitor of $rootScope, or $emmit on the monitor of $scope and catch thet data using $on('eventName', function(ev, data){}) correspondingly.
Related
I have a problem I haven't come across before, and I'm completely lost as to how to solve it.
I have a container which is used to display success and error messages across a wide variety of views. This container is defined by an Angular directive.
The problem is that I would like to have a service or something similar which I can inject into my controllers. I could then use methods on this service to make the "alert box" appear and/or change in content.
How do I go about achieving this or a similarly DRY setup in Angular?
Three perfectly good methods:
Use angular's event system, rootScope.$broadcast or scope.$emit and scope.$on as listener, see the documentation here: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$broadcast
https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit
https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$on
inject the service into the "alert box" directive, let it register itself somehow with the service, that way the service knows of its existence and can control the directive's controller, or its scope, or the element directly, or whatever logic you wish to use.
Use a container as parent, that has the orchestrating logic. the child directive's can require the container controller, and call functions etc. on the container, one child directive is then able to trigger another child through this orchestrating parent container.
EDIT:
My point wasn't to show you how to do it in the most generic and cleanest way, because I don't know your exact situation, you should always consider how generic and abstracted you want your functionality.
Here is a very very simple example to showcase that it's not about finding some complex system or pattern, but finding something that works well for you
method 2 example:
angular.module('myApp').service( 'myService', myService );
function myService(){
this.alertBoxes = {};
this.registerAlertBox = function(name, handle){
this.alertBoxes[ name ] = handle;
}
this.toggleAlertBox = function(byName){
this.alertBoxes[ byName ]();
}
}
I personally would use method3 from the looks of what kind of functionality you're looking for, method 2 could be a generic pubsub service using rootScope.$broadcast and scope.$on, or not even bothering with those and doing it like a very straight forward pubsub.
method 1 really is the simplest way, but obviously would benefit from being abstracted away into a service, where you inject the service everytime you need a very specific pubsub event.
For an example of a pub/sub pattern: Pub/Sub design pattern angularjs service
Have some data for one controller JSON, I fill by this data in ng-repeat my view.
But when selecting this data from view I need pass selected data to Form Controller fill all forms.
I don't know how to do this in angularJS with components. I built service, which parse data when i select it and pass to Form Controller. But it's additional work parse it again by service.Maybe exist some more good way how pickup just id, and Form Controller load it automatically when get id's from service.
AngularJS really hard for all ways as I understood :(
It's better to provide some code samples with the question. Actually you have 3 main ways to share data between modules - parent controller, rootScope and services. Ok, there is event emitter here, but I heard opinions that it's not 'Angular way' to use events, because it's hard to maintain this architecture later. As I understood, you are using service for this porpoise and it's fine. Since Service is Singleton in Angular you can use it not only to share reusable peaces of code but and for sharing data between controllers.
I am building an app in angular, which consumes different APIs and Gives options for the user to select it will be recorded and sent back to the server.
I have designed it as follows.
All the common logic in Main Controller and all other options in different controllers as the child of main controller.
Main Controller retrieve all the data that are required to run the app.
which is consumed by all other child controllers.
To make sure data is loaded I am using promise attached to scope. So all the child controller will know data loaded.
I have moved data updation part of all child controllers to main controller
because all the updates happen in one object.
Child Controller emit/broadcast to communicate between child and main. So when update happens child will emit an event with data which will be captured by Main and it will do the update.
MainController {
$scope.loaded = DataService.get();
$scope.userOptions = {};
$scope.$on('update',function(){
updateUserOptions();
})
}
ChildController {
$scope.loaded.then(function(){
//all logic of child controller
}
$scope.onselect = function(){
$scope.$emit('update',data);
}
}
Questions
Is it a good practice to use events between controllers ?
is it good to use promise attached to scope for child controllers ?
Will it improve my code if I start using services ?
I will try to answer your question based on my own experience. Recently I've built a single page application and I've refactored its architecture.
Here are my answers:
Is it a good practice to use events between controllers? IMHO, it is the most flexible way to share information between all controllers even if they have isolated scope (using $broadcast or $emit for example). This is called the Observer design pattern. However, you can use services instead of events to share data between them. If you are going to use $rootScope, be careful as all the $scopes inherit from $rootScope.
is it good to use promise attached to scope for child controllers ? Firstly, you have to learn about how scope inheritance works. You have to take care to avoid property shadow in JS. Secondly, I would move out all the logic from scope.loaded in ChildController to a service such as ChildService. Keeping the business logic (such as request, etc) in Services instead of Controllers, will ensure it can be re-used.
Segregation of business logic is good design principle.
Will it improve my code if I start using services ? I answered this question above.
In addition, in order to build a good architecture I've read this angular style guide written by John Papa.
I recommend the following changes:
To make sure data is loaded I am using promise attached to scope. So all the child controller will know data loaded.. Instead I would emit a custom 'loaded' event in the MainController using $scope.$emit('loaded'). After that, in the ChildController I would use $scope.$on('loaded', function(){}) to handle the event.
I would move the updateUserOptions function to a service and inject the it into just the controllers that need it.
I hope that helps!
Is it a good practice to use events between controllers ? Not as the main form of data sharing, but you can use it to notify about system events, such as data ready.
Is it good to use promise attached to scope for child controllers ? Don't use scope inheritance, it causes lots of annoying problems.
Will it improve my code if I start using services ? Yep.
This is what I would do in your place:
dataService - this service is responsible for all data coming in / going out. Whenever a request for data is made (no matter which controller asked for the data), the service caches the data (save the promise is good enough). All further requests get the cached data unless they specify they want a fresh data. Whenever data is updated (1st time or refresh), the service broadcasts a 'dataReady' event via $rootScope to which the main controller and other subscribers can listen.
The service is also responsible for data updates, and when the data is updated you can also broadcast an event via the $rootScope.
When the event is activated, all subscribers query the service, and get the data they need.
Controllers - avoid controllers, use directives with isolated scope, and pass the data between them using attributes. In this way you can be sure that each directive gets what it needs, and not everything. The directives can communicate using attributes, services, broadcast / emit or require their parents / siblings if they work closely together.
Is it a good practice to use events between controllers ?
No it's not, it will be deprecated by Angular JS 2.0. It also often leads to unmanagable tangle of events which are hard to understand and debug. Use services to share data between controllers. (Inject same service into multiple controllers, service then holds data, controllers bind to that data and are automatically synchronized) I wrote a blog post explaining this use case.
Is it good to use promise attached to scope for child controllers ?
No it's not. Use promises and resolve data in services. Don't use $scope at all but use controllerAs syntax instead. $scope was deprecated also in Angular JS 1.X because it's usage leads to many different problems with scope inheritance.
Will it improve my code if I start using services ?
YES! Use services for all logic and data manipulation. Use controllers only for UI interaction and delegate everything to services. Also use ui-router for managing state of your application.
I'm not going to answer your questions directly as I have some other comments as well. I think the approach you mentioned is not the best way to build angular applications.
All the common logic in Main Controller and all other options in different controllers as the child of main controller.
It's against all angular style guides to place common logic in controllers. Controllers should only be used for the logic related to the view (data binding, validation, ...). Because the code inside a controller is not reusable, the less code you have in a controller the better. The more logic you have in services, the more scalable your application becomes.
Fix: I suggest you create a service that retrieves data from the server, and inject this service in controllers as you need. Notice also this way offers better dependency management as you can keep track of which controllers need which services exactly.
Nested controllers should be avoided when possible, because angular keeps track of all the active scopes and re-evaluates them in every $apply() loop.
Fix: same as #1, use services instead of the main controller.
To make sure data is loaded I am using promise attached to scope. So all the child controller will know data loaded.
Using a promise for data retrieval is a good practice. But, again, keeping it in a service is much cleaner than main controller.
I have moved data updation part of all child controllers to main controller because all the updates happen in one object.
Child Controller emit/broadcast to communicate between child and main. So when update happens child will emit an event with data which will be captured by Main and it will do the update.
Fix: use a service with an update function instead of events. Events are harder to debug and track. And you need to unregister event handlers on destroying a controller. If you can use a function/promise instead of events, then it's usually a better idea.
Is it a good practice to use events between controllers ?
A problem with your current set-up is that you're implicitly relying on the hierarchy of your controllers (the fact that one is the child of the other) - because you emit the event, only scopes higher up on the hierarchy can catch it. Besides being an implicit connection (that a developer has to remember), this also limits he extendability of this feature.
On the other hand, if you injected a shared service into all the controllers that need it, the connection between the controllers would become explicit and documented, and their scopes' position in the hierarchy independent. This will make your architecture easier to maintain, with the added benefit of also being easier to test, for one.
You can still implement an observer pattern with a service.
is it good to use promise attached to scope for child controllers ?
The issue of polluting scopes pointed out in other answers is valid. This is one of the reasons why it's better to limit the number of objects you attach to your scope, and to use objects as bundles of variables on your scope instead of attaching all the variables to the scope directly.
(For an explanation of these reasons, see discussions about "always having a . in your bindings".)
(Of course, don't do this blindly just to reduce the number of variables, try to find semantic connections between variables that might be bundled together sensefully.)
Will it improve my code if I start using services ?
I think the above answers already outline the answer for this: yes. There are other benefits too, but this format is not best for too long answers, so I won't list anything else now.
All in all, these above pointers are not big issues with your code currently, but if you're looking for the best architecture, I think you can do better.
Answers:
No, it will be deprecated soon.
$scope is deprecated already.
Services is a great choice. Services allow us to share data and behaviour across other objects like controllers.
I have an app with two controllers where I want a change in one controller to affect (different) data stored in the other. tl;dr when I remove a category from the first controller, I want to edit any items in the second controller with that category, so that they will now be category-less.
As far as I can tell what I want is to use a service, but I feel it would be simpler if there were a way for me to simply edit the data inside the controller scope. So my questions are:
Is there a way to simply edit controller data or call controller methods from a service?
Otherwise, is it reasonable to store the latter controller's data in the service, even though the former controller only needs access to change it? How do I reference this data for the purpose of doing ng-repeats?
Edit: to clarify the data is a set of json objects which contain data for each category and each item, and the web page contains ng-repeats to go through and list each of them. I have a number of functions which edit both lists of data, and I want changes to one list to make changes in the other.
Your idea was correct, you should put all your business logic, including data that needs to be consistent between different parts of your application, into services. controllers should only manage the view and connect the data to it.
Keep in mind that services are Singletons - there is always only one instance of each service, holding your data.
To answer your question: I would argue that storing data in a service instead of a controller is always reasonable when it works (aka when the data is not specific to one of multiple views, but consistent throughout the current application state), and giving access to that data to manipulate it is perfectly fine - even better would be to put the manipulation logic into the service itself (or another service only for that) and to just let the controller connect to a call invoking that.
There is an article by Todd Motto on that topic (thin controllers)
I think it will be better use events for this purpose. In your first controller you can published the event on category deletion like below.
$scope.deleteCategory = function (category) {
$rootScope.$broadcast("categoryDeleted", category);
}
Then you can observe this event in any controller like below in second controller you can listen categoryDeleted event.
$scope.$on("categoryDeleted", function (event, category) {
// do whatever you want
});
Do not call controller directly from the service, this is a bad practice, not only in AngularJS, but in most languages frameworks.
The problem you have described ("a change in one controller to affect (different) data stored in the other") is a problem of communication between components. You can solve this issue with events, thus there is no need to move data from the second controller to the service.
Let's consider some example:
http://jsfiddle.net/simpulton/XqDxG/
When you click on the LOG button the this.broadcastItem() is invoked, and the 'handleBroadcast' event is broadcasted.
Other constrollers, controllerOne and controllerTwo, handle this event:
$scope.$on('handleBroadcast'
and do the things they want to do.
So, in yor case, you can introduce the 'categoryRemoved' event, and broadcast this event in the first controller. Once the event is broadcasted, your second controller handle it and edit its items.
Also you should pass the removed category in the event (instead of 'msg' in the example), so that second controller has to aware which exactly category has been removed.
In way you want to do that, $rootScope can be used (shared across controllers - modyfing in one, accessing in another), but its generally bad idea. Im not sure if I get it right but its typical situation when you actually need service with controlling some external data.. Its some static json you want to modify? Can you specify it more clearly ? :)
I have an app I am designing using node/mongo/angular, what I am not getting is how is the best way to get my data from mongo into my pages? I can use node, and thru my routes send back data from mongo with my template(hogan in this case), and bind using mustachejs. That works fine for most things. I have one screen that has a decent amount of drop down lists, to bind them for an edit scenario now seems a challenge. I would like to get them bound to an angular model and go about it that way. Is it better to get the data thru the route in node, then use something like ng-init and get it into angular? Or would I be better off not getting the data thru the route in node, and then using angular to perform a "get" request and bind that way?
From the documentation of ng-init, more precisely from the red warning alert at the top of the page...:
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
So no, do not use ng-init. While that can be a good strategy for lazy migrations from regular applications to single page applications, it's a bad idea from an architectural point of view.
Most importantly, you lose two things:
An API. The benefit of SPAs is that you have an API and that you're constantly developing and maintaining it, even before it has external users
A clean separation of concerns. Views are strictly limited to presentation, can be cached by the client and all data is transferred through JSON API endpoints.
I would say that the best way to get data from Mongo into your page, is as mnemosyn said, using an API.
Basicly, you can have your API route, f.ex '/api/data' configured and then it can be used by a angular service, (which can use ngResource to make things easier). Any controller that wishes to access this data can use the angular service to get it, do some stuff with it, and then update it using the same angular service.