Skip to content

Instantly share code, notes, and snippets.

@pyetras
Last active April 15, 2016 07:17
Show Gist options
  • Save pyetras/7309320 to your computer and use it in GitHub Desktop.
Save pyetras/7309320 to your computer and use it in GitHub Desktop.
Kilka komponentów z beecar.pl, angularJS w wersji 1.0 RC10. Drzewiej wiele rzeczy działało dość dziwnie/kapryśnie (np. nasłuchiwanie na wartości ngModel) a dokumentacja była biedna, także miejscami nie obyło się bez paru średnio optymalnych hacków.
//prosty datepicker, np. do daty urodzenia. Template na dole strony
.directive('datePicker', function($locale){
var strToDate = function(date){
if (_.isString(date)){
var parts = date.split('-');
return new Date(Date.UTC(parseInt(parts[0]), parseInt(parts[1], 10) - 1, parseInt(parts[2])));
}
return date;
};
return {
require: 'ngModel',
restrict: 'E',
scope: {ngModel: 'accessor', required: 'accessor'},
templateUrl: '/views/components/datepicker.html',
replace: true,
link: function(scope, element, attrs, ctrl){
var date;
ctrl.$formatters.push(function(model) {
model = strToDate(model);
if (!model) return undefined;
date = model;
scope.year = model.getUTCFullYear();
scope.month = model.getUTCMonth() + 1;
scope.day = model.getUTCDate();
return model;
});
scope.update = function() {
if (!_.any([scope.year, scope.month, scope.day], _.bind(_.isUndefined, _))){
scope.ngModel(scope.year + '-' + ("0" + scope.month).slice(-2) + '-' + ("0" + scope.day).slice(-2));
}
};
var maxYear = new Date().getFullYear();
scope.possibleYears = _.range(1901, maxYear - 18 + 1).reverse(); //no underage users!
scope.possibleMonths = _.map($locale.DATETIME_FORMATS.SHORTMONTH, function(name, id) {
return {
id: id + 1,
name: name
}
});
scope.possibleDays = function(){
if (_.isUndefined(date) || isNaN(date.getTime())) {
maxDay = 31;
} else {
maxDay = new Date (date.getUTCFullYear(), date.getUTCMonth() + 1, 0).getDate();
}
return _.range(1, maxDay + 1);
}
}
}
});
//stronicowanie. nie chcialem modyfikowac ng-repeat, dlatego komponent rozbity jest na dwie czesci:
//dyrektywa ktora tworzy kontrolke stronicowania oraz filtr, ktory nalezy umiescic na liscie ng-repeat.
//Performance nie bylo istotne przy tym rozwiazaniu.
(function(){
var defaults = {pageSize: 5, page: 0, _itemCount: 0};
angular.module('paginate', [])
.filter('attachIndex', function(){ //because of array slicing in paginate original $index from ng-repeat scope is not showing correct values
return function(arr){
var i = -1;
return _.map(arr, function(x) {
i = i + 1;
return _.isObject(x)?_.extend(x, {$index: i}):x;
});
}
})
.filter('paginate', function(){
return function(arr, model){
if (this[model] == undefined) this[model] = _.clone(defaults);
else this[model] = _.defaults(this[model], defaults);
this[model]._itemCount = arr.length;
var page = this[model].page, ps = this[model].pageSize;
var len;
if (page >= (len = Math.ceil(this[model]._itemCount/ps))){
page = Math.max(0, len - 1);
}
return arr.slice(page*ps, (page+1)*ps);
}
})
.directive('paginator', function factory(){
return {
restrict: 'E',
replace: true,
templateUrl: '/views/components/paginator.html',
scope: {
pageModel: 'accessor'
},
link: function(scope, element, attrs){
var pageSize;
scope.pages = [];
var pageModel = scope.pageModel;
if (attrs.pageSize){
var model = pageModel();
if (model == undefined) pageModel({pageSize: attrs.pageSize});
else{
model.pageSize = attrs.pageSize;
pageModel(model);
}
}
scope.next = function(){
var model = pageModel();
if (model.page + 1 < scope.pages.length){
model.page = model.page + 1;
pageModel(model);
}
};
scope.prev = function(){
var model = pageModel();
if (model.page > 0){
model.page = model.page - 1;
pageModel(model);
}
};
scope.setPage = function(i){
pageModel(_.extend(pageModel(), {page: i}));
};
scope.$watch(function(){
if (scope.pageModel() && scope.pageModel()._itemCount !== undefined &&
(scope.pageModel()._itemCount !== scope.pages._itemCount || scope.pageModel().pageSize !== pageSize)){
var page = scope.pageModel().page;
pageSize = scope.pageModel().pageSize;
scope.pages = _.map(_.range(Math.ceil(scope.pageModel()._itemCount/pageSize)), function(i) { return i == page; });
}
});
}
};
});
})();
//dyrektywa automatycznie zapisujaca/odczytujaca model do ciasteczka, do uzycia np na hidden input
//nie korzystalem z angularowego service dla cookies, bo w tamtej wersji dzialal jakos dziwnie
angular.module('persistence', [])
.directive('psPersist', function($cookieStore){
return {
require: 'ngModel',
inject: {
ngModel: 'accessor',
psPersist: 'attribute'
},
link: function(scope, element, attr){
attr.psPersist = attr.psPersist || attr.ngModel;
},
controller: function($scope, $attrs, ngModel){
var name = $attrs.psPersist || $attrs.ngModel;
var val = $.cookie(name);
if (val != null) {
val = angular.fromJson(val);
ngModel(val);
}
$scope.$watch(function() {
return ngModel()
}, function() {
$.cookie(name, angular.toJson(ngModel()), {path: '/'});
});
}
};
});
<span>
<select style="width: 65px" ng-model="year" ng-options="value for value in possibleYears" required="required()" ng-change="update()">
<option value="">Rok:</option>
</select>
<select style="width: 80px" ng-model="month" ng-options="month.id as month.name for month in possibleMonths" required="required()" ng-change="update()">
<option value="">Miesiąc:</option>
</select>
<select style="width: 60px" ng-model="day" ng-options="value for value in possibleDays()" required="required()" ng-change="update()">
<option value="">Dzień:</option>
</select>
</span>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment