I'm using AngularJS and UI-Router to make a little e-commerce product gallery. It should display a grid of images representing categories of products. When the user selects a category, the grid shall display the child categories of that one, and so on, until the user reaches a level where there are only products. Clicking in a product should display its details. Im using 2 states in UI-Router, one for the gallery and one for the details.
app.config(function($stateProvider){
$stateProvider
// Gallery State
.state('gallery', {
url: '/products/:category',
templateUrl: 'views/gallery.html',
controller: 'galleryCtrl'
})
// Details State
.state('details', {
url: '/details/:id',
templateUrl: 'views/details.html',
controller: 'detailsCtrl'
});
});
In the 'gallery' state, the controller uses the :category parameter to get from the database all subcategories or products of that category to display in the grid. The 'details' state's controller uses an analog strategy to get the product's information with the :id parameter.
Now I want to make a breadcrumb element that shows the 'path' the user went through the gallery. For example, if the user selects the "Computers" category, then the "Notebooks" category and then "Foo Notebook", the breadcrumb should display:
Computers > Notebooks > Foo Notebook
I've found solutions that get the state hierarchy from UI-Router to create the breadcrumb. The problem is, with only the 2 states I created, the breadcrumb wouldn't show all categories, just the last selected one. I don't want to create a state for each category because the number and hierarchy of categories can change. Is there any way to accomplish this?
If you're really interested in documenting the way a user got somewhere: One way to go about this would be to create a service and push each state change to a data store in the service and replicated in a $cookieStore -- you can use the state change events for this.
Generally, though, whenever I've done breadcrumbs / hierarchies in the past - there is a definitive way to get to a location. Your example illustrates that; a specific notebook is under a general list category which is in turn under a parent category. So you can infer all parents when you get to a given item page. Not sure which solution best meets your needs.
Related
I am in the process of building a mobile web app with AngularJS. The app will allow the user to record the salary and other figures for employees.
On the first page the user will select the number of employees to be filled for instance 3. When the user clicks next, it will go to employee 1 then next to employee 2 and so on.
The problem is i am having these views do not exist. I will create an augular directive which will contain the elements to be captured for the employee. But then how to route to these views. It should allow to go back as well to edit for any employee.
Any advise is welcome.
Thanks,
Ashley
You can have one state with the form and another parameterised state with the employee ID.
$stateProvider .state('contacts.detail',
{
url: "/contacts/:contactId",
templateUrl: 'contacts.detail.html',
controller: function ($stateParams) {
// If we got here from a url of /contacts/42
expect($stateParams).toBe({contactId: "42"});
}
})
From one employee editing state you can navigate to the next employee editing state.
More info/examples: https://github.com/angular-ui/ui-router/wiki/URL-Routing
I'm designing a 'custom portal' and i'm stuck on how to design our application in the right way. The portal has a view as on picture below where:
Shopping carts dropdown where user can select 'current' shopping cart(can be empty)
a. Button that redirect to 'current' shopping cart details page.
Application menu. Where we can navigate f.e to Catalogs
The main application area.
I have the following workflow:
User goes to catalogs menu.
url: domain/catalogs; state: catalogs
User select a catalog and see products in the catalog
url: domain/catalogs/catalog1ID; state:catalogs.detail.
User can click on product tile and go to product detail view
url: domain/catalogs/catalog1ID/product1ID;state:catalogs.detail.product
Looks natural and nothing special for now. Now the main problem how to integrate to this workflow a 'shopping cart' functionality. I mean that if user select a shopping cart the data on views might be different(Because shopping cart related to different customers and we can show different catalogs/price/products for different customers). F.e The price for Product1 from Catalog1 might be different for shopping cart 1 and shopping cart 2 and shopping cart not selected. I'm trying to find answers for next questions:
Where to keep selected shopping cart(state/url)?
How to inform current state that shopping cart changed?
How to make F5/refresh works correctly(f.e if my current state is catalogs.detail.product and i have a shopping cart selected)?
Sounds like a lot of questions need to be answered, I'll try to provide some guidance to point you in the right direction, but of course this will need to be tailored to your application.
Always keep scope in perspective. For example, lets say your top level scope (rootScope) stores shopping cart data. Any other state within your app will have access to those rootScope properties. So if you need data that persists across multiple states, storing in something like a rootScope would give child states access. Now please note, it's probably not a good idea to be storing all your data in the app rootScope, it would make much more sense to store data in individual controllers, and give custom directives access to those controllers. This will keep things much more modular and won't pollute the global rootScope
This can easily be done by creating a function within a directive that modifies the scope that holds the shopping cart data.
Sample Directive:
app.directive('pagedirective',function(){
//directive definition object
return{
restrict:'E'
templateUrl:'path to html template'
link:function(scope){
scope.update_Shopping_Cart_In_Controller = function(item){
scope.shopping_Cart_Array.push(item)
}
}
}
})
Sample Controller:
app.controller('shoppingCartCtrl',function($scope){
//this will get updated from the directive
$scope.shopping_Cart_Arr = [];
})
main page html:
<div ng-controller="shoppingCartCtrl">
<p ng-repeat="items in shopping_Cart_Arr">{{items}}</p>
<pagedirective></pagedirective?
</div>
Directive html:
<div>
<button ng-click="update_Shopping_Cart_In_Controller('new toy')">add item</button>
</div>
Obviously you will want your directive html to be much smarter than this, but I just wanted to the pattern across of passing data to the controller from a directive.
If you refresh your a page in your app, you will lose any data that is not initialized within a controller on load. For example, if you add items to your shopping cart controller on one page, and then refresh your browser, data added to that controller will be lost. Here are a couple approaches to solving that issue.
Saving the data to sessionStorage or localStorage. This data will not be lost during refresh and keep me used to initialize data within a controller.
Save data to a user table within the backend of your app, and then retrieve that data when needed.
Let me know if anything is unclear, but hopefully this points you in the right direction.
I have a big data entry form that's defined programmatically.
I'm modeling it with a PlanController.
On launch, the PlanController loads the plan, its sections, and fields.
The PlanController's template shows:
the plan header information
a navbar loaded from the plan's sections, like this:
li.nav-item(ng-repeat="section in plan.sections | filter:{'active': '1'}" ui-sref-active="active")
a.nav-link(ui-sref="plan.section({idSection: section.idSectionDef})" ) {{sexion.title}}
This is all working great.
Note: Since I don't know what sections there are beforehand, or what the fields in them are, I decided to use a ui-router child route to display the information section information.
This where I run into problems
All the Plan information is loaded in the parent Controller. I'm not sure how to bind the child information in the child view to the parent to the parent.
First Try - no child controller
I've tried declare the child (section) state without a controller, and try to access the section information directly from the PlanController:
.state('plan.section', {
url: '/section/{idSection}',
templateUrl: 'app/plan/planSection.html'
})
but in binding the section fields in the child state's template, planSection.html :
div.col-md-4.col-sm-12
ilp-field(ng-repeat="fld in getFields($stateParams.idSection)| filter:{ layoutColumn: 10, idSectionDef: $stateParams.idSection}" datafield="fld" )
div.col-md-8.col-sm-12
ilp-field(ng-repeat="fld in getFields($stateParams.idSection) | filter:{ layoutColumn: 20}" datafield="fld" onchangecallback="checkilp")
I get the fields for the first section ($scope.getFields takes a section id and returns a list of fields that belong to that section).
When I select one of the other child sections, the url is showing the change of states, but the fields are not changing.
Second Try - child controller
I originally tried declaring the child state with its own controller:
.state('plan.section', {
url: '/section/{idSection}',
templateUrl: 'app/plan/planSection.html',
controller: 'planSectionController'
})
but I don't know how to get the list of fields from the parent to the child control, and when I try to bind to:
$state.$current.parent
as in
ilp-field(ng-repeat="fld in $state.$current.parent.getFields($stateParams.idSection)| filter:{ layoutColumn: 10, idSectionDef: $stateParams.idSection}" datafield="fld" )
I get an error that $state.$current.parent is not accessible.
Would welcome some guidance on the right design pattern to use here with ui-router
I am building an AngularJS app (using router-ui) that uses a tab style navigation. The users page, for example, will have the following tabs:
Listing (will always be the main tab and list all users. Upon selecting a user on this tab, the rest will be come active
Account Details
Contact Details
Notes
etc
Now, I am using a service called entityIdPersistenceService to remember the ID of the user selected on the listing tab. In my routes, I am doing something like the below to notify the child tabs of the selected userId (so they can grab the necessary data), however it seems like alot of repetitive code. Is there a better way to provide my selected id to all other tabs?
resolve: {
entityId: function(entityIdPersistenceService) {
return entityIdPersistenceService.getId();
}
}
It seems like alot of repetitive code. I added it to the parent state, however it looks like the code only runs once on the parent state, and not on every child state change.
Are there any better ways of doing something? Ideally id like to put this on the parent state and have it run at each route change.n
All pages related to a user should have UserId somewhere in the url and in route parameters:
.state('user.accountDetails', {
url: "/users/{userId}/account",
templateUrl: "..."
})
.state('user.contactDetails', {
url: "/users/{userId}/contact",
templateUrl: "..."
})
In this way the UserId will be stored in url instead of the service and every child controller will be able to get it via $stateParams.userId
I'm creating a true SPA application using AngularJS – there is only one view.
For each main feature of the application different panels (directives) will be displayed. Also, there are some common panels that will be shared across features. Below is an image of a contrived example. Notice that there are multiple panels on the view.
The short question is, when the user selects a specific feature (Airline, Hotels, Cars) how do I manage all the different panels that should be displayed and hidden? If this was an application with multiple views I would use AngularJS routing, but not sure if this applies to an application that only has one view.
A couple of things to keep in mind:
All my directives and services should continue to be testable
If possible, I rather not use $broadcast for communication
Should be able to use URL Routes
Are there any recommendations of how to solve this issue?
Given how you've tagged this question with ui-router shows that you're definitely on the right path. What you're looking for can be achieved in a clean manner by using multiple named views.
The short question is, when the user selects a specific feature
(Airline, Hotels, Cars) how do I manage all the different panels that
should be displayed and hidden?
Well given how you will have 3 base states, cars/hotels/airlines, then you will just show the view for that state when it is active, the multiple child views will be shown by default for that state. Look at the basic multiple named view demo:
$stateProvider
.state('cars',{
views: {
'carsFilter': {
templateUrl: 'car-filters.html',//or a common filters templare shared by all your states
controller: function($scope){}
},
'carsSearch': {
templateUrl: 'car-search.html',
controller: function($scope){}
},
'carDetails': {
templateUrl: 'car-details.html',
controller: function($scope){}
},
}
})
You would have that state for airlines and hotels, so handling the state changes is not something to worry about, unless I'm not fully understanding what you asked ;)
If this was an application with multiple views I would use AngularJS
routing, but not sure if this applies to an application that only has
one view.
Well your application is not a single view app. You have that map (could be a base abstract state shared among your child states), you have the airlines/hotels/cars views, as well as the filters/details/search result views.