Angularjs custom filter not working - html

I am trying to filter elements based on the range. I am using two controllers & $rootScope broadcast-on approach to retrieve the min-max range of a slider & sharing it with the other controller.
HTML-
<body ng-app="myApp">
<div ng-controller="RangeController as vm">
<rzslider rz-slider-model="vm.slider.minValue" rz-slider-high="vm.slider.maxValue" rz-slider-options="vm.slider.options"></rzslider>
</div>
<div ng-controller="SampleController">
<div ng-repeat="x in elements | inRange:min:max">
{{x}}
</div>
</div>
</body>
AngularJS-
var app = angular.module('myApp', ['rzModule']);
app.controller('SampleController', function($scope,$rootScope) {
$scope.min = 1500;
$scope.max = 5500;
$scope.elements = [1530,2100,2780,3323,3420,4680,5020,5300,5402];
$scope.$on('MIN_PRICE', function(response) {
$scope.min = minPrice;
})
$scope.$on('MAX_PRICE', function(response) {
$scope.max = maxPrice;
})
});
app.value('minPrice',1500);
app.value('maxPrice',5500);
app.controller('RangeController', RangeController);
function RangeController($scope,$rootScope) {
var vm = this;
vm.changeListener = function() {
minPrice = vm.slider.minValue;
maxPrice = vm.slider.maxValue;
console.log(minPrice + " " +maxPrice);
$rootScope.$broadcast('MIN_PRICE', minPrice);
$rootScope.$broadcast('MAX_PRICE', maxPrice);
};
vm.slider = {
minValue: 1500,
maxValue: 5500,
options: {
floor: 1500,
ceil: 5500,
step: 500,
translate: function(value) {
return '₹' + value;
},
onChange:vm.changeListener
}
}
}
app.filter('inRange', function() {
return function(array, min, max) {
array = array.filter(function(element) {
return (element >= min && element <= max);
});
console.log(array);
};
});
I tried debugging, the filter works fine but it won't reflect in the template.

The self-assignment to array inside your filter (array = array.filter(…);) seems slightly suspicious to me. Have you tried simply returning array.filter(…); directly?
app.filter('inRange', function() {
return function(array, min, max) {
return array.filter(function(element) {
return (element >= min && element <= max);
});
};
});

Related

Test angular directive that adds $parser

I have a directive that validates text to be in a specific format:
angular.module('app')
.directive('validNumber', validNumber);
function validNumber() {
var directive = {
restrict: 'A',
require: '?ngModel',
link: linkFunc
};
return directive;
function linkFunc(scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$parsers.push(function (val) {
if (angular.isUndefined(val)) {
var val = '';
}
var clean = val.replace(/[^0-9\.]/g, '');
var decimalCheck = clean.split('.');
if (!angular.isUndefined(decimalCheck[1])) {
decimalCheck[1] = decimalCheck[1].slice(0, 2);
clean = decimalCheck[0] + '.' + decimalCheck[1];
}
if (val !== clean) {
ngModelCtrl.$setViewValue(clean);
ngModelCtrl.$render();
}
return clean;
});
element.bind('keypress', function (event) {
if (event.keyCode === 32) {
event.preventDefault();
}
});
}
}
Now I want to test the inner parser function I added and I just can't do it. How can I invoke a call to that function? How can I test the result? My very unsuccessful tests are:
describe('validNumber directive specs', function () {
var scope, compile;
var validHtml = '<form name="testForm"><input name="test" type="text" valid-number ng-model="str" /></form>';
beforeEach(function () {
angular.mock.module('dashboardApp');
module(bootstrapperMock);
inject(function (_$rootScope_, _$compile_) {
scope = _$rootScope_.$new();
compile = _$compile_;
});
});
describe('When a key press occures', function () {
it('should :( ', function () {
scope.str = 0;
var element = compile(validHtml)(scope);
var viewValue = 2, input = element.find('input');
scope.str = viewValue;
scope.$digest();
var e = angular.element.Event('keypress keydown');
e.which = 50;
element.trigger(e);
scope.$digest();
});
});
});
I tried both changing the model and triggering a keypress.
Thanks!
The following works:
describe('When a key press occures', function () {
it('when a key press', function () {
var expected = '';
var element = compile(validHtml)(scope);
element.val('asda');
element.trigger('input');
var actual = element.val();
expect(expected).toBe(actual);
});
});
I also updated the html in this spec:
var validHtml = '<input name="test" type="text" valid-number ng-model="str" />';
The magic here is to trigger 'input' for the element.

