Using angular 1.6, here are the components I need to have:
+-------------------+
|side| |
|bar | |
|... | view |
|... | |
|... | |
+-------------------+
side bar: placed on left. using ng-repeat and updates dynamically.
view: rest of the app. template is static but contents changes when click on side bar.
when the user clicks on each list <li> of side bar, it updates the view. only view content/model will change. not the html template.
Important: I just don't want to write a code in <body>. Would like to have sidebar in one html file and view in another associated with their controllers and render the whole app through ng-view in <body>.
The side bar updates dynamically so that I cannot specify state in config() for each.
any other standard architecture is appreciated.
As mentioned in the comments, there are multiple architecture and framework choices that can be made to implement the type of templating you're looking for. I'll provide a basic example based on something I've used in the past with angular, using ui-router and child/nested views.
Lets say you have an index.html like:
<body>
<div ui-view="header" class="header"></div>
<div ui-view="main" class="main"></div>
<div ui-view="footer" class="footer"></div>
</body>
The index page just has the layout for the most high level view, namely header, content and footer. If you have no need for header and footer, you can just ignore/remove it. Now the layout your are looking for, with a side bar on the left and the content on the right, will be placed within the main view. To do this lets declare another page which will define this structure, call it landing.html (use bootstrap for simplicity):
<div class="container-fluid">
<div class="row landingContainer">
<div class="col-md-2 col-sm-4 col-xs-3 sidebarSection">
<div class="row item" ng-click="landing.changePage('content1')">
<span>Show Content 1</span>
</div>
<div class="row item" ng-click="landing.changePage('content2')">
<span>Show Content 2</span>
</div>
<div class="row item" ng-click="landing.changePage('content3')">
<span>Show Content 3</span>
</div>
</div>
<div class="col-md-10 col-sm-8 col-xs-9 contentSection">
<div ui-view="content"></div>
</div>
</div>
</div>
You can think of this page as the root of your layout. The page is split into a left and right section using bootstrap columns. The left side contains the listing of all your content views. You can use li, i just prefer divs. The right side will be where the dynamic part of the page is, where the content will change based on the item selected in the side bar. Each component view is a child of the landing page, it inherits all the features of the parent and then adds its own content into the ui-view, similar to how landing.html added its content into the main ui-view. Now lets take a look at the ui-router config that makes all this work.
function routerConfig($stateProvider, $urlRouterProvider) {
$stateProvider
.state('landing', {
url: '/landing',
views: {
'header': {
templateUrl: 'app/components/header/headerPrivate.html',
controller: 'HeaderController',
controllerAs: 'header'
},
'main': {
templateUrl: 'app/landing/landing.html',
controller: 'LandingController',
controllerAs: 'dashboard'
},
'footer': {
templateUrl: 'app/components/footer/footer.html',
controller: 'FooterController',
controllerAs: 'footer'
}
}
})
.state('landing.content1', {
url: '/content1',
views: {
'content': {
templateUrl: 'app/content1/content1.html',
controller: 'Content1Controller',
controllerAs: 'content1'
}
}
})
.state('landing.content2', {
url: '/content2',
views: {
'content': {
templateUrl: 'app/content2/content2.html',
controller: 'Content2Controller',
controllerAs: 'content2'
}
}
})
.state('landing.content3', {
url: '/content3',
views: {
'content': {
templateUrl: 'app/content3/content3.html',
controller: 'Content3Controller',
controllerAs: 'content3'
}
}
})
$urlRouterProvider.otherwise('/');
}
Here you'll notice that the landing page route defines the configuration for the 3 main views, header, main and footer. The url path will be /landing. Then content1, content2 and content3 are defined as children by nesting them within landing using the dot notation: landing.content1. The url paths for each child will then resolve to /landing/content1, /landing/content2, landing/content3. So now anytime you navigate to those locations only the content for that specific child will be nested within the landing pages "content" ui-view and the remaining layout of the page stays the same.
For the sake of completion, this is how the landing controller might look:
function LandingController($state) {
var vm = this;
vm.changePage = function(page){
$state.transitionTo('landing.'+page, null, null);
}
}
Related
I have a scenario like this:
Link1
Link2
<div id="child1"></div>
<div id="child2"></div>
When I click on link1 I expect to load View A in div child 1 and View B in div child2.
When I click on link2 I expect to load View C in div child 1 and View D in div child2
I'm using ngRoute (standard AngularJS router) in my app.
i.e: I expect a different set of views for different links. How can this be accomplished using AngularJS. I understand that Angular does provide routing, but whatever examples I see online is only for a single view.
I have kept this scenario very simple. In reality it is a lot more complicated so I will not be able to combine 2 views into 1 for each link.
Here is a suggestion:
1) Make 4 templates (one per view):
viewA.html
viewB.html
viewC.html
viewD.html
2) Set 2 routes on your app (one per link):
$routeProvider.when('/page1', {
templateUrl: 'page1.html',
controller: 'Page1Ctrl'
}).when('/page2', {
templateUrl: 'page2.html',
controller: 'Page2Ctrl'
});
3) Include the views in page link1.html and link2.html
<!-- page1.html -->
<div id="child1" ng-include="'viewA.html'"></div>
<div id="child2" ng-include="'viewB.html'"></div>
<!-- page2.html -->
<div id="child1" ng-include="'viewC.html'"></div>
<div id="child2" ng-include="'viewD.html'"></div>
3) Set your <a> tags
Link1
Link2
Another suggestion, you can use ui-router and multiple named views it's easy to use and really powerful.
You can create your different view container using ui-view like this
<a href="#" >Link1</a>
<a href="#" >Link2</a>
<div ui-view="child1"></div>
<div ui-view="child2"></div>
And in you app.config, you can set for every states which template you want to load for the different views :
.state('report', {
views: {
'child1': { ... templates and/or controllers ... },
'child2': {}
}
So I am trying to display multiple views in angular to help fix the footer problem I am having with this site I am building. I want to make sure that what I have been reading about and trying to mimic is making sense. This is what I have so far.
index.html
<!--Do not code below this line-->
<main ng-view="header"></main>
<main ng-view="body"></main>
<main ng-view="footer"></main>
routes.js file
angular.module('appRoutes', ['ngRoute'])
.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
views: {
'header': {
temmplateUrl: 'app/views/header.html'
},
'body': {
templateUrl: 'app/views/body.html'
},
'footer': {
templateUrl: 'app/views/footer.html'
}
}
})
I have it working where I have just one view and have my header and footer inside the index.html file but I saw that you can have multiple views and really just switch out the "body" view with other pages.
Any advice would be much appreciated. Thank you
To display multiple views you could use only ng-include.
See https://docs.angularjs.org/api/ng/directive/ngInclude for more ngInclude details.
Here is a example: (notice the wrap in single quotes)
<div id="header">
<div ng-include="'header.html'"></div>
</div>
<div id="content" ng-view></div>
<div id="footer">
<div ng-include="'footer.html'"></div>
</div>
Use ngRoute with ng-view to define a region (e.g div#content) where will be changed the dynamic content (as partial html).
See https://docs.angularjs.org/api/ngRoute/directive/ngView for more ngRoute details.
Good luck!
You can have just one ng-view.
You can change its content in several ways: ng-include, ng-switch or mapping different controllers and templates through the routeProvider.
Alternatively, use ui-router
In short: my demo, how to have subviews? (without reloading page)
Longer:
I'm using snapscroll to provide different screens effect (demo). This directive doesn't support by default ui-router so I had to do it myself.
When you move the mouse wheel beforeSnap event event is triggered. I call ui-router $state.go to change URL hash. I don't reload the controller.
$scope.beforeSnap = function (snapIndex) {
$state.go('view' + snapIndex,{},{
notify:false,
reload:false,
location:'replace',
inherit:true
});
};
Routing is like
.state('main', {
url: '/',
templateUrl: 'views/screens.html',
controller: 'mainCtrl'
})
.state('view0', {
url: '/green',
templateUrl: 'views/screens.html',
controller: 'mainCtrl',
params: {'color': 'green', 'screenIndex': 0}
})
[...]
views/screens.html:
<div fit-window-height="" snapscroll="" snap-index="snapIndex" before-snap="beforeSnap(snapIndex)">
<div class="green"></div>
<div class="blue">
<!-- How to enable these subviews? -->
<!-- <a ui-sref="view0.a">Go to view0.a</a> -->
<!-- <a ui-sref="view0.b">Go to view0.b</a> -->
<div ui-view="view1"></div>
</div>
<div class="red"></div>
<div class="yellow">
<div ui-view="view3"></div>
</div>
</div>
You can see a codepen and a demo.
The issue is that I want to have subviews in some "screens" (colours divs), e.g: view1 and view3.
But I don't know how to do it without reloading the whole page. Subviews like:
/view1/a
/view1/b
/view3/a
/view3/b
/view3/c
Besides, I think screens would be better/cleaner being children of main main.view0, main.view1, etc.
I read about multiple named views in ui-router and nested states without success.
I have an AngularJs app with start up page as index.html, by default the projects view will be displayed and on top of the page I am showing a icon to show the todo items (for the logged-in user) which I am using bootstrap's data-toggle dropdown. The issue is whenever I click the todo link the partial view (todo.html) is not showing. BTW, I am new to the angular world so please forgive me if there is anything silly. Please see the code below:
Index.html
<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head></head>
<body>
<a data-toggle="dropdown" class="dropdown-toggle" ui-sref=".todo">
<i class="icon-tasks"></i>
<span class="badge badge-grey">4</span>
</a>
<div ng-view></div>
</body>
app.js
// For any unmatched url, redirect to /projects
$urlRouterProvider.otherwise("/projects");
//
// Now set up the states
$stateProvider
.state('projects', {
url: "/projects",
templateUrl: "/app/views/projects/projects.html",
controller: "projectController"
})
.state('projects.todo', {
url: "/todo",
templateUrl: "/app/views/todo/todo.html"
});
First of all replace ng-view with ui-view in the root template, cause it seems you want to use ui-router instead of ng-router.
Wrap the content of your template files with div of ui-view as a parent element.
/app/views/projects/projects.html
/app/views/todo/todo.html
<div ui-view>
... previously defined content ...
</div>
Let's say your view was
<div class="container">
<div class="page-header">
<h1>Title: {{title}}</h1>
</div>
</div
you need to add ui-view to the div
<div class="container" ui-view>
<div class="page-header">
<h1>Title: {{title}}</h1>
</div>
</div
or wrap your view with div containing ui-view descriptor in case your vie contains several tags.
I cannot show you an example since you did not provide content of view files.
/app/views/projects/projects.html
/app/views/todo/todo.html
The issue is that after fist template applying angular does not see the place to put new template anymore.
ui-router isn't really supposed to be used in this way. To integrate bootstrap with angular you want to look at UI Bootstrap - http://angular-ui.github.io/bootstrap/
Then to achieve your drop down, look at their basic examples. If you want to use separate view files to define your drop down content, you can use <div ng-include="'mycontent.html'"></div>.
ui-router is useful if you have a complex view hierarchy, where you are for example, looking for dynamic loading of children, while keeping parent states static.
In ui-router you defined all of this in the $stateProvider, so there you should define that you have a view that has another view belonging to it, example:
<!-- In index.html the main view that will have all the page views-->
<div id="main" ui-view="main"></div>
<!-- In todo.html with a partial with the dropdown code in dropdown.html -->
<h1> This is a nice todo drop down </h1>
<div id="todoDropDown" ui-view="todoDropDown"></div>
//In your app file
.state('projects.todo', {
url: '/todo',
views: {
'main#': {
templateUrl: '/app/views/todo/todo.html',
controller: 'TodoCtrl'
},
'todoDropDown#projects.todo': {
templateUrl: '/app/views/partials/dropdown.html'
}
}
})
"todoDropDown#projects.todo" This does the magic, it tells that this view has another view inside. And you can add controller to it and all other options you have in ui-router. In this way you can break up as much as possible reusable parts.
I am trying to change the page header depending on current view. The header is outside of ngView. Is that possible or do I need to put the header inside the view?
My code looks similar to this:
<div id="header">
<div ng-switch on="pagename">
<div ng-switch-when="home">Welcome!</div>
<div ng-switch-when="product-list">Our products</div>
<div ng-switch-when="contact">Contact us</div>
</div>
(a lot of unrelated code goes here)
</div>
<div id="content>
<div ng-view></div>
</div>
Give each route a name when you are defining it. Then inject $route into the controller, then have the controller publish it into the current scope. You can then bind the ng-switch to $route.current.name
You could inject the $location service and check $location.path(). http://docs.angularjs.org/api/ng.$location
JS:
function Ctrl($scope, $location) {
$scope.pagename = function() { return $location.path(); };
};
HTML:
<div id="header">
<div ng-switch on="pagename()">
<div ng-switch-when="/home">Welcome!</div>
<div ng-switch-when="/product-list">Our products</div>
<div ng-switch-when="/contact">Contact us</div>
</div>
</div>
Seems as it will be different controllers for header and content. Best way for communication between controllers is service. Another way - events. See Vojta answer.
A nice aproach for solving this is maybe to inject $route in your controller and then use it to grab the current route name.
app.controller('YourController', function($scope, $route){
$scope.pagename = $route.current.$$route.name;
});
And you have to name your routes like the following:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/product-list', {
templateUrl: 'views/product-list.html',
controller: 'ProductsController',
name: 'product-list'
}).
when('/home', {
templateUrl: 'views/home.html',
controller: 'HomeController',
name: 'home'
}).
otherwise({
redirectTo: '/'
});
}]);
So when you load a route,the controller will read the current route name and pass it to the view in your pagename variable. Then the view will pick it up and display the correct view as you need.
Hope it helps :)