ngModelController: update view value after parser executed - angularjs

I have the following directive:
myDirectives.directive('simpleDate', function ($filter, $locale, $timeout) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var dateFormat = 'shortDate';
ctrl.$formatters.unshift(function (modelValue) {
if (!modelValue) {
return '';
} else {
return $filter('date')(modelValue, dateFormat);
}
});
ctrl.$parsers.unshift(function (viewValue) {
if (!isNaN(viewValue)) {
var now = new Date(),
year = now.getFullYear(),
month = now.getMonth() + 1,
day = Number(viewValue),
lastDay = (new Date(year, month, 0)).getDate();
if (day >= 1 && day <= lastDay) {
return '' + year + (month < 10 ? '-0' : '-') +
month + (day < 10 ? '-0' : '-') + day;
}
}
return '';
});
}
};
});
Parser calculates model value, and needs to reflect back view value; but how? I know there is ctrl.$render() method, but I guess it must run after $parser executed. The following didn't work:
$timeout(function () { ctrl.$render(); });
How can I render view value then?

Is this what you expect? :
myDirectives.directive('simpleDate', function ($filter, $locale, $timeout) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var dateFormat = 'shortDate';
ctrl.$formatters.unshift(function (modelValue) {
if (!modelValue) {
return '';
} else {
return $filter('date')(modelValue, dateFormat);
}
});
ctrl.$parsers.unshift(function (viewValue) {
if (!isNaN(viewValue)) {
var now = new Date(),
year = now.getFullYear(),
month = now.getMonth() + 1,
day = Number(viewValue),
lastDay = (new Date(year, month, 0)).getDate();
if (day >= 1 && day <= lastDay) {
var currentValue = '' + year + (month < 10 ? '-0' : '-') + month + (day < 10 ? '-0' : '-') + day;
ctrl.$setViewValue(currentValue);
ctrl.$render();
return currentValue;
}
}
return '';
});
}
};
});

Related

Remove $watch from this directive

I've wrote this directive to handle my date inputs: https://plnkr.co/edit/7hpc8u5pVc7iaNSwn7Zw?p=preview
app.directive('myDate', ['$filter', myDate]);
function myDate($filter) {
var directive = {
restrict: 'E',
template: template,
require: 'ngModel',
scope: {},
link: link
}
return directive;
function template(element, attrs) {
var template = '<input ng-model="date" ng-keyup="keyup($event.keyCode)" ui-mask="99/99/9999" type="text" ';
if (attrs.class) {
template += 'class="' + attrs.class + '"';
element.removeClass(attrs.class);
}
template += '/>';
return template;
}
function link(scope, element, attrs, ctrl) {
scope.keyup = function(key) {
if (key === 68) { // D key
scope.date = $filter('date')(new Date(), 'ddMMyyyy');
}
};
ctrl.$formatters.push(function(data) { // model to view
data = $filter('date')(data, 'ddMMyyyy');
return data;
});
ctrl.$parsers.push(function(data) { // view to model
var year = data.toString().substr(-4);
var month = data.toString().substr(2, 2);
var day = data.toString().substr(0, 2);
var sep = '-';
data = (year && month && day) ? Date.parse(year + sep + month + sep + day) : '';
return data;
});
scope.$watch('date', function() {
ctrl.$setViewValue(scope.date);
});
ctrl.$render = function() {
scope.date = ctrl.$viewValue;
};
}
}
Unfortunately I used $watch to keep my model updated... I would like to know if there is a better way to trigger $setViewValue(scope.date) without $watch; just to optimize it a bit.
Thank you!
Add this to your template:
ng-change="updateParent()"
And this to you link:
scope.updateParent = function(){
ctrl.$setViewValue(scope.date);
}

How to allow a number with digit limit and without decimal,and negative ina textfield using AngularJS directive

