AngularJS Processing multiple parts of data in a controller - javascript

I'm building an app that basically calculates prices as you pick options. So basically it's a form with select options and as you go down the form, the app will process the price more refined and change the options for the other form fields.
Currently I have an API built out and in this processing, the app will need to hit the API a few different times. There will ultimately be 7-12 form fields that will make up the pricing options. While mapping out the controllers, I'm wondering what the best method to keep my processing organized. If I was doing this in a language like PHP I'd have different functions per form element and process each time the form changes.
In Angular, having one controller per view, I'm curious how you guys, the pro, would organize it..
In a non-specific language structure I'm thinking something like...
select one has 5 options, user selects one.
Using the data from step one, a calculation happens, hits the API and gets new options for select number 2. User selects options and process happens again.
So if my controller is say...
.controller("formController", function($scope){
//Function for when select one changes, listen for form change ng-change
function item1Change(){
//hit api with item1 value, get options for item2 and and load them into item2
}
function item2Change(){
//hit api with item2 value, get options for item3 and and load them into item3
}
})
To me, this seems very dirty as in I need to define stuff, it doesn't seem modular or segmented and I just feel that there's a better option. If you guys have any better ideas, let me know, THANKS!

There is no restriction regarding the number of controller available to a view. A general best practice I have come to follow* is to initialize one controller for each $scope I feel needs to be handled with more care than that available to it's parent scope or the behavior definitons for directives within the scope.
I would use:
a formCtrl
a priceCtrl
a custom directive for the calculated total
a directive strategy for selection behaviors withing the scope paired to the form controller (one or more directives)
a service that feeds option configuraitons, dependencies, etc to your controllers and directives as needed (this is a good way to decouple your data from your controller and encapsulate the handling/processing of the data. This also makes it possible for you to provide the same data to different form views if needed
You can think of your directives and directive strategy as being similar to how you would use the decorator pattern. By decorating the form input with a particular directive, it gains the ability to __
*(this is my own best practice, I can't say it's what the Angular team or community at large endorse)

Related

AngularJS transfer data between one component to another

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.

AngularJS - accessing elements inside a controller from a service

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 ? :)

Angular: How to go about refreshing service data

I am fairly new to Angular and trying to build an Angular application.
I have a lot of data that needs to be used by multiple controllers throughout the app. As I understand it, that is the perfect situation to use a service.
I am planning on storing this kind of data in services. For example I plan on having a users service which all controllers that need user data will inject.
I would like the users service to hold the master list of users and any controller that needs users to just use the one instance of service list.
I am having trouble envisioning the pattern though. I mean:
1) What is the standard way of having the service refresh its data from the server? I realize that I could just go and request the entire list of users every 10 seconds from the server but that seems kind of heavy weight...
2) Ideally I would like to be passing around only a single instance of each user. This way if it gets updated in the service, it is sure to be updated in all of the controllers. I guess the other option is to have the service broadcast an event every time it updates a user? or to use watchers?
3) What is the pattern by which the controllers interact with the service and filters? Do the controllers just request data from the service and filter it in the controller? The other option is to have the service do the filtering. If so how do I communicate the kind of filtering I need done to the service?
I think that by using some kind of solid pattern I can take care of alot of these issues (and more that I am sure will arise). Just looking for advice on some common patterns people employ when using singleton services.
Thanks in advance.
Answer to point 1. A service is just a singleton. How you store and refresh data into it has nothing to do with its nature. Not sure why you want all user data inside a service (unless you are building a user management app), but you could use several techniques like polling (eg. using $timeout ask for new users and append them to the existing ones) or push (eg. socket.io/signalR which will push you the payload of new users when available). This can be done both inside the service itself or by a controller that will add/remove data to the service (eg. a refresh users button in the UI)
Answer to point 2. You can bind/use the reference of the data inside the service directly into your controllers using a getter so that changes to the data are shown instantly (given that are two way binded, if not use events).
Answer to point 3. You can apply filters inside the controllers or in the view it self (not recommended). You can also have a function in the service where you pass the filter or filter params and get the filtered copy of the users collection back (since you will be using the users collection directly in many controllers at once you shouldn't modify that, unless that's desired). If you are reusing the same filters again and again across the controllers you can have a function for each filter that returns the filtered collection with a "hardcoded" filter. You can even have helper function in the service to help you assemble complex filters or have multiple copies of the collection already filtered cached(if you find you are using the same filter again and again)

How to make a SPA testable outside of the browser?

I have a SPA that pulls configuration data down from a server and generates a form that a user can proceed to fill out.
I want to be able to make this SPA testable outside of the browser, which would imply that the views and their implementations are not being tested.
Currently, the structure of a form is like so:
form
page 1
firstName
lastName
page 2
email
There is a form model which holds the data of the form and its configuration, and then there is a controller and a view. The controller attaches data from the model directly into the view, and also manipulates the view based on events. The view also has logic for creating and controlling certain aspects, such as inline scrollers (iScroll).
As you can probably see, this makes it impossible to test the code without the browser because the code is so tightly coupled to the view.
I think the way to fix the problem would be to have a controller, a controller model, a view, and then a concrete class that joins them together while providing the form configuration to the controller. But this raises some issues, specifically, that I would then need a factory for creating a controller for the form, pages, and fields, and then something that creates the concrete binder between the view and the controller, and then something that joins them all together in the correct way.
I feel like there has to be a better way to do this.
Any guidance would be greatly appreciated.
I think what you need is AngularJS. There is a template you can clone with git. It has testable capability right out of the box. I don't think there is a need to reinvent the wheel since there is already a well known framework that has this.

Calling into or between controllers in AngularJS

I have some fairly complicated logic in a bootstrap dialog which I've moved into its own controller for isolation.
There are times I want to launch the dialog or call a function in the controller based on some logic that occurs elsewhere in the app - in another controller, or a service. I've achieved this by adding an id to the ng-controller element then looking up the element by that id, and calling things off the .scope() of that controller. Essentially this:
In html:
<div id="modalController" ng-controller="modalController">
And in another service or controller:
angular.element("#modalController").scope().somefunction()
This seems pretty weird that I can't just get a controller by name. Is there a way to do this?
Create a service and bind the model to data maintained in that service. Make a change to the model within the service and it's made everywhere.
You could also create a service that provides a pubsub interface to the changes you need to make.
Yet another way to do it would be to have a single model representing the state of your system and modify that. Attach the relevant parts of that model to the scopes of each widget as necessary and you have a communication device built in.
It sounds like you are making a change in one place that should cause a change in another place. If that's the case, I'd argue having a service that updates all parts of the model correctly is the best way to go. Always imagine what you'd do if you added another widget that hangs off this functionality.

Categories