How to angular $watch element height after class change by model?

I've read and tried probably every thread on angular $watch() DOM element height but can't work out how to do this. Any help is greatly appreciated!
I have an angular app that does a simple class name update by changing a model value. Example:
class="theme-{{themeName}}"
When the class updates the DIV changes height.
I want to receive a callback on the height change.
I've tried to use $watch() and $watch(..,,true) and using both angular.element() as well as jquery ( $('foo')... ) but the $digest cycle never even calls the $watch expression.
Update (code example):
'use strict';
angular.module('k2')
.directive('k2', ['$rootScope', '$templateCache', '$timeout', 'lodash' ,'k2i',
function ($rootScope, $templateCache, $timeout, lodash, k2i) {
return {
restrict: 'E',
template: $templateCache.get('k2/templates/k2.tpl.html'),
replace: true,
scope: {
ngShow: '=',
ngHide: '=',
settings: '='
},
link: function(scope, elem, attrs) {
k2i.initK2(scope, scope.settings || {});
scope.$watch(function() {
return $('.k2 .k2-template [k2-name]').height();
}, function(newValue, oldValue, scope) {
respondToChange(newValue, oldValue, scope);
}, true);
scope.$watch(function() {
var kb = document.querySelectorAll('.k2 .k2-template [k2-name]')[0];
var ab = document.querySelectorAll('.k2 .k2-template [k2-name] .k2-acc-bar')[0];
var value = {
kb: 0,
ab: 0
}
if (kb) {
value.kb = kb.clientHeight;
}
if (ab) {
value.ab = ab.clientHeight;
}
return value;
}, function(newValue, oldValue, scope) {
respondToChange(newValue, oldValue, scope);
}, true);
function respondToChange(newValue, oldValue, scope) {
if (newValue === oldValue) return;
if (!scope.k2Pending) return;
var kbNode = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Pending.name + '"]');
var abNode = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Pending.name + '"] .k2-acc-bar');
// Ensure required keyboard elements are in the DOM and have height.
if ((kbNode.length > 0 && !scope.k2Pending.requiresAccessoryBar ||
kbNode.length > 0 && abNode.length > 0 && scope.k2Pending.requiresAccessoryBar) &&
(kbNode[0].clientHeight > 0 && !scope.k2Pending.requiresAccessoryBar ||
kbNode[0].clientHeight > 0 && abNode[0].clientHeight > 0 && scope.k2Pending.requiresAccessoryBar)) {
$rootScope.$emit('K2KeyboardInDOM', scope.k2Pending.name, getHeight());
}
};
function getHeight() {
var height = {};
var kbElem = angular.element(document.querySelectorAll('.k2')[0]);
var wasHidden = kbElem.hasClass('ng-hide');
kbElem.removeClass('ng-hide');
height[k2i.modes.NONE] = 0;
height[k2i.modes.ALL] = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Name + '"]')[0].clientHeight;
height[k2i.modes.ACCESSORY_BAR_ONLY] = document.querySelectorAll('.k2 .k2-template [k2-name="' + scope.k2Name + '"] .k2-acc-bar')[0].clientHeight;
height[k2i.modes.KEYBOARD_KEYS_ONLY] = height[k2i.modes.ALL] - height[k2i.modes.ACCESSORY_BAR_ONLY];
if (wasHidden) {
kbElem.addClass('ng-hide');
}
return height;
};
}
}
}
]);
You should simply watch the 'themeName' variable. Then do your calculations in $timeout. $timeout should be required to wait your manual DOM updates. For ex:
scope.$watch('k2Name', function(newValue, oldValue){
$timeout(function(){
//do what you want
});
});

