How can I reuse an angularJS UI View in other ui-view - angularjs

So I want to have two big pages. I cannot use ui-view because many widgets/ sections of the views are shared (they remain on the screen without loading). The moment a user clicks on a button, the app should move to a different state with merely other sections changing with some animations.
Now, what I tried till now is nested views. But these are only one directional top down architecture. I could not find a way to share views.
I am not using mere ng-include or a directive because I want it to follow a state by state approach keeping intact the history with a URL mapped to it's own template.
The chances are many views are going to reuse/ share the inner views like widgets.
Config
$stateProvider.state('dashboard',{
url : "/dashboard",
views : {
'dashboard' : {
templateUrl: './templates/dashboard.html'
}
}
}).state('dashboard.personalDetails', {
url : '/personalDetails',
views : {
'main' : {
templateUrl: "./templates/personalDetails.html"
}
}
}).state('shipment',{
url : '/personalDetails',
views : {
localView1 : {}
localView2 : //reuse the personal details view
}
})
This is just one example, I basically want to reuse my views as widgets across many pages. Is it possible with ui-router? If yes, then how?
PS: Don't go on this specific example. The actual implementation is for a trading detail page in banking application. And due to policies I cannot share that code structure.

Not really sure what you are asking however you can always create objects to represent similar views and pass those objects to a state definition
var subviews_1={
'widget_1' {templateUrl: '..'},
'widget_2' {templateUrl: '..'},
}
$stateProvider.state('shipment',{
url : '/personalDetails',
views : subviews_1
})

Related

Angular UI-Router Best Practices: Share Data Between named views

