Many active states simultaneously with ui-router - angularjs

What would you like to be able to do for example is:
I have an active state 'order.detail' in shell, in these details would provide a link in each product line that leads to 'product.detail' state that is also a state that can be displayed in the shell.
But this link should display the state ' product.detail' as a frame in a dialog without changing the current location and maintain active state in the shell intact.
Also the 'product.detail' state, to be used as a main view of the shell, and to allow their reuse, your template should be wrapped by 'div' template of dialogue.
What I mean is, allow consult the details of something without leaving the current screen, and do so using the same existing details screen, or simply allow the 'Drill down' by related data with existing views.

Sharing state in AngularJS
One of the great things about Angular is that's it quite easy to keep track of state via providers.
For example consider one index view containing a paged grid table with many filter options. Clicking on one of the entries will take you to details view of the entry. When the user goes back from the details to the index he/she will expect that the UI state of the grid will be exactly the way they left it: same page, same sort by, same filters applied, same everything. With traditional techniques you would have to fallback on cookies, query params and/or server side state(less) magic, which all feels (and actually is) very cumbersome and error prone.
Provider values are singletons in the world of Angular, so when we inject the instance in one of the controllers, it will always be the same instance. Controllers on the other hand will be recreated each time one is requested.
Example
Register an empty object to keep track of controllers:
myApp.value('formState', {});
Create a controller, inject the provider value and expose it on the scope:
myApp.controller('MyController', function($scope, formState) {
$scope.formState = formState;
});
Hook any property of the provider value to input elements via the ng-model directive.
<input type="text" ngModel="formState.searchFilter"/>
Now every time the user will leave and re-enters this view the state of the UI is kept intact. You can add as many data to the state as you see fit and maybe even share it among multiple controllers if needed.
Provider types
There are different ways to create provider values: factory, service, value, constant and provider. If you want more control over the state, eg state management, you could use one of the other options. More info can be found here.
To dialog or not to dialog
In traditional websites displaying the details in a dialog was a "cheap" trick to keep track of the state in the background. Of course this is still an option with Angular, but there's no need for it. From the UX view, dialogs are "not done" and should be avoided if possible, but it also introduces pains in the areas of responsiveness and printing.
Plunker examples
Some code examples sharing state among controllers/views.
http://plnkr.co/edit/MwJrk5?p=preview
http://plnkr.co/edit/bNJtOP?p=preview

Related

Angularjs ui-route correct implementation for multiple views

I would like to implement a page like this
I have the following folder structure:
-root/
--root.controller.js
--root.module.js
--root.routes.js
--root.html
--filters/
----filters.controller.js
----filters.module.js
----filters.routes.js
----filters.html
--tableData/
----tableData.controller.js
----tableData.module.js
----tableData.routes.js
----tableData.html
--graph/
----graph.controller.js
----graph.module.js
----graph.routes.js
----graph.html
I would like to know the best approach in order to manage call from each controller in the best way.
I mean, from filters, I can change selection on dropdown or update other fields values, and then I would update data on tabledata. And if I change selection on tabledata I would update graph.
Root url is '/root/{itemId}' and I cannot add some other value on querystring.
How can I manage internal variables and methods?
thanks a lot
From my understanding, it's better to go for events $emit , $broadcast & on to achieve the same.
As I can see, you are having independent controllers with no relationship with most of the the other controllers.
Few suggestions to implement it:
use $rootScope.$emit and $rootScope.on if they controllers have no relationship. Make sure you are removing it manually though (can be ignored if the app is not too heavy but should be kept in mind). This link will be surely helpful . Eg:
If the filter is changed, an event filter-changed will be triggered. The graph.controller and table.controller will be informed and will render the UI accordingly.
Create a messaging service from where the controllers will subscribe and unsubscribe the events. That will keep numbers of events in check. You'll be aware of how many events have actually been created in the application.

Cordova - Angular - <select> tag open a new view

I have an Angular/Cordova app and I'm trying to figure out how should I handle the HTML SELECT tag. What I would like to do is to open a new window with all the options in a list, the user picks one and returns with that value.
The problem is when I do that I lose all the data I had in the first screen as I am closing it when I move to the second one.
I am using Angular's UI.ROUTER. One thing, which I am not too convinced to do, is to save all data entered into StateParams, and when I return, place it back.
What would be the best approach?
It really depends on the use case. If you need to be able to "deep link" to the view where a link loads the view with the pop-up active then using ui-router and stateparams makes the most sense. If deep linking isn't a concern and the user must always select something then you can just use a service/factory/value/provider in order to share the data between the controllers during the lifetime of the app.

Angular sidebar search directive

