I'm learning AngularJS following the good Pro AngularJS written by Adam Freeman.
I'm stuck on ng-repeat pagination using filters. I know there are bootstrap ui directives for Angular, but i'm following this book in order to learn how angular works.
My code:
<section class="row-fluid" ng-controller="GetAjax">
<div class="col-md-12">
<h2>Repater Caricato in Ajax</h2>
</div>
<div class="row-fluid">
<div class="col-md-6" style="max-height: 350px; overflow-y: auto" ng-controller="PagedData">
<ul class="list-group">
<li class="list-group-item" ng-repeat="item in data.visitors | filter:query | range:selectedPage:pageSize">
<b>{{item.id}}.</b> {{item.first_name}} {{item.last_name}} | <small><i>{{item.email}} - {{item.country}} {{item.ip_address}}</i></small>
</li>
</ul>
<ul class="pagination">
<li ng-repeat="page in data.visitors | pageCount:pageSize"
ng-click="selectPage($index + 1)"
ng-class="pagerClass($index + 1)">
<a>{{$index + 1}}</a>
</li>
</ul>
</div>
</div>
</section>
Angular filters
angular.module("customFilters")
/******* Filters per la paginazione dei dati ******************/
//Genera il range di dati in base alla page size
.filter("range", function ($filter) {
return function (data, page, size) {
if (angular.isArray(data) && angular.isNumber(page) && angular.isNumber(size)) {
var start_index = (page - 1) * size;
console.log(data.length);
if (data.length < start_index) {
return [];
} else {
return $filter("limitTo")(data.splice(start_index), size);
}
} else {
return data;
}
}
})
//Calcola il numero di pagine
.filter("pageCount", function () {
return function (data, size) {
if (angular.isArray(data))
{
var result = [];
for (var i = 0; i < Math.ceil(data.length / size) ; i++) {
result.push(i);
}
return result;
}
else
{
return data;
}
}
});
Angular Controller
.controller("GetAjax", function($scope, $http){
$http.get('data/visitors.json').success(function(data) {
$scope.data = {visitors : data};
});
})
.constant("activeClass", "active")
.constant("perPage", 30)
.controller("PagedData", function($scope, $filter, activeClass, perPage){
$scope.selectedPage = 1;
$scope.pageSize = perPage;
console.log("page"+ $scope.selectedPage);
$scope.selectPage = function (newIndex) {
$scope.selectedPage = newIndex;
console.log( {idx: newIndex});
}
$scope.pagerClass = function (index) {
return (index == $scope.selectedPage) ? activeClass : "";
}
});
The result is that after 3 range filter invocations during the page render, the data array looses all the data.
Strange is that using the example from the book this code works perfectly.
Please, help me to know my error :D
splice function overwrites array
if you have an array
a = [1,2,3,4];
a.splice(2,1);
// a = [1,2,4]
results is a = [1,2,4]
use slice instead
Related
Edit:
Just for checking purposes, I also did a console.log inside the nextPage function, to check if it's being triggered:
$scope.nextPage = function() {
var captureLength = $scope.captures.length;
console.log('TRIGGER');
if($scope.busy) {
return;
}
...
}
};
And it seems I'm getting a infinite loop, but I can't see why.
=================================
I'm trying to implement infinitescroll into a view but for some reason it's only loading the initial 4 images and not triggering the rest.
Here is my code:
CTRL:
/* ----------------------- Variables ----------------------- */
$scope.auth = auth;
$scope.captures = [];
$scope.following = [];
$scope.allData = [];
$scope.busy = true;
var page = 0;
var step = 4;
$scope.nextPage = function() {
var captureLength = $scope.captures.length;
if($scope.busy) {
return;
}
$scope.busy = true;
$scope.captures = $scope.captures.concat($scope.allData.splice(page * step, step));
page++;
$scope.busy = false;
if($scope.captures.length === 0) {
$scope.noMoreData = true;
}
};
/* ----------------------- Process Data ----------------------- */
$q.all({follows: findFollow(), users: getUsers(), captures: getAllCaptures()}).then(function(collections) {
var follows = collections.follows;
var users = collections.users;
var captures = collections.captures;
follows.filter(function(follow) {
return follow.follower_id === auth.profile.user_id;
}).forEach(function(follow) {
users.filter(function(user) {
return user.user_id === follow.followed_id;
}).forEach(function(user) {
$scope.following.push(user);
});
});
follows.filter(function(follow) {
return follow.follower_id === auth.profile.user_id;
}).forEach(function(follow) {
captures.filter(function(capture){
return follow.followed_id === capture.userId;
}).forEach(function(capture){
console.log(capture);
$scope.allData.push(capture);
});
});
$scope.nextPage();
$scope.busy = false;
});
/* ----------------------- Retrieve Services - Data ----------------------- */
function findFollow() {
return userApi.findFollow().then(function(res) {
return res.data;
});
}
function getUsers() {
return userApi.getUsers().then(function(res) {
return res.data.users;
});
}
function getAllCaptures() {
return captureApi.getAllCaptures().then(function(res) {
return res.data;
});
}
Partial:
<div class="col-md-8">
<div class="well main-well">
<h3 class="page-header-h3">Following Dashboard:</h3>
<hr />
<h4 align="center" ng-show="!captures.length">
<strong>The people that you are following, have not posted anything yet.. Yikes!</strong>
<br /><br />
Quickly, go follow more people!</h4>
<div class="row" infinite-scroll="nextPage()" infinite-scroll-disabled="busy || noMoreData" infinite-scroll-distance="0.1">
<ul class="dynamic-grid" angular-grid="captures" ag-id="gallery">
<li data-ng-repeat="capture in captures | orderBy :'created_at':true" class="grid">
<a ui-sref="detail({id: capture._id})">
<img ng-src="{{capture.picture}}" class="grid-img" />
<span class="follow-capture-info">
<span class="follow-capture-name"><span class="glyphicon glyphicon-user"></span>
{{capture.author}}
<span class="following-capture-time">ยท
<span class="glyphicon glyphicon-time"></span>
<span am-time-ago="capture.created_at"></span>
</span>
</span>
</span>
</a>
</li>
</ul>
</div>
<div ng-show="busy">Loading more...</div>
</div>
Anyone know where I went wrong?
Thanks.
I am trying to be able to filter multiple columns on multiple values. So far I can filter multiple columns on 1 single value:
myApp.controller('MyCtrl', ['$scope', '$http', function($scope, $http) {
$scope.empList = [];
$http.get('getAllOnline.php')
.success(function(data) {
$scope.empList = data;
});
$scope.column1List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column1"
}
})
.success(function(data) {
$scope.column1 = data;
});
$scope.column2List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column2"
}
})
.success(function(data) {
$scope.column2 = data;
});
$scope.column3List = [];
$http.get('getAllSomething.php', {
params: {
wuk: "column3"
}
})
.success(function(data) {
$scope.column3 = data;
});
$scope.setColumn1Value = function(val) {
if ($scope.zelectedColumn1 == val) {
$scope.zelectedColumn1 = undefined;
} else {
$scope.zelectedColumn1 = val;
}
}
$scope.setColumn2Value = function(val) {
if ($scope.zelectedColumn2 == val) {
$scope.zelectedColumn2 = undefined;
} else {
$scope.zelectedColumn2 = val;
}
}
$scope.setColumn3Value = function(val) {
if ($scope.zelectedColumn3 == val) {
$scope.zelectedColumn3 = undefined;
} else {
$scope.zelectedColumn3 = val;
}
}
Then i use this to set my single value filters:
<ul id="Column1" class="collapse">
<li ng-repeat="emp in empList | unique:'Column1'">
<a ng-click="setColumn1Value(emp.Column1);" ng-class="{selected: emp.Column1 === zelectedColumn1}">
<div ng-repeat="someone in Column1List | filter:{Column1_id:emp.Column1}">
{{someone.value}}
</div>
</a>
</li>
</ul>
This works perfect! But now I want to be able to filter on multiple values in 1 column. So I changed my setter functions to:
$scope.zelectedColumn1=[];
$scope.setColumn1Value = function(val) {
var found = jQuery.inArray(val, $scope.zelectedColumn1);
if (found >= 0) {
// Element was found, remove it.
$scope.zelectedColumn1.splice(found, 1);
} else {
// Element was not found, add it.
$scope.zelectedColumn1.push(val);
}
console.log($scope.zelectedColumn1);
}
So now I add or remove indexes to an array instead of storing a single value. This also works, but how do I filter my columns on the contents of an array instead of on a single value as I do now:
<div class='row'>
<div class='col-lg-2 col-md-3 col-sm-6' ng-repeat="emp in empList | filter:{column1:zelectedColumn1,column2:zelectedColumn2,column3:zelectedColumn3} as res">
{{emp.column1}}
{{emp.column2}} <br>
{{emp.column3}} <br>
</div>
</div>
I have been struggling with this al day and hope someone can help me out here!
I able to embed the 4 videos in a particular div using "angular.js".But,I have so many videos nearly 20.
So,what I'm trying to do is,on click of "Next" button should get next 4 videos in a same div.How can I achieve this?
Can anyone please help me out regarding this issue ...
My html code:
<div class="panel-body">
<video width=176 height=99 style=" margin-left: 10px; margin-right: 10px;"
ng-repeat="videoSource in videoSources track by $index" autoplay
controls ng-src="{{videoSource | trustUrl}}">
</video>
<br> <a href="#" ng-click='loadVideos()'>Load videos</a>
<br><button type="button">Next</button>
</div>
My js code:
angular.module('Admin', [])
.controller('Home', function($scope) {
$scope.videoSources = [];
$scope.loadVideos = function() {
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Customer_Service.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Customer_Service.mp4');
$scope.videoSources.push('http://54.88.118.248/Video/Digital_Hiring.mp4');
};
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
]);
The actual Angular way would be to create a custom filter to paginate the results. In the example, I've created a paginate filter that takes two parameters: pageNum and pageSize to slice the input array into the required chunk without any pre-processing of the array required.
Also added the necessary next and previous buttons and hid the load videos button.
angular.module('Admin', [])
.controller('Home', function($scope) {
$scope.pageNum = 0;
$scope.pageSize = 4;
$scope.isFirstPage = function() {
return $scope.pageNum === 0;
};
$scope.isLastPage = function() {
return $scope.pageNum >= Math.floor($scope.videoSources.length / $scope.pageSize);
};
$scope.prevPage = function() {
$scope.pageNum--;
};
$scope.nextPage = function() {
$scope.pageNum++;
};
$scope.videoSources = [];
$scope.loadVideos = function() {
for (var i = 0; i < 6; i++) {
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/boat_149.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/horse_riding_205.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/flower_124.webm');
}
};
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
])
.filter('paginate', function() {
console.log('creating paginate function', arguments);
return function(inputArray, pageNumber, pageSize) {
console.log('paginating', arguments);
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
if (!Array.isArray(inputArray)) return inputArray;
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="panel-body" ng-app="Admin" ng-controller="Home">
<video width=176 height=99 ng-repeat="videoSource in videoSources | paginate:pageNum:pageSize track by $index" autoplay controls ng-src="{{videoSource | trustUrl}}">
</video>
<div ng-show="videoSources.length">
<button ng-disabled="isFirstPage()" ng-click="prevPage()">Previous</button>
<button ng-disabled="isLastPage()" ng-click="nextPage()">Next</button>
</div>
<div ng-hide="videoSources.length">
<a href="#" ng-click='loadVideos()'>Load videos</a>
</div>
</div>
And because I felt like playing around with the code some more, here's a version that makes a pagination object, so that the functionality can be reused in different controllers and directives:
angular.module('Admin', [])
.controller('Home', function($scope, Pagination) {
$scope.videoSources = [];
$scope.pagination = new Pagination(4);
$scope.loadVideos = function() {
for (var i = 0; i < 6; i++) {
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/boat_149.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/horse_riding_205.webm');
$scope.videoSources.push('http://images.all-free-download.com/footage_preview/webm/flower_124.webm');
}
};
})
.factory('Pagination', function() {
var Pagination = function(pageSize) {
this.pageSize = pageSize || 4;
this.pageNum = 0;
this.sourceLength = 0;
};
Pagination.prototype.isFirstPage = function() {
return this.pageNum === 0;
};
Pagination.prototype.isLastPage = function(sourceLength) {
return this.pageNum >= Math.floor((sourceLength || this.sourceLength) / this.pageSize);
};
Pagination.prototype.prevPage = function() {
this.pageNum--;
};
Pagination.prototype.nextPage = function() {
this.pageNum++;
};
Pagination.prototype.setPage = function(pageNum) {
this.pageNum = pageNum;
};
Pagination.prototype.setPageSize = function(pageSize) {
this.pageSize = pageSize;
};
Pagination.prototype.setSourceLength = function(sourceLength) {
this.sourceLength = sourceLength;
}
Pagination.prototype.getPage = function() { return this.pageNum; };
Pagination.prototype.getPageSize = function() { return this.pageSize; };
Pagination.prototype.getSourceLength = function() { return this.sourceLength; };
return Pagination;
})
.filter("trustUrl", ['$sce',
function($sce) {
return function(recordingUrl) {
return $sce.trustAsResourceUrl(recordingUrl);
};
}
])
.filter('paginate', function() {
console.log('creating paginate function', arguments);
return function(inputArray, pageNumber, pageSize) {
console.log('paginating', arguments);
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
if (pageNumber && pageNumber.pageSize) pageSize = pageNumber.pageSize;
if (pageNumber && pageNumber.pageNum !== undefined) pageNumber = pageNumber.pageNum;
if (!Array.isArray(inputArray)) return inputArray;
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="panel-body" ng-app="Admin" ng-controller="Home">
<video width=176 height=99 ng-repeat="videoSource in videoSources | paginate:pagination track by $index" autoplay controls ng-src="{{videoSource | trustUrl}}">
</video>
<div ng-show="videoSources.length">
<button ng-disabled="pagination.isFirstPage()" ng-click="pagination.prevPage()">Previous</button>
<button ng-disabled="pagination.isLastPage(videoSources.length)" ng-click="pagination.nextPage()">Next</button>
</div>
<div ng-hide="videoSources.length">
<a href="#" ng-click='loadVideos()'>Load videos</a>
</div>
</div>
The filter now looks a bit stranger, because I wanted to keep the option to use it as paginate:pageNumber:pageSize but also allow it to be used as paginate:paginationObject and that took a little trickery. But now, our pagination functionality is abstracted away into a factory so it can be reused and keep our controller lean, and that is the Angular Way(TM) :D
Edit: Extra paginate filter explanation:
.filter('paginate', function() {
a console.log() call I used to debug that I forgot in here
console.log('creating paginate function', arguments);
To be able to accept parameters in an Angular filter, you have to return a function that needs those parameters from the filter function
return function(inputArray, pageNumber, pageSize) {
Another debugging console.log() call
console.log('paginating', arguments);
We can't be sure the parameters were passed in, so we provide sensible defaults (in this case, if pageNumber wasn't given, we'll set it to 0 and if pageSize wasn't given, we'll set it to 4)
pageNumber = pageNumber || 0;
pageSize = pageSize || 4;
Because we want to be able to pass in a Pagination object as a parameter instead of a page number & page size, we see if the first parameter isn't by chance an object containing pageSize and/or pageNum members, and if it is, we set the local pageNumber and pageSize variables to the values of the Pagination object's members
if (pageNumber && pageNumber.pageSize) pageSize = pageNumber.pageSize;
if (pageNumber && pageNumber.pageNum !== undefined) pageNumber = pageNumber.pageNum;
Then we check to see if the first parameter to the filter (the value being filtered) is actually an array. If it isn't, we just return the value unchanged. For example, if we were to have {{ 1 | paginate }} in an Angular template, the result would be 1, our algorithm wouldn't break. If it's an array, though, such as {{ [1, 2, 3, 4, 5] | paginate }} (with default paginate parameters) it would become [1, 2, 3, 4] and {{ [1, 2, 3, 4, 5] | paginate:0:2 }} would become [1, 2].
if (!Array.isArray(inputArray)) return inputArray;
And then the actual pagination logic (funny how it's way smaller than the input checking part of the code). We slice the input array to start at index pageNumber * pageSize and to end at index (pageNumber + 1) * pageSize (non-inclusive). Think of the first page (for pageSize = 4) having page number 0 and starting at index 0 and finishing at index 3 (so ending at index 4 (= 1 * 4) non-inclusive), page two having page number 1 and starting at index 4 (= 1 * 4) and finishing at index 7 (index 8 (= 2 * 4) non-inclusive, and so on. More information on Array.prototype.slice()
return inputArray.slice(pageNumber * pageSize, (pageNumber + 1) * pageSize);
};
});
Without sliding effect, you can do it with pages:
Change loading videos:
$scope.videoSources.push({url:'video_here');
Then, page them:
var page = 1, perpage = 4;
$.each($scope.videoSources, function(k, v) {
v.page = (page++)/perpage;
Now that we have pages, in your HTML you can filter your data:
ng-repeat="video in videoSources | filter: {page: current_page}"
And next page ng-click:
ng-click="current_page++"
Same for previous page.
**Important noticing: because I changed your array to contain objects, to access the video you need to use video.url in your ng-src
How may I order the <div class="item item-divider"> in descending way, where on the top I would like to have the most recent date?
// index.html
...
<ion-content>
<div ng-repeat="(date,list) in items">
<div class="item item-divider">
{{date}}
</div>
<ul class="list">
<li class="item item-icon-right" ng-repeat="item in list">
<h2>{{item.name}} {{item.surname}}</h2>
</li>
</ul>
</div>
</ion-content>
...
-------------------------------------------------------------------------------------------------
// app.js
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope) {
$scope.items = {
'11/12/2014':[
{name:'mark',surname:'john',birth:'11/12/2014'}
],
'12/12/2014':[
{name:'tom',surname:'smith',birth:'12/12/2014'}
{name:'carl',surname:'northon',birth:'12/12/2014'}
]
}
});
Why not sort the object itself, before passing it to angular.
function sortObjectByKeys(object, order) {
var sortedObject = {}, key, tempKeyArray = [];
for (key in object) {
if (object.hasOwnProperty(key)) {
tempKeyArray.push(key);
}
}
// You can also retrieve all the keys using Object.keys() method and then sort them..
tempKeyArray.sort(function(a, b){return b - a});
for (key = 0; key < tempKeyArray.length; key++) {
sortedObject[tempKeyArray[key]] = object[tempKeyArray[key]];
}
return sortedObject;
}
Can you change the data-structure to have two keys, 'date' and 'people'? Then you could do
items | orderBy:'date'
This should work fine:
$scope.mysort = function(obj){
var sortedObject = {}, key, tempKeyArray = [];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
tempKeyArray.push(key);
}
}
tempKeyArray.sort(function(a, b){
var parts = a.split("/");
a = new Date(parts[2], parts[1] - 1, parts[0])
parts = b.split("/");
b = new Date(parts[2], parts[1] - 1, parts[0]);
return b-a;
});
for (key = 0; key < tempKeyArray.length; key++) {
sortedObject[tempKeyArray[key]] = obj[tempKeyArray[key]];
}
return sortedObject;
}
I have an ngClick directive on elements that are also bound to $swipe. The ngClick doesn't fire (it was firing before the $swipe was added).
I'm using jQuery mobile combined with AngularJS.
Interestingly, my diagnostics show a swipe event with start and end the same - seems to contradict the minimum swipe distance, but perhaps the cancel function is being called. Possibly I could use the cancel function to find out where the click occurred but I feel I shouldn't have to do that.
The site is viewable at http://skimobile.cbdweb.net
HTML
<div id="matrix" data-role="page" ng-controller="matrixCtrl" ng-init="init()">
<div data-role="header">
<?php echo CHtml::encode($this->configs['SITENAME']); ?> Bookings
</div>
<div data-role="content">
<div class="lodgename noborder"> </div>
<div class="oneday onemonth" ng-repeat="m in months" ng-style="callMonthstyle(m)" ng-class="$last ? 'lastcol' : ''" on-finish-days>
{{monthNames[m.month]}} {{m.year}}
</div>
<br clear="both" />
<div class="lodgename noborder"> </div>
<div class="oneday" ng-style="callDaystyle()" ng-class="$last ? 'lastcol' : ''" ng-repeat="d in dates">
{{d.getDate()}}
</div>
<br clear="both" />
<div ng-repeat="lodge in data.lodges">
<div class="lodgename" ng-class="$last ? 'lastrow' : ''">{{lodge.lodgetitle}}</div>
<div class="oneday" ng-style="callDaystyle()" ng-class="($last ? 'lastcol' : '') + ' ' + ($parent.$last ? 'lastrow' : '')" ng-repeat="d in dates" ng-click="showDate(lodge, d)">
</div>
<br clear="both" />
</div>
<div ng-show="data.debug" style="margin-top: 20px;"
<ul>
<li>
move = {{swipe.move}}
</li>
<li>
start = {{swipe.start}}
</li>
<li>
end = {{swipe.end}}
</li>
<li>
scope.startDay = {{startDay}}
</li>
<li>
daysMoved = {{swipe.daysMoved}}
</li>
<li>
daysFinished = {{nDaysFinished}}
</li>
</ul>
</div>
<ul>
<?php foreach(Yii::app()->params['lodges'] as $lodgecode=>$lodge) {
echo "<li>" . $lodgecode . " = " . $lodge['lodgetitle'] . "</li>";
$lci = $this->lodgeconfigs[$lodgecode]['PREFIX_BOOKING_ID'];
echo "<LI>" . $lci . "</li>";
} ?>
</ul>
</div>
</div>
Javascript:
/* NG services */
var matrix = angular.module('Skimobile', ['ngTouch']);
matrix.factory('dayswidth', ['writeDays', function(){ // gets called on window change width
return function(scope, ww) {
var lodgename = $('.lodgename').first().width() + 4; // 4 = padding in lodgename class
var padding = 60 + lodgename;
var oldDisplayDays = scope.displayDays;
scope.displayDays = Math.min(28, (ww - padding)/scope.mindaywidth); // show at most 28 days, fewer if needed to maintain minimum width
scope.dayWidth = Math.floor( (ww-padding) / scope.displayDays );
if(oldDisplayDays!=scope.displayDays) { // avoid changing scope.dates as this will cause infinite recursion in the $digest on ng-repeat d in dates
scope.callWriteDays();
}
};
}]);
matrix.factory('writeDays', function() {
return function(scope) {
var d = new Date();
d.setTime(scope.startDay.getTime());
scope.dates = []; // repeat on this to draw days
scope.months = []; // repeat on this to draw months
var yearShown = false; // only show year once, when the month is long enough
var m = d.getMonth();
var daysLeft = 0; // days shown belonging to each month
for(i=0; i<scope.displayDays; i++) {
scope.dates.push(new Date(d.getTime()));
var oldd = new Date(d.getTime());
d.setDate(d.getDate()+1);
daysLeft++;
var newm = d.getMonth();
if(newm!=m) { // finished a month, display it
var newMonthObj = {month:m, days:daysLeft};
if(!yearShown && daysLeft*scope.dayWidth-1-4>3*scope.mindaywidth) {
newMonthObj.year = oldd.getFullYear();
yearShown = true;
} else {
newMonthObj.year = '';
}
scope.months.push(newMonthObj);
m = newm;
daysLeft = 0;
}
}
if(daysLeft>0) { // final month
newMonthObj = {month:m, days:daysLeft};
newMonthObj.year = yearShown ? '' : oldd.getFullYear();
scope.months.push(newMonthObj);
}
}
});
matrix.factory('daystyle', function(){
return function(scope) {
return {
'width': (scope.dayWidth-1) + 'px'
}; // -1 allows for border
}
});
matrix.factory('monthstyle', function(){
return function(scope, m) {
var days = m.days;
return {
'width': (scope.dayWidth*days-1-4) + 'px'
} // 1 for border, 4 for padding-left
}
});
matrix.directive('onFinishDays', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attr) {
if(scope.$last === true) { // without this it gets called once per visible month!
$timeout(function() {
scope.$emit('daysFinished');
})
}
}
}
});
/* NG controllers */
function matrixCtrl($scope, dayswidth, writeDays, daystyle, monthstyle, $swipe) {
$scope.callDayswidth = function(w){
dayswidth($scope, w);
};
$scope.callDaystyle = function() {
return daystyle($scope);
}
$scope.callMonthstyle = function(m) {
return monthstyle($scope, m);
}
$scope.callWriteDays = function() {
return writeDays($scope);
}
$scope.data = _main; // passed via Yii layout file
$scope.mindaywidth = 30;
$scope.monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var d = new Date(); // initially the matrix starts from today
$scope.startDay = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
var w = $(window);
$scope.getWindowDimensions = function() {
return {'h': w.height(), 'w': w.width()};
};
$scope.$watch($scope.getWindowDimensions, function(newValue, oldValue){
$scope.callDayswidth(newValue.w);
}, true);
w.bind('resize', function() {
$scope.$apply();
})
$scope.showDate = function(lodge, d){
alert(lodge.lodgetitle + ' ' + d.getDate());
}
$scope.swipe = {};
$scope.nDaysFinished = 0;
$scope.$on('daysFinished', function(event) {
$scope.nDaysFinished++;
$swipe.bind($('.oneday'), {
end:function(loc){
$scope.swipe.end = loc.x;
$scope.swipe.daysMoved = Math.floor((loc.x - $scope.swipe.start) / $scope.dayWidth);
$scope.startDay.setTime($scope.startDay.getTime() - $scope.swipe.daysMoved*24*60*60*1000); // compute new start day at end of drag;
$scope.callWriteDays();
$scope.$apply();
}
})
});
}