I'm migrating from angular-route to ui-router and Im trying to figure out what is the best option for build the following scenario:
I have a generic panel wich is composed of two nested elements showed at the same time: A FORM(rendered on the left) and a simple LIST (rendered on the right).
The form is used to edit or even create a new item for the list (this manipulation is made throught a service makes ajax calls)
The list shows all items and I can select one of them and click on "edit", after that the item goes to the form and I'll be able to manipulate/update it.
Here is my code:
.state('mt.demos', {
abstract: true,
url: '/demos',
views : {
'container#' : {
templateUrl: '....demos.html'
}
}
})
.state('mt.panel', {
url: '',
views: {
'form#mt.demos': {
templateUrl: '...form.html',
controller : 'formController',
controllerAs : 'vm',
},
'list#mt.demos': {
templateUrl: '...list.html',
controller : 'listController',
controllerAs : 'vm',
}
}
})
So far I ended up with those 2 options:
Create a service for the shared data (this seems to be the most acceptable answer due to its Singleton behavior -- but look the NOTE 1 at the end of this post.
Create a parent controller so each nested state can access parent properts like the list or the item to be edited. So when I click on "edit item" the listController updates the parent's scope and the formController will be changed with this data.
NOTE 1: Of course to retrieve the list and to update items I already use one service called demoService. This service is used for ajax requests only. So If the best decision is the options 1 showed above, I belive that I should create a new service with a new proposal, right? Or it isnt bad to store some local variable in this same service.
This new service will have the list and the item itself, so when I click on edit I will update the service "item" property?
I think you already have an answer of using a service to store your data.
About a new service with new proposal, while I'm not entirely sure what a 'proposal' means, I'm assuming you are referring to a different list with different fields with your current one?
In that case that would depends on how reusable your code is. If your forms are similar enough, or you can generalize it, then it might be fine to share the same service so you don't have to rewrite the functions. The lists and selected items can be stored as different variables in the service.
Or, you can also store them in different service, and write their common functions in another service.

angular ui route nested

Hi I'm trying to create a nested route inside an already nested route. In synthesis, I have a portal which contains my sidebar and some other fancy stuff and there I can navigate to clusters. From clusters I should be able to select one cluster and view its details.
My "grandparent" route is called "portal" and the parent "clusters". Here's the code for clusters-route
(function () {
'use strict';
angular.module('ap.clusters.RouteConfig', ['blocks.router','ncy-angular-breadcrumb', 'ap.clusters.ClusterDetailsCtrl'])
.run(onRun);
/* #ngInject */
function onRun(routerHelper) {
routerHelper.configureStates(getStates());
}
function getStates() {
return [
{
state: 'portal.clusters',
config: {
url: '/clusters',
templateUrl: '/views/clusters/clusters.html',
controller: 'clustersCtrl',
controllerAs: 'vm',
title: 'Clusters',
ncyBreadcrumb: {
label: 'Clusters'
},
settings: {
nav: 4,
content: '<i class="fa fa-cubes nav-icon"></i>a<span>Clusters</span>'
}
}
}
];
}
})();
Here's the nested route I'm trying to include:
(function () {
'use strict';
angular.module('ap.clusters.clusterDetailsRouteConfig', ['blocks.router', 'ncy-angular-breadcrumb', 'ap.clusters.ClusterDetailsCtrl'])
.run(onRun);
/* #ngInject */
function onRun(routerHelper) {
routerHelper.configureStates(getStates());
}
function getStates() {
return [
{
state: 'portal.clusters.cluster-details',
config: {
url: '/cluster/:id',
templateUrl: '/views/clusters-details/clusters-details.html',
controller: 'clusterDetailsCtrl',
controllerAs: 'vm',
title: 'Cluster',
ncyBreadcrumb: {
label: '{{vm.cluster.name}}'
}
}
}
];
}
})();
The routing seems to be working fine, since the url is showing ....portal/clusters/cluster/0 or whatever index I select, but the html is not rendering. When I click the link just the url changes. Here's how i call the routing from clusters view (using jade)
a(ui-sref="portal.clusters.cluster-details({id: $index})")
Don't really know whats wrong with it, why isn't the html showing
I recently inherited this project that anram posted the question about. The issue he describes actually arose due to the nature of the site. The site actually has some fairly complex routing and breadcrumb needs (complex from a development standpoint, rather simple and strait forward from a usability standpoint). For more details on that, and a breadcrumb generation issue I am having with this same project, please read my question here.
In the specific case that anram was asking about, we have a portal layout with a banner bar, sidebar menu, table listing views, and detail views. Every one of the listing views can be navigated to directly from the sidebar menu. The detail views can all be navigated to via two means, either from the listing views reachable via the sidebar menu, or by child listing views from some virtual "parent" detail view. There are a number of different levels in this virtual hierarchy, so the navigation paths to get down to the lowest leaf level can be quite short, or quite long, depending on exactly how you navigate.
The problem that anram encountered was due to the fact that all detail views were originally configured to be child states of the listing states in ui-router, when in actuality they were not. They were siblings in the literal sense. A detail view was not a child view embedded in a <div ui-view /> somewhere on the listing views...they were entirely separate views. A such, the name portal.clusters.cluster-detail was incorrect. It implied that the clusters state should be loaded first, and that cluster-detail would be loaded into a ui-view somewhere in that view. There was no ui-view anywhere in the clusters view, so cluster-details did not load. The navigation via ui-router was working, and working as described...the problem wasn't ui-router, the problem was the simple fact that there was no second level of nested state.
I recently refactored the ui-router states, making all detail views direct children of the portal root view, so we now have portal.clusterDetails instead of portal.clusters.cluster-detail. That solved one problem, the detail views now load properly, and we can route to them from any other view (since pretty much every view is now a sibling, and all are children of portal.) This has given rise to another issue, however, regarding breadcrumb generation (see my linked question above if you have more interest in reading about that.)
I created working plunker here.
I would say, that your setting seems to be ok. The only (hidden here) place, which could be wrong is the content of the
templateUrl: '/views/clusters/clusters.html',
Inside of the state: 'portal.clusters'. What could be wrong there?
This is in fact a parent view for state: 'portal.clusters.cluster-details'. And that means, that we need to insert an anchor (ui-view) there:
...
<div ui-view=""></div>
That allows our child to be rendered
Check it in action here

# and named# doesn't work on the same state views with resolve

I have a state named 'index' that, depending on the ui-view on the page, display different information and have different templateUrl and share the same URL:
$stateProvider.state('index', {
url: '/',
views: {
'all#': {
templateUrl: '/templates/partials/all/index',
controller: function(){
console.log('main');
}
},
'#': {
templateUrl: '/templates/partials/home/index',
controller: Controllers.Index,
controllerAs: 'academia',
resolve: {
templates: Preload.go(),
academia: ['Info', (Academia: Academia.Services.Info) => {
return Academia.get();
}],
plans: ['Planner', (Planner: ngPlanner.Planner) => {
return Planner.init();
}]
}
}
}
});
when the page is loaded with a <div ui-view></div>, it works as intended (matching the '#' rule). but when the page is loaded with <div ui-view="all"></div> it doesn't.
A plunkr showing the problem http://plnkr.co/edit/a2SWbB?p=preview
The resolve from the unnamed # shouldn't interfere with the all# view. The current resolve is being rejected because it doesn't belong on the current view (since there's no unnamed view when the page is loaded). Is it a bug, or am I doing something wrong?
At these days, the UI-Router implementation is driven by state management. And despite of the fact, that there are some voices/requirements to split the resolve of the views (well, not the parent view vs child view - but that is discussed below) ... I would say, current behavior in the Q&A described above is simply correct.
Firstly, what is the problem in above question? In general there are two or more sibling views, with their own resolve definitions:
views: {
'view1': {
....
resolve : { // could fail
}
},
'view2': {
...
resolve: { // could fail as well
...
What is state?
A state is a cluster of settings, representing the state. (sounds like self proving statement, but I am serious). All the settings are about state definition, about one, specific state.
We can use more views to represent it (e.g. title and content).
We can use some resolve statements (for all sibling views, for some of them) to support these views.
We have still only ONE state.
And that's it. It is all or nothing. The state is one, one cluster of settings, which all of them must be workign/resolved - or the whole state is failing.
Another story: parent - child
There are also some requirements to let the parent view to render once all its stuff is resolved. Why? Currently, if we navigate directly to child state, all/both resolves must be resolved before the state is initiated, the views are rendered.
That could be really improved. Make sense. And as far as I remember it is planned to happen in later releases (I'would swear I've seen that in one of the comments by UI-Router team member - but cannot find it).
It is different scenario. Because 1) we can go to parent (list view) first. And then 2) select child (detail). In this scenario it could really happen that list is working, detail fails.
So going directly to child should render parent, next fail on a child... But this is different story then the sibling described above.
Suggestions:
My suggestion would be, keep in resolve only stuff which is essential, absolute MUST for a state. Move the rest into Controllers injected services. This is my suggestion and my way...

In AngularJS - Managing Multiple Panels for a True SPA

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.

Confused in how to implement tab based app with angular ui-router (like gmail)

I was thinking last night about how to port our crud application to a tab based app, like gmail. First, reading a lot about ui-router, i thought in create a tabService that will create a new tab for each state change (listen $rootScope.stateChangeSuccess), the new tab would include the corresponding view (ui-view="bancos") that will display the state template.
My first test and my first problem, when shown the list of items, a click over one of the item (itemId=4 for example) should open a new tab and display the item with id=4 in this tab, inside the corresponding (ui-view="bancos/4"). Note how i'm trying to map named ui-view with states to display the state defined templates in the corresponding ui-view.
I know that ui-router sample seems to do what i'm trying, but they are using nested states with unamed ui-view inside parent state template. In my case, the parent state ui-view element and his child state ui-view element should be sibling.
Considering the nature of angular, tree structured, and the nature of ui-router states, tree structured too, can i use ui-router to implement my requirements (crud application with tab based design).
Regards
Danny
Here is me example, if you have questions i can answer
http://plnkr.co/edit/tqfPVFaesSe9Q6ZkPMzE?p=preview
Short explain: don't use tabs instead of that create a fake, works well and you save a lot of work.
Best
I was looking for the same info, does the following example help you?http://plnkr.co/edit/gMsKK9l7J0B8vZIMIVfD?p=preview (Not my coding.)
Edit:
I've found more useful data.
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
In my code, I have set it up like this:
.state('surveys.list', {
url: '/list',
templateUrl: "survey/surveys_overview.tpl.html",
controller: 'SurveyOverview'
})
.state('surveys.create', {
abstract: true,
templateUrl: "survey/survey.create.tpl.html",
controller: 'SurveyManageController'
})
.state('surveys.create.views', {
url: '/create',
views: {
'details': {
templateUrl: "survey/survey.create.details.tpl.html"
},
'steps': {
templateUrl: "survey/survey.create.steps.tpl.html"
}
}
});
The surveys.create.tpl.html just has a and two tabs with each one div:
<div ui-view="details"> & <div ui-view="steps">

Resources