In my angular application I have a global sidebar navigation directive which among other things provides a global search for the user (with bunch of criteria, not just a text field).
Upon searching I'd like to show a page with the search results.
Now, the sidebar is defined in the main app html, what is the best way of sharing the search results data?
Should the sidebar be in charge of performing the search? If so how do I share it's data to the specific page results?
Or on the other hand, perhaps the specific search results page should be in charge of this data? If so how do I connect it with the sidebar search parameters when performing a search?
Any best practices of this scenario are appreciated.
Steps to make your future bright:
Separate your search module in 3 modules: main, sidebar, results
Translate data between each of them with one major SearchResultsService that will:
a) acquire collection of sidebar filters with true or false for each key (key as name for GET param that will be used for passing to search API of your back-end);
b) serialize or deserialize data depending on results module approach;
c) do some pagination;
d) hold or even cache data if you need (for infinite scroll functionality);
sidebar and results will be views of main (child modules), so you will be able to share main controller methods if needed (noob way)
When I was facing implementation of such module I've used black magic to escape $watch and $on event system. If you are young - then use $on that will allow you to notify each of modules about something important (such pagination change, item selection, etc.) and keep them separated same time.
You are free to place your existing sidebar in main module but I'd moved from directive to view with own controller and template.
Directives are used for reusable items either for pasting functionality. But dat sidebar obviously should be defined as separate part of app (aka module) but not as directive.
P.S. Keep your controllers simple.
Google list:
Multiply satisfection
Your golden chest
Root of AngularJS evil
Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.
https://docs.angularjs.org/guide/services

is it right to do using ui-router to activate menu items. Any advices to help me understand this?

What I did is:
When a menu item is clicked, a action will be done, like deleting a user, sending emails to a group, etc. To this end, for each menu item, I define a ui-router state, and use the state url to activate the state via sref. I thought that a menu action is just a UI component for user to let users to do something, which is just a state of UI.
I was advised that I was using ui-router in a wrong way as a state url can not identify an action. For example, to delete a group of users, the state url can not tell you what group of users have been deleted.
In short, I agree with your manager while being an angular newbie myself. Angular routes are designed for managing different views of your app. I.e. define a route and corresponding view template for each view. If you add application logic into the routes, your application structure gets quickly a mess and difficult to keep clear.
To me it is much more natural that the views are managed by the routes, and each action in each view is handled by the controller of that view. If the actions grow "big", then it is worth refactoring parts of the controller into separate services. If you require some sort of "dynamic HTML" depending on the action, e.g. bootstrap modals are handy for doing that within the current view (see http://angular-ui.github.io/bootstrap/).
E.g. in my current project, I don't actually manually edit the routes at all but let yeoman angular generator to do that for me free of charge - i.e. I instantiate each new view in my dev.env using the following command (more info on this from https://github.com/yeoman/generator-angular)
yo angular:route myNewView
More info on angular philosophy can be read from angular documentation for developers: https://docs.angularjs.org/guide/concepts
You should probably be doing this actions via a method on $scope.
$scope.deleteItem = function (items) {
Service.delete(items);
};
// which is the same as:
$scope.deleteItem = Service.delete;
<a ng-click="deleteItem(item)">Delete This Item</a>
Having it in the URL just seems wrong. I mean what does that look like? www.mysite.com/delete/users?

Having a set of checkboxes map to a nested array

I am working on a SPA that pulls in customer data from one $resource call, and gets some generic preference data from another $resource call.
The preference data is sent as an array, which I want to use to populate a series of checkboxes, like so:
<div ng-repeat="pref in fieldMappings.mealPrefs">
<input type="checkbox"
id="pref_{{$index}}"
ng-model="customer.mealPrefs"
ng-true-value="{{pref.name}}" />
<label class="checkbox-label">{{pref.name}}</label>
</div>
When a user clicks one or more checkboxes, I want the values represented in that array of checkboxes to be mapped to an array nested inside a customer object, like so:
.controller( 'AppCtrl', function ( $scope, titleService, AccountDataService ) {
// this is actually loaded via $resource call in real app
$scope.customer = {
"name": "Bob",
"mealPrefs":["1", "3"]
};
// this is actually loaded via $resource call in real app
$scope.fieldMappings.mealPrefs = [
{'id':"1", 'name':"Meat"},
{'id':"2", 'name':"Veggies"},
{'id':"3", 'name':"Fruit"},
{'id':"4", 'name':"None"}
];
});
I have tried setting up ng-click events to kick off functions in the controller to manually handle the logic of filling the correct part of the customer object model, and $watches to do the same. While I have had some success there, I have around 2 dozen different checkbox groups that need to be handled somehow (the actual SPA is huge), and I would love to implement this functionality in a way that is very clean and repeatable, without duplicating lots of click handlers and setting up lots of $watches on temporary arrays of values. Anyone in the community already solved this in a way that they feel is pretty 'best practice'?
I apologize if this is a repeat - I've looked at about a dozen or more SO answers around angular checkboxes, and have not found one that is pulling values from one object model, and stuffing them in another. Any help would be appreciated.
On a side-note, I'm very new to plunkr (http://plnkr.co/edit/xDjkY3i0pI010Em0Fi1L?p=preview) - I tried setting up an example to make it easier for folks answer my question, but can't get that working. If anyone wants to weigh in on that, I'll set up a second question and I'll accept that answer as well! :)
Here is a JSFiddle I put together that shows what you want to do. http://jsfiddle.net/zargyle/t7kr8/
It uses a directive, and a copy of the object to display if changes were made.
I would use a directive for the checkbox. You can set the customer.mealPrefs from the directive. In the checkbox directive's link function, bind to the "change" event and call a function that iterates over the customer's mealPrefs array and either adds or removes the id of the checkbox that is being changed.
I took your code and wrote this example: http://plnkr.co/edit/nV4fQq?p=preview

Resources