Here is my code.But its not working in my directive .Please help me to show errors
When initially run I enter characters(other than number) in two times it will allowed. For Eg press 'W' in two times it will allwed in that textfield.
What is the error in this code?
Here is my script
var myApp = angular.module('myApp', []);
myApp.directive('nksOnlyNumber', function () {
return {
restrict: 'EA',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
var spiltArray = String(newValue).split("");
if(attrs.allowNegative == "false") {
if(spiltArray[0] == '-') {
newValue = newValue.replace("-", "");
ngModel.$setViewValue(newValue);
ngModel.$render();
}
}
if(attrs.allowDecimal == "false") {
newValue = parseInt(newValue);
ngModel.$setViewValue(newValue);
ngModel.$render();
}
if(attrs.allowDecimal != "false") {
if(attrs.decimalUpto) {
var n = String(newValue).split(".");
if(n[1]) {
var n2 = n[1].slice(0, attrs.decimalUpto);
newValue = [n[0], n2].join(".");
ngModel.$setViewValue(newValue);
ngModel.$render();
}
}
}
if(attrs.limitDigit=="true"){
// attr.$set("ngTrim", "false");
var limitLength = parseInt(attrs.awLimitLength, 10);// console.log(attrs);
if(ngModel.$viewValue.length>limitLength){
newValue = ngModel.$viewValue.substring(0, limitLength);
ngModel.$setViewValue(newValue );
ngModel.$render();
}
}
if (spiltArray.length === 0) return;
if (spiltArray.length === 1 && (spiltArray[0] == '-' || spiltArray[0] === '.' )) return;
if (spiltArray.length === 2 && newValue === '-.') return;
/*Check it is number or not.*/
if (isNaN(newValue)) {
ngModel.$setViewValue(oldValue);
ngModel.$render();
}
});
}
};
});
Here is my textfield in html file
<b>NumberLimited Only 4 Digits</b><br>
<input type="text" nks-only-number ng-model="mynumber6" aw-limit-length="4" allow-decimal="false" allow-negative="false" limit-digit="true" /><br>
Please use the following directives
/*
*Directive to Numbers only
*use - numbers-only
*/
myApp.directive('numbersOnly', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModelCtrl) {
function fromUser(text) {
if (text) {
var transformedInput = text.replace(/[^0-9]/g, '');
if (transformedInput !== text) {
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
}
return transformedInput;
}
return false;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
});
/*
*Directive to Max legth to prevent type
*use - my-maxlength
*/
myApp.directive('myMaxlength', function() {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var maxlength = Number(attrs.myMaxlength);
function fromUser(text) {
if (text.length > maxlength) {
var transformedInput = text.substring(0, maxlength);
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
return transformedInput;
}
return text;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
})
And use like
<input name="field1"
type="text"
ng-model="model1"
numbers-only
my-maxlength="4">
Hopes this will help you !
Had something similar recently. Altered the input box:
<input {normal attributes here} data-ng-keydown="vm.checkNumber($event)" max="9999" />
Then in my controller:
vm.checkNumber = checkNumber;
function checkNumber(e) {
var keyCode = e.which || e.keyCode;
if (([9, 8, 13, 27, 37, 38, 39, 40].indexOf(keyCode) > -1) ||
(keyCode >= 49 && keyCode <= 57) ||
(keyCode >= 96 && keyCode <= 105)) {
return;
} else {
event.preventDefault();
}
}

Directive updates parent scope value

I use bootstrap-datepicker for its ability to select a range of days. I try to put it in Angular directive and expect to update the parent scope value when the date changed, and the date format must be in the whole week (20-04-2015 - 26-04-2015)
var app = angular.module('angular.controls.weekDatePicker', [])
app.directive('weekDatePicker', ['$filter', '$parse', function ($filter, $parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var ngModelParseFn = $parse(attrs.ngModel);
element.datepicker({
minViewMode: parseInt(attrs.minviewmode),
format: attrs.format,
language: "vi"
}).on('changeDate', function (e) {
scope.$apply(function () {
console.log(scope.$parent);
if (attrs.week == 'true') {
}
else {
// code
}
});
});
element.datepicker('update', new Date()); //reset to now
if (attrs.week == 'true') {
scope.$watch(function () {
return ngModelCtrl.$modelValue;
}, function (newValue) {
var date = element.data().datepicker.viewDate;
var startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay());
var endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + 6);
startDate = $filter('date')(startDate, 'dd-MM-yyyy');
endDate = $filter('date')(endDate, 'dd-MM-yyyy');
console.log(scope.$parent.fixtureDate); //fail to get parent scope value ?
var dateText = startDate + ' - ' + endDate;
ngModelParseFn.assign(scope, dateText);
});
}
scope.$on("$destroy",
function handleDestroyEvent() {
element.datepicker('remove');
}
);
}
};
}]);
View:
<input week-date-picker ng-model="fixtureDate" minviewmode="0" format="MM-yyyy" class="form-control" week="true" style="width:200px" />
Plunker source here
I've done a version with a callback (Plunker).
JS
element.datepicker({
minViewMode: parseInt(attrs.minviewmode),
format: attrs.format,
language: "vi"
}).on('changeDate', function(e) {
var date = e.date;
var firstDay = new Date(date.getFullYear(), date.getMonth(), 1); // 0 = january
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
var startDate = firstDay.getDate() + '-' + (firstDay.getMonth() + 1) + '-' + firstDay.getFullYear();
var endDate = lastDay.getDate() + '-' + (lastDay.getMonth() + 1) + '-' + lastDay.getFullYear();
scope.changeDate({startDate: startDate, endDate: endDate});
});
Note this in the directive;
scope: {changeDate: "&"},
index.html
$scope.fixtureDate = {
startDate: startDate,
endDate: endDate
};
$scope.updateFixtures = function(startDate, endDate) {
$scope.fixtureDate.startDate = startDate;
$scope.fixtureDate.endDate = endDate;
};
You should change the html to
<input week-date-picker ng-model="fixtureDate" minviewmode="0" format="MM-yyyy" class="form-control" week="true" style="width:200px" />
You have no variable called dt in your code. The variable that you want to change is called fixtureDate.

