How to make a list and grid view toggle switch control that loads in partials in AngularJS?
You should define application controller's scope property that will hold URL of template for ng-include
, and bind this property to your directive's scope. Make sure to use isolated scope in your directive in order to avoid side effects while modifying directive's scope. Here is an example of doing it (see comments in the code):
JavaScript
angular.module('app', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/main', {
controller: 'appController',
templateUrl: 'main.html'
})
.otherwise({
redirectTo: '/main'
});
}])
.controller('appController', ['$scope', function($scope) {
$scope.view = 'list.html'; // <- default template used for ng-include
$scope.data = [{
text: '1'
}, {
text: '2'
}, {
text: '3'
}, {
text: '4'
}, {
text: '5'
}, {
text: '6'
}];
}])
.directive('appView', function() {
return {
scope: {
// link view property of directive's scope to some property
// in the parent scope (scope of appController) specified in
// app-view attribute of root element of directive
view: '=appView'
},
replace: true,
template:
'<nav class="navbar navbar-default">' +
'<div class="container">' +
'<ul class="nav navbar-nav navbar-right">' +
'<li ng-repeat="v in views" ng-bind="v.name" ' +
'ng-class="v.icon" ng-click="switchView(v)"></li>' +
'</ul>' +
'</div>' +
'</nav>',
link: function(scope, el, attr) {
scope.views = [{
name: 'List',
template: 'list.html',
icon: 'btn btn-default navbar-btn glyphicon glyphicon-th-list'
}, {
name: 'Grid',
template: 'grid.html',
icon: 'btn btn-default navbar-btn glyphicon glyphicon-th'
}];
},
controller: ['$scope', function($scope) {
$scope.switchView = function(view) {
$scope.view = view.template; // <- modify parent scope view
}
}]
}
});
Main page of application (index.html)
<html ng-app="app">
...
<body ng-view=""></body>
</html>
Route template (main.html)
<header app-view="view"></header>
<section ng-include="view"></section>
List view template (list.html)
<div class="container">
<div class="row">
<div class="col-md-12 col-sm-12 panel panel-default" ng-repeat="item in data">
<div class="panel-body">{{item.text}}</div>
</div>
</div>
</div>
Grid view template (grid.html)
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-6 panel panel-default" ng-repeat="item in data">
<div class="panel-body">{{item.text}}</div>
</div>
</div>
</div>
Plunker: http://plnkr.co/edit/uWw7NuPG0I161mHXZg2r?p=preview
Bonus: grid is responsive, just play a bit with a window size
Another option:
As you have probably noticed, grid.html
and list.html
are very similar, so if you have only these two options you may decide not to use ng-include
with separate switchable views at all, but place content view directly into your route's template and just switch classes used in panels using ng-class
directive which can switch classes when view is changed.
Route template (main.html)
<header app-view="view"></header>
<section>
<div class="container">
<div class="row">
<div ng-class="{'col-md-4': view === 'grid.html',
'col-md-12': view === 'list.html',
'panel':true, 'panel-default':true}"
ng-repeat="item in data">
<div class="panel-body">{{item.text}}</div>
</div>
</div>
</div>
</section>
Basically what you need to do is put a list view and grid same time on the same page and display one at a time, toggling between them (changing css class) with the switch button. I'll discuss the Example #5 here:
First layout variable in the scope:
$scope.layout = 'grid';
Here are the switch buttons:
<!-- On click change "$scope.layout = list" and if layout=='list' add class 'active' to self-->
<a href="#" class="list-icon" ng-class="{active: layout == 'list'}" ng-click="layout = 'list'"></a>
<!-- On click change "$scope.layout = grid" and if layout=='grid' add class 'active' to self-->
<a href="#" class="grid-icon" ng-class="{active: layout == 'grid'}" ng-click="layout = 'grid'"></a>
These are the grid and list blocks:
<!-- Layout=='grid' if layout parameter is grid than show this block -->
<ul ng-show="layout == 'grid'" class="grid">...</ul>
<!-- Layout=='list' if layout parameter is list than show this block -->
<ul ng-show="layout == 'list'" class="list">...</ul>
Css that makes the list and grid layout:
/*-------------------------
List layout
--------------------------*/
ul.list {
list-style: none;
width: 500px;
margin: 0 auto;
text-align: left;
}
ul.list li {
border-bottom: 1px solid #ddd;
padding: 10px;
overflow: hidden;
}
ul.list li img {
width: 120px;
height: 120px;
float: left;
border: none;
}
ul.list li p {
margin-left: 135px;
font-weight: bold;
color: #6e7a7f;
}
/*-------------------------
Grid layout
--------------------------*/
ul.grid {
list-style: none;
width: 570px;
margin: 0 auto;
text-align: left;
}
ul.grid li {
padding: 2px;
float: left;
}
ul.grid li img {
width: 280px;
height: 280px;
display: block;
border: none;
}
The easiest way to do this is:
<div class="bar">
<h1>Contacts</h1>
<a href="#" class="list-icon" ng-class="{active: layout == 'list'}" ng-click="layout = 'list'"></a>
<a href="#" class="grid-icon" ng-class="{active: layout == 'grid'}" ng-click="layout = 'grid'"></a>
</div>
<div ng-show="layout == 'list'" class="list">
<!-- Add your list page here -->
<div ng-show="layout == 'grid'" class="grid">
<!-- Add your grid page here -->
CSS:
/*
Contacts bar with toggle switches
*/
.bar {
background-color: #5c9bb7;
background-size: 100% 100%;
box-shadow: 0 1px 1px #ccc;
border-radius: 2px;
height: 100px;
padding: 10px;
position: relative;
text-align: right;
line-height: 1;
}
.bar a {
background: #4987a1 center center no-repeat;
width: 32px;
height: 32px;
display: inline-block;
text-decoration: none !important;
margin-right: 5px;
border-radius: 2px;
}
.bar a.active {
background-color: #c14694;
}
.bar a.list-icon {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkYzNkFCQ0ZBMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkYzNkFCQ0ZCMTBCRTExRTM5NDk4RDFEM0E5RkQ1NEZCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RjM2QUJDRjgxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RjM2QUJDRjkxMEJFMTFFMzk0OThEMUQzQTlGRDU0RkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7h1bLqAAAAWUlEQVR42mL8////BwYGBn4GCACxBRlIAIxAA/4jaXoPEkMyjJ+A/g9MDJQBRhYg8RFqMwg8RJIUINYLFDmBUi+ADQAF1n8ofk9yIAy6WPg4GgtDMRYAAgwAdLYwLAoIwPgAAAAASUVORK5CYII=);
}
.bar a.grid-icon {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjBEQkMyQzE0MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjBEQkMyQzE1MTBCRjExRTNBMDlGRTYyOTlBNDdCN0I4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MERCQzJDMTIxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MERCQzJDMTMxMEJGMTFFM0EwOUZFNjI5OUE0N0I3QjgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4MjPshAAAAXklEQVR42mL4////h/8I8B6IGaCYKHFGEMnAwCDIAAHvgZgRyiZKnImBQsACxB+hNoDAQyQ5osQZIT4gH1DsBZABH6AB8x/JaQzEig++WPiII7Rxio/GwmCIBYAAAwAwVIzMp1R0aQAAAABJRU5ErkJggg==);
}
/*-------------------------
List layout
--------------------------*/
ul.list {
list-style: none;
width: 500px;
margin: 0 auto;
text-align: left;
}
ul.list li {
border-bottom: 1px solid #ddd;
padding: 10px;
overflow: hidden;
}
ul.list li img {
width: 120px;
height: 120px;
float: left;
border: none;
}
ul.list li p {
margin-left: 135px;
font-weight: bold;
color: #6e7a7f;
}
/*-------------------------
Grid layout
--------------------------*/
ul.grid {
list-style: none;
width: 570px;
margin: 0 auto;
text-align: left;
}
ul.grid li {
padding: 2px;
float: left;
}
ul.grid li img {
width: 280px;
height: 280px;
display: block;
border: none;
}