I have an angular application where an input field should allow only positive numbers with one decimal point. In my directive I am replacing anything other than 0-9 and '.'.But currently my application is accepting multiple decimal values.
It should accept:
0.5
0.56
Not
0.5.5 or 0..5
PFB the code:
link: function (scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
// this next if is necessary for when using ng-required on your input.
// In such cases, when a letter is typed first, this parser will be called
// again, and the 2nd time, the value will be undefined
if (inputValue === undefined) {
return '';
}
var transformedInput = inputValue.replace(/[^0-9\.]/g, '');
if (transformedInput !== inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
This question might seem ignorant but I have tried all the solutions provided before this but changing my regular expression according to the same doesn't seem to work. It accepts right now multiple '.'.
Thanks in advance.
Here is fiddle http://jsfiddle.net/oora0t93/ check it:-
app.directive('inputPrice', function () {
return {
restrict: 'EA',
template: '<input name="{{inputName}}" ng-model="inputValue" />',
scope: {
inputValue: '=',
inputName: '='
},
link: function (scope) {
scope.$watch('inputValue', function(newValue,oldValue) {
if(String(newValue).indexOf(',') != -1)
scope.inputValue = String(newValue).replace(',', '.');
else {
var index_dot,
arr = String(newValue).split("");
if (arr.length === 0) return;
if (arr.length === 1 && (arr[0] == '-' || arr[0] === '.')) return;
if (arr.length === 2 && newValue === '-.') return;
if (isNaN(newValue) || ((index_dot = String(newValue).indexOf('.')) != -1 && String(newValue).length - index_dot > 3 )) {
scope.inputValue = oldValue;
}
}
});
}
};
});
I got it. I did not find a regular expression solution so did it using some javascript string manipulations. PFB the code:
var firstIndexOfDecimal = transformedInput.indexOf('.');
var lastIndexofDecimal = transformedInput.lastIndexOf(".");
if(firstIndexOfDecimal !== lastIndexofDecimal){
transformedInput = transformedInput.substr(0,lastIndexofDecimal) + transformedInput.substr(lastIndexofDecimal+1, transformedInput.length);
}
Thanks for the help.
Related
I'm kinda new to Angular and Regex and I'm currently stuck with a problem. I need to create an Angular Directive that allows an input field to do the following:
Alphanumeric only
First input must be a letter
One space only between words
No special character
I've done numbers 1-3 my problem comes in #4. The input still accepts '_' , '`', '&', 'ˆ' and some other special characters. I just need to negate all special characters.
Here is my directive:
export class AlphaNumericFieldDirective{
private regex: RegExp = new RegExp(/^[a-zA-Z]([a-zA-Z0-9]+ ?)*$/);
private specialKeys: Array<string> = ['Backspace', 'Space', 'Tab', 'End', 'Home'];
constructor(private el: ElementRef) {}
#HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if (this.specialKeys.indexOf(event.key) !== -1) {
return;
}
let current: string = this.el.nativeElement.value;
let next: string = current.concat(event.key);
if (next && !String(next).match(this.regex)) {
event.preventDefault();
}
}
}
And here is the sample output featuring some of the characters I want to negate
I would appreciate the help, thanks
This solution is working for me.
import { Directive, HostListener } from "#angular/core";
#Directive({
selector: "[appAlphabetOnly]",
})
export class AlphabetOnlyDirective {
#HostListener("keydown", ["$event"]) public onKeydown(event: KeyboardEvent) {
if (
(event.keyCode >= 15 && event.keyCode <= 64) ||
event.keyCode >= 123 ||
(event.keyCode >= 96 && event.keyCode <= 105)
) {
event.preventDefault();
}
}
}
I have been trying to do a regexp to force the user to just type what I want in the input and NOT display it in the input field. I have try use many events, but the event type it on the input field and have to make an extra validation or found some errors using the code. Never found the correct event.
this is my html file:
<input [(ngModel)]="someModel" (keydown)="validate($event);">
this is my component
validateRegex(event: any) {
let regexp = new RegExp(/^([0-9][0-9]?)(\.([0-9][0-9]?)?)?$/);
let keyPress = event.key;
if(!event.ctrlKey && (event.altKey || event.shiftKey)) {
event.preventDefault();
event.stopPropagation();
}
if(!event.ctrlKey && keyPress.length < 2) { //Make sure it just press 1 digit, avoid stuff like "backspace"
if(keyPress == '0' || keyPress == '1' || keyPress == '2' || keyPress == '3' || keyPress == '4' || keyPress == '5'
|| keyPress == '6' || keyPress == '7' || keyPress == '8' || keyPress == '9' || keyPress == '.' ) {
let test = this.someModel ? this.someModel.toString() + event.key: event.key;
if (!regexp.test(test)) {
event.preventDefault();
event.stopPropagation();
}
} else {
event.preventDefault();
event.stopPropagation();
}
}
}
Now this validate the user to insert just decimals with 2 digits after and before the dot, also doesn't allow the dot to be on his own. The problem with this code, is that if the user types '99.' and then clicks/arrows backwards to the first 9, he will be able to insert a 9 at the beginning, breaking the regular expression. event just only got the key inserted, not sure where is going to be inserted.
If I don't use keydown/keypress the display input will change already, the validation will be made, but the UI will change. In keydown, the UI is still not changed. I don't want to block the user from moving around the input neither
Now yes, I could solve this problem using a mask, but the library mask I got have problems and have some errors too. Shamefully I cannot use any other.
Can this be solve with the proper event or asking for the altered value somehow on the keydown or It is possible to create my own mask and solve this?
html
<input #input [ngModel]="value" (ngModelChange)="onChange($event);">
ts
import { Component, ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
value = '0';
#ViewChild('input', { static: false }) input;
onChange(value: string) {
let regexp = /^([0-9][0-9]?)(\.([0-9][0-9]?)?)?$/;
if (!value) {
this.value = '';
}
if (!regexp.test(value)) {
this.input.nativeElement.value = this.value
} else {
this.value = value;
}
}
}
https://stackblitz.com/edit/angular-phfkcy?file=src%2Fapp%2Fapp.component.html
I can't seem to fetch new data in Internet Explorer 9. For the purpose of an example I test the store this way:
App.__container__.lookup('store:main').find('style')
The only error I receive is the following:
SCRIPT5022: Error: Assertion Failed: [object Object]
Does Ember-data works out of the box (without polyfills, ...) in Internet Explorer 9?
versions:
Ember: 1.9.1
Ember-data: 1.0.0-beta.12
Problem solved. When doing an AJAX request with jQuery, this normally happens through the XMLHttpRequest object.
On IE8-9, this object is not present, instead it uses XDomainRequest. The simplest fix for this is adding: https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest.
ember-data works out of the box with IE8+. According to this issue:
We've been supporting IE8 with our platform (built on Ember) for a
while now. Things I know:
shim/sham is not needed, it's polyfilled by Ember and Ember-Data.
You will need it if you want additional things like .bind() on a function, then you must prepend it to the vendor file (using Brocfile)
and we only include the shim for that purpose, not the sham
Solution Synthesis
Reason :
On IE8-9, this object is not present, instead it uses XDomainRequest.
Solution :
The issue is solved. When using an AJAX request with jQuery. Normally this is done through the XMLHttpRequest object. A simple fix would be using the Open-Source jQuery-ajaxTransport-XDomainRequest
Code : Adding :
jQuery-ajaxTransport-XDomainRequest.js
/*!
* jQuery-ajaxTransport-XDomainRequest - v1.0.4 - 2015-03-05
* https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
* Copyright (c) 2015 Jason Moon (#JSONMOON)
* Licensed MIT (/blob/master/LICENSE.txt)
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals.
factory(jQuery);
}
}(function($) {
// Only continue if we're on IE8/IE9 with jQuery 1.5+ (contains the ajaxTransport function)
if ($.support.cors || !$.ajaxTransport || !window.XDomainRequest) {
return $;
}
var httpRegEx = /^(https?:)?\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^(\/\/|' + location.protocol + ')', 'i');
// ajaxTransport exists in jQuery 1.5+
$.ajaxTransport('* text html xml json', function(options, userOptions, jqXHR) {
// Only continue if the request is: asynchronous, uses GET or POST method, has HTTP or HTTPS protocol, and has the same scheme as the calling page
if (!options.crossDomain || !options.async || !getOrPostRegEx.test(options.type) || !httpRegEx.test(options.url) || !sameSchemeRegEx.test(options.url)) {
return;
}
var xdr = null;
return {
send: function(headers, complete) {
var postData = '';
var userType = (userOptions.dataType || '').toLowerCase();
xdr = new XDomainRequest();
if (/^\d+$/.test(userOptions.timeout)) {
xdr.timeout = userOptions.timeout;
}
xdr.ontimeout = function() {
complete(500, 'timeout');
};
xdr.onload = function() {
var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
var status = {
code: 200,
message: 'success'
};
var responses = {
text: xdr.responseText
};
try {
if (userType === 'html' || /text\/html/i.test(xdr.contentType)) {
responses.html = xdr.responseText;
} else if (userType === 'json' || (userType !== 'text' && /\/json/i.test(xdr.contentType))) {
try {
responses.json = $.parseJSON(xdr.responseText);
} catch(e) {
status.code = 500;
status.message = 'parseerror';
//throw 'Invalid JSON: ' + xdr.responseText;
}
} else if (userType === 'xml' || (userType !== 'text' && /\/xml/i.test(xdr.contentType))) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = false;
try {
doc.loadXML(xdr.responseText);
} catch(e) {
doc = undefined;
}
if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
status.code = 500;
status.message = 'parseerror';
throw 'Invalid XML: ' + xdr.responseText;
}
responses.xml = doc;
}
} catch(parseMessage) {
throw parseMessage;
} finally {
complete(status.code, status.message, responses, allResponseHeaders);
}
};
// set an empty handler for 'onprogress' so requests don't get aborted
xdr.onprogress = function(){};
xdr.onerror = function() {
complete(500, 'error', {
text: xdr.responseText
});
};
if (userOptions.data) {
postData = ($.type(userOptions.data) === 'string') ? userOptions.data : $.param(userOptions.data);
}
xdr.open(options.type, options.url);
xdr.send(postData);
},
abort: function() {
if (xdr) {
xdr.abort();
}
}
};
});
return $;
}));
I get cannot call method 'set' of undefined object when I remove item from ArrayController.
start: function () {
this.registerModel('listController', Ember.ArrayController.create());
this._super();
},
My view looks like,
{{#each item in marketingListCriteriaList}}
{{view Select valueBinding="item.entity" contentBinding="controller.allEntities" optionLabelPath="content.Name" optionValuePath="content.Name" }}
{{/each}}
I have a observer method which observes
.observes('listController.#each.entity')
The above observer gets called when i remove object from array controller using removeObject() method.
Are there any other ways to remove objects from array?
entityChangeObserver: function (thisModule) {
var thisModule = this;
var criteria = thisModule.get('listController.content');
if (criteria != undefined && criteria.length > 0 && criteria[criteria.length - 1].entity != undefined) {
var presentObject = criteria[criteria.length - 1];
$.each(thisModule.get('allEntities'), function (index, item) {
if (presentObject.entity === item.Name) {
presentObject.set('allAttributes', item.Attributes);
}
});
}
}.observes('listController.#each.entity'),
attributeChangeObserver: function (thisModule) {
var thisModule = this;
var criteria = thisModule.get('listController.content');
if (criteria != undefined && criteria.length > 0 && criteria[criteria.length - 1].attribute != undefined) {
var presentObject = criteria[criteria.length - 1];
$.each(presentObject.get('allAttributes'), function (index, item) {
if (presentObject.attribute === item.Name) {
thisModule.setDefaulsVisibility(presentObject);
if (item.Type === '1') {
presentObject.set('textVisible', true);
}
else if (item.Type === '2') {
presentObject.set('selectVisible', true);
presentObject.set('allValues', item.Values);
}
else if (item.Type === '3') {
presentObject.set('multiSelectVisible', true);
presentObject.set('allValues', item.Values);
}
else if (item.Type === '4') {
presentObject.set('dateVisible', true);
}
}
});
}
}.observes('listController.#each.attribute'),
You can also remove array elements using "remove" instead of "removeObject" however, you might want to double check your logic in your observer which gives undefined error when you remove an object. I would recommend sticking to remove object and just fixing the error within the observer. Also, do note that using "remove" will not instantly update handlebar templates if you are looping over the array.
Firstly sorry for the late post.
I figured the solution for the problem "calling set on destroyed object".
In my control definition of didInsertElement I made a check for if (!me.isDestroyed) for every set operation.
According to this question, it was possible to do something like this with Handlebars rc1:
{{#each links}}
<li>{{#index}} - {{url}}</li>
{{/each}}
{{#index}} would basically give you the iteration index, which is really useful when creating tables.
When I try this with Ember.js rc3, I get an unexpected token error. Does this not work anymore? Did it ever work? Is there another way to get the iteration index?
It looks like it was possible. Can't get it to work with HBS RC3. Probably, is deprecated.
Here's a "hand written" HBS helper.
This can help you gettin the index with {{index}} and side by side you can know if the iteration in on first or last object of the Array with {{first}} and {{last}} respectively.
Ember.Handlebars.registerHelper("foreach", function(path, options) {
var ctx;
var helperName = 'foreach';
if (arguments.length === 4) {
Ember.assert("If you pass more than one argument to the foreach helper, it must be in the form #foreach foo in bar", arguments[1] === "in");
var keywordName = arguments[0];
options = arguments[3];
path = arguments[2];
helperName += ' ' + keywordName + ' in ' + path;
if (path === '') {
path = "this";
}
options.hash.keyword = keywordName;
} else if (arguments.length === 1) {
options = path;
path = 'this';
} else {
helperName += ' ' + path;
}
options.hash.dataSourceBinding = path;
// Set up emptyView as a metamorph with no tag
//options.hash.emptyViewClass = Ember._MetamorphView;
// can't rely on this default behavior when use strict
ctx = this || window;
var len = options.contexts[0][path].length;
options.helperName = options.helperName || helperName;
options.contexts[0][path].map(function(item, index) {
item.index = index;
item.first = index === 0;
item.last = index === len - 1;
})
if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
new GroupedEach(ctx, path, options).render();
} else {
return Ember.Handlebars.helpers.collection.call(ctx, Ember.Handlebars.EachView, options);
}
});
and this can be tested like
{{#foreach array}}
{{log index first last}}
{{/foreach}}
i had the same problem recently i finish by writing a bound helper and passing them objects via Binding for example item here is an ember DS.Store object and content is a 'content' of the controller. hope it
Ember.Handlebars.registerBoundHelper 'isPair', (content, options)->
item = options.hash.item
content_name = options.hash.content || 'content'
if #get(content_name).indexOf(item) % 2 == 0 then 'is-pair' else 'is-unpair'
and in you view you call it
{{isPair content itemBinding='order'}}
i don't know if it is what you looking for but it might give you some ideas how to use it in your project.
btw. Ember overwrites #each helper that's why there it no #index i suppose