Change the ngmodel value in Angularjsdatepicker directive

I have tried to convert a bootstrap datepicker from eternicode to angularjs directive and allow to select week mode. The problem is the value {{var1}} from the view doesn't reflect to the custom value that i set on Directive
weekDatePicker directive:
var app = angular.module('angular.controls', [])
app.directive('weekDatePicker', ['$filter','$parse', function ($filter, $parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
console.log('start');
element.datepicker({
//minViewMode: 1,
//format: "MM-yyyy"
}).on('changeDate', function (e) {
scope.$apply(function () {
var date = e.date;// element.datepicker('getDate');
startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay());
endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + 6);
startDate = $filter('date')(startDate, 'dd-MM-yyyy');
endDate = $filter('date')(endDate, 'dd-MM-yyyy');
dateText = startDate + ' - ' + endDate;
e.date = dateText;
//console.log(e.date);
ngModelCtrl.$setViewValue(dateText); //>> no change from view
console.log(ngModelCtrl);
});
});
}
};
}]);
You can see the whole code at jsFiddle
View: <input week-date-picker ng-model="var1" class="form-control" />
{{var1}} >> why it doesn't get the custom value from directive ?
I have forked you fiddle
var app = angular.module('angular.controls', [])
app.directive('weekDatePicker', ['$filter','$parse', function ($filter, $parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
console.log('start');
var ngModelParseFn = $parse(attrs.ngModel);
element.datepicker({
//minViewMode: 1,
//format: "MM-yyyy",
// language: "vi"
})
scope.$watch(function () {
return ngModelCtrl.$modelValue;
}, function (newValue) {
var date = element.data().datepicker.viewDate;
startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay());
endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + 6);
startDate = $filter('date')(startDate, 'dd-MM-yyyy');
endDate = $filter('date')(endDate, 'dd-MM-yyyy');
dateText = startDate + ' - ' + endDate;
ngModelParseFn.assign(scope, dateText);
});
}
};
}]);
Hope this is what you want.

AngularJS filter with multiple arguments

In my page I have:
<h1>{{name | fontResize: 25:42}}</h1>
and I have a filter
angular.module('myApp').filter('fontResize', function () {
return function (text, length, end) {
if (!text) {
return text;
}
if (isNaN(length))
length = 10;
if (end === undefined)
end = "...";
if (text.length <= length || text.length - end.length <= length) {
$('h1').css('fontSize', '30px');
return text;
} else {
$('h1').css('fontSize', '12px');
return text;
}
};
});
How do I set the fontsize for my second argument (42) ?
Filters are not for manipulating DOM. You should create a directive.
There are 2 example directives:
First:
.directive('fontResize', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var size = attrs.size || '30px';
var length = attrs.length || 10;
attrs.$observe('text', function() {
var text = attrs.text;
if (text.length <= length) {
elem.css('fontSize', size);
} else {
elem.css('fontSize', '12px');
}
elem.text(attrs.text);
});
}
}
})
HTML:
<h1 font-resize text="{{name}}" size="42px"></h1>
And the second one:
.directive('fontResize2', function() {
return {
restrict: 'A',
scope: {},
link: function(scope, elem, attrs) {
var size = attrs.size;
var length = attrs.length || 10;
scope.$watch(function() {
return elem.text();
}, function(newVal, oldVal) {
setText(newVal)
})
function setText(text) {
if (text.length <= length) {
elem.css('fontSize', size);
} else {
elem.css('fontSize', '12px');
}
elem.text(attrs.text);
}
}
}
});
HTML:
<h1 font-resize2 size="60px">{{name}}</h1>
You can extend them as you wish.
Here is the plunkr: http://plnkr.co/edit/uO9uYqcqLPuqAhJdtJ9m?p=preview

Resources