I have seen a design interface here, and I liked it.
I was doing it with AngularJS, and ui-router which allows to have multiple views on a single page.
I have an issue for smartphone, because I want to display only one view, not two like tablets/desktops.
Question :
What would be the best way to display only one view for small screens, and two for larger screens ?
I had basic ideas :
Idea 1 (not so so good) : I was thinking to create multiple routes, then route to one or the other route depending the screen size.
Inside angular config
app.config(function($stateProvider) {
$stateProvider
.state('listForLargeScreens', {
url: "/listForLargeScreens",
views: {
"primary": { templateUrl: "list.html" },
"secondary": { templateUrl: "detail.html" }
}
})
.state('listForSmallScreens', {
url: "/listForSmallScreens",
views: {
"primary": { templateUrl: "list.html" }
}
})
...;
Inside a random controller
app.controller('RandomCtrl', function ($scope, $state) {
$scope.changePage = function () {
if (isLargeScreen) {
$state.go('indexForLargeScreens');
} else {
$state.go('indexForSmallScreens');
}
}
});
Problem : this idea seems to me too "dirty" for maintaining.
Idea 2 : I was thinking to declare primary and secondary views, then I could hide with CSS the secondary one with CSS :
Inside angular config
app.config(function($stateProvider) {
$stateProvider
.state('list', {
url: "/",
views: {
"primary": { templateUrl: "list.html" },
"secondary": { templateUrl: "detail.html" }
}
})
.state('detail', {
url: "/detail/:id",
views: {
"secondary": { templateUrl: "list.html" },
"primary": { templateUrl: "detail.html" }
}
})
...;
Inside html
<div ui-view="main"></div>
<div ui-view="secondary"></div> <!-- For small screens, hide this view with CSS -->
Problem : different URLS can have the same views, but with inverted priorities, see :
So, on small screens, it could work, but on larger screens, in some URLs, views can be inverted, (see the image above), so in HTML, list.html and detail.html templates will be inverted too.
It could solve the problem if I could invert <div ui-view="primary"></div> and <div ui-view="secondary"></div> on some views...
I've just found on scotch.io a piece of code which is the key for that problem.
The idea 2 on the question was almost the answer, but I had a problem with different URLs which where using same views, but with inverted priorities.
ui-router allows with multiple named views, to get ancestors, so I have to create a piece of html which declares ui-views :
Inside angular config :
$stateProvider.state('list', {
url: '/list',
views: {
'': { templateUrl: 'partial-list.html' },
'primary#list': {
templateUrl: 'list.html',
controller: 'ListCtrl as ctrl'
},
'secondary#list': {
templateUrl: 'detail.html',
controller: 'DetailCtrl as ctrl'
}
}
}).state('detail', {
url: '/detail/:id',
views: {
'': { templateUrl: 'partial-detail.html' },
'primary#list': {
templateUrl: 'detail.html',
controller: 'DetailCtrl as ctrl'
},
'secondary#list': {
templateUrl: 'list.html',
controller: 'ListCtrl as ctrl'
}
}
});
Inside partial-list.html :
<div ui-view="primary"></div>
<div ui-view="secondary" class="hide-it-for-small-screens"></div>
Inside partial-detail.html :
<div ui-view="secondary" class="hide-it-for-small-screens"></div>
<div ui-view="primary"></div>
It works ! :)
Related
I would like to have a common state with all the common views like the header and the sidebar, and a template where I would like to load different views that can change when the state is changing.
I have an index HTML file like this:
...
<div ui-view="header"></div>
<div ui-view="sidebar"></div>
<div class="container">
<div class="wrapper">
<div ui-view="content"></div>
</div>
</div>
...
While the AngularJS config is something like this:
$stateProvider
.state('mainCommonState', {
url: '',
abstract: true,
views: {
header: {
templateUrl: 'app/common/header.html',
controller: 'headerCtrl',
controllerAs: 'vm'
},
sidebar: {
templateUrl: 'app/common/sidebar.html',
controller: 'sidebarCtrl',
controllerAs: 'vm'
},
content: {}
},
resolve: {
apiEnvironment: function ($q, environmentApiService) {
var deferred = $q.defer();
deferred.resolve(environmentApiService.getApiEnvironment());
return deferred.promise;
}
}
})
.state('first-page-content', {
url: '/first-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/first-page-content.html'
controller: 'firstPageCtrl',
controllerAs: 'vm'
}
}
})
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
.state('third-page-content', {
url: '/third-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/third-page-content.html'
controller: 'thirdPageCtrl',
controllerAs: 'vm'
}
}
})
For some reason this is not working: I have an empty view instead of the 3 templates that I would like to show in the content ui-view.
If I define a template (even a blank template) inside the the abstract state, the view that is always showing is the one inside the abstract state mainCommonState.
Where am I wrong?
1st Edit: UPDATE Following the first answer
Following the suggestion from Chris T, I have updated my code, but there still something missing.
I have created a Plunker so you can help me fixing the issues.
2nd Edit
Following the suggestions from Chris T, I have updated the code using the absolute path for the content view and now the contents are switching correctly.
I have updated the Plunker accordingly to that and introduced a new level of nesting view (tabs in the first page), and I would like to have the first tab active when the first page content is loaded.
If I follow these solutions and set empty the url of the first page and set it to the first tab instead, this is not working.
Any suggestions?
Your views are targeting the wrong named ui-view.
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In this snippet, it targets the ui-view named content from the parent state which is mainCommonState. However, the content ui-view was not created in the mainCommonState. It was created in the root template.
Change your view declarations to target the view at the correct state, for example this targets the content view at the root state (which is named empty string):
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'content#': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In ui-router 1.0 and higher you can also use absolute ui-view names by prefixing with an exclamation
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'!content': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
Read more about view targeting in the UI-Router views guide:
https://ui-router.github.io/guide/views#view-name-only
I have been reading about nested views and multiple views but I can't find an example using both
In a landing page I have multiple views, one after other. A picture speaks a thousand words:
To consider:
Every section/view will have full window height, so on scroll I want to change location to /view1, /view2, /view3, etc.
That should be compatible with going to /view1/b or /view3/b and showing subview (view1.b or view3.b).
Scroll should not make load page again.
I have success doing tasks separately but not all together.
you can use $statePorivder nested view with
config(function ($stateProvider) {
$stateProvider
.state('main', {
url: '/',
views: {
'': {
templateUrl: 'app/main/layout.html',
controller: 'LayoutController'
},
'header#main': {
templateUrl: 'app/main/header/layout.html',
controller: require("./headerController.js")
},
'left#main': {
templateUrl: 'app/main/left/layout.html',
controller: require("./leftController.js")
},
'content#main': {
templateUrl: 'app/main/content/layout.html',
controller: require("./conentController.js")
},
'right#main': {
templateUrl: 'app/main/right/layout.html',
controller: require("./rightController.js")
}
}
});
};
I have this in my app.js:
$stateProvider
.state('actionplans', {
url: "/actionplans",
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
How can I default load nest view action 'actionplans.summary.html' when called actionplans.html?
There is a working example
The way which will
load some view inside of a parent - and stay on parent
allow child change it when navigating to child
is called Multiple named views:
.state('actionplans', {
url: "/actionplans",
views: {
'': {
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
},
'#actionplans': {
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
}
}
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
What we did above, is that we used views : {} object to define two views. First is targeting the index.html (the '') the second is targeting this state view target for children ( the '#actionplans').
views: {
'': { // index.html
...
},
'#actionplans': { // this targets the unnamed view for children
Read more about absolute names here
Another way, is to define some default redirection, but that will disable parent state as a real target (e.g. here Redirect a state to default substate with UI-Router in AngularJS)
Here discuss about AngularJS Routing Using UI-Router, you will get enough idea about nested view and multiple view.
https://scotch.io/tutorials/angular-routing-using-ui-router
I found a simple solution here.
$urlRouterProvider.when('/actionplans', '/actionplans/summary');//<-- Add in this line
$stateProvider
.state('actionplans', {
url: "/actionplans",
abstract: true,/// <-- Add in this line
templateUrl: "pages/actionplans.html",
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
})
This will load nest view actionplans.summary.html by default when you call /actionplans. My apology that I did not make this clearer in my question so I post the answer here hopefully it will help someone else with the similar scenario.
Could someone give a hint on ui-view?
I have one main ui-view and then two nested views, but for some reason these two are not getting loaded.
$stateProvider
.state('index',{
url:'',
templateUrl: './app/modules/main.html'
})
.state('index.feed',{
parent:'index',
templateUrl: './app/modules/feed.html'
})
.state('index.status',{
parent: 'index',
templateUrl: './app/modules/status.html'
});
The main page is loading but the two other views not
in my main.html I have:
<div class="voucher-display" ui-view="index.feed"></div>
<div class="feed" ui-view="index.feed"></div>
I just get an empty file.
The value you pass to ui-view in your html is not the name of the state, it should be the name of the view.
Is the html in feed.html and status.html supposed to replace or be displayed along with main.html? Either scenario will change the solution, but assuming you always want main.html, and want to display the feed.html and status.html where their respective nested views are selected, you'd do something like this:
.state('index', {
url: '/',
views: {
'main': {
templateUrl: './app/modules/main.html'
}
}
})
.state('index', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/feed.html'
}
}
})
.state('index.status', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/status.html'
}
}
});
And in your html you'd need two div tags that map the view.
<div ui-view="main"></div>
<div ui-view="detail"></div>
Now main.html should always show up in the first div, and feed.html or status.html, when their states are active, will show up in the second div.
Here is the docs for the ui-view directive, and here is a good blog post about ui-view.
After a couple of attempts, this worked to load the nested:
$stateProvider
.state('index', {
url: '',
views: {
'main': {
templateUrl: './app/modules/main.html'
},
'status#index': {
templateUrl: './app/modules/status.html'
},
'feed#index':{
templateUrl: './app/modules/feed.html'
}
}
})
index.html has:
<main ui-view="main"></main>
and inside the main.html I have:
<div class="voucher-display" ui-view="feed"></div>
<aside ui-view='status'></aside>
but now I got another problem, if I want to load another ui-view from my index.html, what should I do? because this does not work:
.state('clients',{
url:'/clients',
templateUrl: './app/modules/clients.html'
});
I want to give 2 parts of my UI the same controller but still let them have each of their own unique controllers.
$stateProvider
.state('standard.page', {
url: '/:page',
resolve: {
page: function($stateParams) {
...
},
},
views: {
'content': {
templateUrl: '/tmpl/page',
controller: 'controllercontent'
},
'sideMenu': {
templateUrl: '/tmpl/menu',
controller: 'controllermenu',
}
}
})
So I want both content and sideMenu to share a controller. If I add a controller above the views then it requires a new template, I want to use the standard template instead of making a unique template for this state. Any ideas how I can get 3 controllers going in this example? Thanks.
I battled with this at some point in time, and I believe I made a template file that isn't directly accessible (via abstract: true). Here's an example...
.state('standard', {
url: '/standard',
abstract: true,
templateUrl: '/tmpl/standard.html',
controller: 'SharedController'
},
})
.state('standard.page', {
url: '/:page',
resolve: {
page: function($stateParams) {
...
},
},
views: {
'content': {
templateUrl: '/tmpl/page',
controller: 'controllercontent'
},
'sideMenu': {
templateUrl: '/tmpl/menu',
controller: 'controllermenu',
}
}
});
In your tmpl/standard.html file, make sure this exists somewhere within the file:
<div ui-view="sideMenu">
<div ui-view="content">
Hope this points you in the right direction.