Validate credit card expiration with angular?

I can not validate my date field.
The idea is that when the user enter the date validate if the card is expired. I made this directive but I am a little lost with the directives of angular.
checkOut.directive('cardDateExpiration', function() {
return {
require: 'ngModel',
link: function(date) {
var currentDate = new Date();
var m, y, d;
if (/^\d{2}\/\d{2}$/.test(date)) {
m = date.substring(0, 2) - 1;
y = 20 + date.slice(-2);
d = new Date(y, m);
} else if (/^\d{2}\/\d{4}$/.test(date)) {
m = date.substring(0, 2) - 1;
y = date.slice(-4);
d = new Date(y, m);
} else if (/^\d{4}$/.test(date)) {
m = date.substring(0, 2) - 1;
y = 20 + date.slice(-2);
d = new Date(y, m);
}
return currentDate > d;
}
}
});
<div class="large-6 columns sd-items-form">
<label>
<input id="expiry_date" maxlength="5" name="datacard" card-date-expiration ng-disabled="" class="sd-field sd-txt-center p-l-0" ng-model="form.data.datacard" type="text" type placeholder="MM / YY" required></input>
</label>
<div class="error" ng-if="checkoutPayment.$submitted || checkoutPayment.datacard.$touched" ng-messages="checkoutPayment.datacard.$error">
<p class="text-msg" ng-message="required">Not valid date credit card</p>
</div>
</div>
I have this example of how to do custom validation of input fields in angular here: http://jsfiddle.net/fortesl/2uv6xmjL/6/
I am using momentjs to validate a date, which I recommend, but you can also parse the input string if you like (I do not recommend it). Code is shown below:
app.directive('dateFieldValidator', [function () {
var validateDate = function (date, format) {
if (!date.length) {
return true;
}
return moment(date, format.toUpperCase(), true).isValid();
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
dateFormat: '#'
},
link: function (scope, elem, attrs, ngModelCtrl) {
//For DOM -> model validation
ngModelCtrl.$parsers.unshift(function (value) {
var valid = validateDate(value, scope.dateFormat);
ngModelCtrl.$setValidity('validDate', valid);
return valid ? value : undefined;
});
//For Model Update --> DOM
ngModelCtrl.$formatters.unshift(function (value) {
var valid = validateDate(value, scope.dateFormat);
ngModelCtrl.$setValidity('validDate', valid);
return value;
});
}
};
}]);
and here is a sample html which uses the directive:
<div ng-app="dateApp">
<div ng-controller="DateController as dateCtrl">
<form name="dateForm" novalidate
ng-submit="dateCtrl.setDate(dateForm.dateInput.$valid); dateForm.dateInput.$setPristine();">
Enter date:
<input name="dateInput" ng-model="dateCtrl.date" date-format="{{dateCtrl.format}}"
date-field-validator placeholder="{{dateCtrl.format}}">
<button ng-disabled="dateForm.dateInput.$pristine">Submit</button>
</form>
</div>
A sample controller:
var app = angular.module('dateApp', []);
app.controller('DateController', function () {
var self = this;
self.format = 'MM/DD/YYYY';
self.date = '';
self.setDate = function (valid) {
if (valid) {
window.alert('You entered a valid Date: ' + self.date);
} else {
window.alert('!!!!!!!!!! WARNING: INVALID DATE !!!!!!!!!');
}
self.date = '';
}
})

Polymer - reload core-list data

I wanted reload a core-list element to show new data, but it´s not refreshing.
I re-call the JS function thats generate the data but doesn t work... and reload like a 'normal' div doesn t work either! The list only shows the new data if i reload the entire page...
function values(sender, textomsg, datacriacao, senderfoto){
var sender2 = sender.split(",");
var textomsg2 = textomsg.split(",");
var datacriacao2 = datacriacao.split(",");
var senderfoto2 = senderfoto.split(",");
var namegen = {
generateString: function (inLength) {
var s = '';
for (var i = 0; i < inLength; i++) {
s += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
return s;
},
generateName: function (inMin, inMax) {
return this.generateString(Math.floor(Math.random() * (inMax - inMin + 1) + inMin));
}
};
Polymer('list-test', {
count: sender.length,
ready: function () {
this.data = this.generateData();
},
generateData: function () {
var names = [], data = [];
for (var i = 0; i < this.count; i++) {
names.push(namegen.generateName(4, 8));
}
names.sort();
for (var i = 0; i < this.count; i++) {
data.push({
index: i,
sender: sender2[i],
textomsg: textomsg2[i],
datacriacao: datacriacao2[i],
senderfoto: senderfoto2[i]
});
}
return data;
},
tapAction: function (e) {
console.log('tap', e);
}
});
}
<%----%>
<template id="templateConversas" runat="server">
<div id="item" class="item {{ {selected: selected} | tokenList }}" ><%--onClick="conversa('{{name}}');"--%>
<div class="message" style="background-image: url({{senderfoto}});">
<span class="from"><br/>{{sender}}</span>
<span class="timestamp">{{datacriacao}}</span>
<div class="subject"><br/>{{textomsg}} </div><%--------Infinite List. {{index}}--%>
<%--<div class="body"><br/>Mensagem de teste...........</div>--%>
</div>
</div>
</template>
The problem is also reload the 'list-test'. if i call the js function after the list is loaded it doesn't apply the new data...
Your code isn't complete so it is hard to understand but I think that the problem is that you don't assign the result of the generateData() function to the template's model. Try following script for your component
Polymer('list-test', {
created: function () {
this.data = [];
},
refresh: function () {
this.data = this.generateData();
},
generateData: function () {
// your original code here
}
});
Now the list content should be updated with newly generated data when you call refresh() of the list-test element. To fill the list when element is created add
ready: function () {
this.refresh();
},

watch changes on JSON object properties

I'm trying to implement a directive for typing money values.
var myApp = angular.module('myApp', []);
var ctrl = function($scope) {
$scope.amount = '0.00';
$scope.values = {
amount: 0.00
};
};
myApp.directive('currency', function($filter) {
return {
restrict: "A",
require: "ngModel",
scope: {
separator: "=",
fractionSize: "=",
ngModel: "="
},
link: function(scope, element, attrs) {
if (typeof attrs.separator === 'undefined' ||
attrs.separator === 'point') {
scope.separator = ".";
} else {
scope.separator = ",";
};
if (typeof attrs.fractionSize === 'undefined') {
scope.fractionSize = "2";
};
scope[attrs.ngModel] = "0" + scope.separator;
for(var i = 0; i < scope.fractionSize; i++) {
scope[attrs.ngModel] += "0";
};
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
};
var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;
if (pattern.test(newValue)) {
scope[attrs.ngModel] += "00";
return;
};
}, true);
}
};
});
HTML template:
<div ng-app="myApp">
<div ng-controller="ctrl">
{{amount}}<br>
<input type="text" style="text-align: right;" ng-model="amount" currency separator="point" fraction-size="2"></input>
</div>
</div>
I want to bind the value in my input element to values.amount item in controller but the watch instruction of my directive doesn't work.
How do I leverage two-way-data-binding to watch JSON objects?
To understand problem more precise I've created a jsfiddle.
The task is the following: Add extra zeros to the input element if user put a point. I mean if the value in input element say "42" and user put there a point, so the value now is "42." two extra zeros have to be aded like this "42.00".
My problems:
If I use ng-model="amount" the logic in input element works, but amount value of outer controller doesn't update.
If I use ng-model="values.amount" for binding, neither amount of outer controller nor input element logic works.
I really have to use ng-model="values.amount" instruction, but it doesn't work and I don't know why.
Any ideas?