Is there anyway to detect removed character from a text using ngModel in Angular 2 ?
I want something like:
Original text: #Hello World !
Modified text : Hello World !
Console.log
Removed character: '#'
I've found a cool example on Javascript with Jquery by Arie Xiao below:
https://stackoverflow.com/a/17005983/5668956
But I wonder if I can use another thing beside Jquery, as I find that Jquery is pretty hard to implement in Angular 2
I would suggest a Forms controller for acting on changes on the input field. The snippet displays the character add or deleted from the input field.
Check out my Plunker to see the code running.
#Component({
selector: 'app-root',
template: `
<input type="text" [formControl]="form" class="form-control" >
<h3 *ngIf="initial">
Text <span [style.color]="textadd ? 'green' : 'red'" > {{textadd ? "add" : "removed" }} </span>: {{change}}
</h3>
`,
})
export class App {
form;
textadd;
text = "#Hello World!";
initial = false;
change;
ngOnInit() {
this.form = new FormControl({ value: this.text, disabled: false });
this.form.valueChanges.subscribe(val => {
let change;
if (val.length > this.text.length) {
change = val;
for (let variable of this.text) {
change = change.replace(variable, '');
this.textadd = true;
}
} else {
change = this.text;
for (let variable of val) {
change = change.replace(variable, '');
this.textadd = false;
}
}
this.change = change;
this.text = val;
this.initial = true;
});
}
}
Related
I am having some trouble trying to change the css of a button with the (click) event. I managed to do it, but the problem is that I have 10 buttons, so I can't depend on one variable in the .ts because once it changes, it will affect all 10 buttons and not just the one which was clicked, so the only thing I thought was having 10 different variables, but it is not quite elegant. Is there any way of doing it a bit cleaner?
Here is what I've got so far:
html:
<button (click)="b1 = !b1" class="tarea" [id]="cambiaId(b1)"></button>
<button (click)="b2 = !b2" class="tarea" [id]="cambiaId(b2)"></button>
<button (click)="b3 = !b3" class="tarea" [id]="cambiaId(b3)"></button>
[...]
<button (click)="b10 = !b10" class="tarea" [id]="cambiaId(b10)"></button>
ts:
export class TareasComponent {
b1 : boolean = false;
b2 : boolean = false;
b3 : boolean = false;
[...]
b10 : boolean = false;
cambiaId(b : boolean){
if (b) {
return "done";
}else{
return "todo";
}
}
I think you might have some valid reason to have duplicate id for elements. To do it in performant way in angular would be to do with ngFor and trackby. A sample imlementation is available at CodeSandbox Implementation
buttons: ButtonType[] = Array(10)
.fill("todo")
.map((value, i) => ({ id: i, value }));
trackById(index: number, button: ButtonType) {
return button.id;
}
buttonClicked(index: number, button: ButtonType) {
const ret = this.buttons.slice(0);
ret[index] = {
...button,
value: button.value ? "done" : "todo"
};
this.buttons = ret;
}
<ng-container *ngFor="let item of buttons; index as i; trackBy:trackById">
<button [id]="item.value" (click)="buttonClicked(i, item)" class="tarea">
{{item.value}}
</button>
</ng-container>
<!--language:lang-html-->
<div class="form-group m-b-40 ">
<input type="text" class="form-control" id="input1">
<span class="bar"></span>
<span class="error_form" id="bname_error_message"></span>
<label for="input1">Regular Input</label>
</div>
In the above html I need to add "form-control-success" class to the input element and keep it true as long as it complies with the state if (pattern.test(bname) && bname !== '')
The same logic should also be applied to the parent element of input. But this time different class "has-success" should be added to the parent class and keep it untill it meets the same condition.
For other cases like else if(bname !== '') and (!pattern.test(bname)) the classes "form-control-success" and "has-success" that has been added to input and its parent respectively should be replaced with their opposite classes "form-control-warning" and "has-warning". This process is bind to "keyup" event. I wonder if there's a method or an elegant way that will reduce the lines of code and keep it simple.
In the clumsy way, the code looks like this:
<!--language: lang-js-->
$("#input1").keyup(function(){
check_bname();
});
function check_bname() {
var pattern = /^[a-zA-Z]*$/;
var bname = $("#input1").val();
if (pattern.test(bname) && bname !== '')
{
$("#bname_error_message").hide();
$("#input1").removeClass("form-control-warning");
$("#input1").parents(".form-group").removeClass("has-warning")
$("#input1").parents(".form-group").addClass("has-success")
$("#input1").addClass("form-control-success");
}
else if(bname === '')
{
$("#bname_error_message").html("Should not be empty");
$("#bname_error_message").show();
$("#input1").removeClass("form-control-success");
$("#input1").parents(".form-group").removeClass("has-success")
$("#input1").addClass("form-control-warning");
$("#input1").parents(".form-group").addClass("has-warning")
}
else
{
$("#bname_error_message").show();
$("#bname_error_message").html("Should contain only Characters");
$("#input1").removeClass("form-control-success");
$("#input1").parents(".form-group").removeClass("has-success")
$("#input1").addClass("form-control-warning");
$("#input1").parents(".form-group").addClass("has-warning")
}
}
here is a version of your code with some more brevity to it and using more dry coding (less repetition), however i havent been able to try the code so it may contain a bug or two, you need to try it before you run, but i hope you get general idea:
<!--language:lang-jquery-->
$elemInput.keyup(function(){
check_bname();
});
function check_bname() {
var pattern = /^[a-zA-Z]*$/,
bname = $elemInput.val(),
$elemInput = $("#input1"),
$elemError = $("#bname_error_message"),
patternMatch = pattern.test(bname) && bname !== '';
$elemError[patternMatch ? 'hide' : 'show']();
$elemError.removeClass(patternMatch ? "form-control-warning" : "form-control-success")
$elemInput.parents(".form-group").removeClass(patternMatch ? "has-warning" : "has-success")
$elemInput.addClass(patternMatch ? "form-control-success" : "form-control-warning");
$elemInput.parents(".form-group").addClass(patternMatch ? "has-success" : "has-warning")
if (!patternMatch) {
$elemError.html(bname === '' ? "Should not be empty" : "Should contain only Characters");
}
}
I think its quite good but I'd suggest some small changes:
Group your else logic in the same block because they are duplicated except the line to set the html text.
Use .parent() instead of .parents(".form-group") to get the input direct parent.
So it could look like this:
$("#input1").keyup(function(){
check_bname();
});
function check_bname() {
var pattern = /^[a-zA-Z]$/;
var bname = $("#input1").val();
if (pattern.test(bname) && bname !== '') {
$("#bname_error_message").hide();
$("#input1").removeClass("form-control-warning");
$("#input1").parent().removeClass("has-warning");
$("#input1").parent().addClass("has-success");
$("#input1").addClass("form-control-success");
} else {
$("#bname_error_message").html(bname === ''? "Should not be empty" : "Should contain only Characters");
$("#input1").removeClass("form-control-success");
$("#input1").parent().removeClass("has-success");
$("#input1").addClass("form-control-warning");
$("#input1").parent().addClass("has-warning");
}
}
$('#input1').on('keyup', function(event) {
check_bname(event.target.value);
});
function check_bname(bname) {
var $bnameInput = $("#input1");
var $bnameErrorMessage = $("#bname_error_message");
var pattern = /^[a-zA-Z]*$/;
if(bname && pattern.test(bname)) {
$("#bname_error_message").hide();
$bnameInput.removeClass("form-control-warning");
$bnameInput.parents(".form-group").removeClass("has-warning");
$bnameInput.addClass("form-control-success");
$bnameInput.parents(".form-group").addClass("has-success");
}
else {
$bnameInput.removeClass("form-control-success");
$bnameInput.parents(".form-group").removeClass("has-success");
$bnameInput.addClass("form-control-warning");
$bnameInput.parents(".form-group").addClass("has-warning");
if (!bname) {
$bnameErrorMessage.text("Should not be empty");
}
else {
$bnameErrorMessage.text("Should contain only Characters");
}
$bnameErrorMessage.show();
}
}
In the bellow picture first one is security question which a drop down, and the second one is the text box which i want to be empty on changing the question
But the issue is that they both are different templates they both don't have any link
I used one controller to put regex between them. which is working fine. but the issue is i can not clear the text box when i change the security question .
Bellow is my drop down template for security question. the template is very big so just but the required part of the template code. please refer the second part of data-ng-click
<li data-ng-repeat="char in characteristic.getAvailableValues() | filter: {value: searchFilter}"
data-ng-class="{violatesCompatibilityRule: characteristic.doesValueViolateCompatibilityRule(char.id)}">
<a id="{{vm.productSpecIdPath}}---{{characteristic.characteristicSpecId}}---{{char.id}}---dropdownitem"
role="menuitem"
tabindex="-1"
href="javascript:void(0)"
data-ng-click="vm.selectCharacteristicValue(char.id); $root.securityQuestionRegex=characteristic.getSelectedValues()[0].regularExpression"
data-ng-bind="char.value">
</a>
Bellow is the Template for Input Box. Please look at the ng-change
<input ng-if="characteristic.getDisplayName() == 'Security Answer'"
type="text"
placeholder="{{'LABELS.USERDEFINEDCHARACTERISTICS.PLACEHOLDER.TEXT' | translate}}"
id="{{vm.productSpecIdPath}}---{{characteristic.getUserDefinedCharacteristicId()}}---{{$index}}---input"
name="{{characteristic.getUserDefinedCharacteristicId()}}-{{$index}}"
data-ng-model="value.value"
data-ng-change="value.value=$root.textboxRegexValidation(value.value);"
data-ng-disabled="characteristic.isReadOnly() || vm.isReserved"
data-ng-class="{ isReadOnly: characteristic.isReadOnly(), 'reduced-input-width': characteristic.getTooltipId(), dirty: characteristic.isDirty() }"
sigma-validation-pattern="{{characteristic.getRegularExpression()}}"/>
Bellow is my part of controller code
$rootScope.textboxRegexValidation = function (obj: any) {
if ($rootScope.securityQuestionRegex) {
var regEx = new RegExp($rootScope.securityQuestionRegex);
if (regEx.test(obj)) {
return obj;
} else {
return obj.substring(0, obj.length - 1);
}
} else {
return obj;
}
}
Yo.. I got the solution
I put one extra flag in ng-click of my dropdown template "$root.emptyTextFlag"
<a id="{{vm.productSpecIdPath}}---{{characteristic.characteristicSpecId}}---{{char.id}}---dropdownitem"
role="menuitem"
tabindex="-1"
href="javascript:void(0)"
data-ng-click="vm.selectCharacteristicValue(char.id); $root.securityQuestionRegex=characteristic.getSelectedValues()[0].regularExpression; $root.emptyTextFlag = true"
data-ng-bind="char.value">
</a>
And in my text box field i used ng-value which evaluate my angular expression at run time "ng-value="value.value= $root.checkEmptyTextFlag() ? '' : value.value"
<input ng-if="characteristic.getDisplayName() == 'Security Answer'"
type="text"
placeholder="{{'LABELS.USERDEFINEDCHARACTERISTICS.PLACEHOLDER.TEXT' | translate}}"
id="{{vm.productSpecIdPath}}---{{characteristic.getUserDefinedCharacteristicId()}}---{{$index}}---input"
name="{{characteristic.getUserDefinedCharacteristicId()}}-{{$index}}"
data-ng-model="value.value"
data-ng-change="value.value= $root.textboxRegexValidation(value.value) "
ng-value="value.value= $root.checkEmptyTextFlag() ? '' : value.value"
data-ng-blur= "$root.isMappingRuleTrigger(characteristic.getUserDefinedCharacteristicId()) ? vm.emitToggledEvent() : 'false'"
data-ng-disabled="characteristic.isReadOnly() || vm.isReserved"
data-ng-class="{ isReadOnly: characteristic.isReadOnly(), 'reduced-input-width': characteristic.getTooltipId(), dirty: characteristic.isDirty() }"
sigma-validation-pattern="{{characteristic.getRegularExpression()}}"/>
And finally in my controller i toggle the flag with the use of setTimeout in function checkEmptyTextFlag()
$rootScope.textboxRegexValidation = function (obj: any) {
if ($rootScope.securityQuestionRegex) {
var regEx = new RegExp($rootScope.securityQuestionRegex);
if (regEx.test(obj)) {
return obj;
} else {
return obj.substring(0, obj.length - 1);
}
} else {
return obj;
}
};
$rootScope.checkEmptyTextFlag = function (obj: any) {
if($rootScope.emptyTextFlag == true){
setTimeout(function () {
$rootScope.emptyTextFlag= false;
}, 100);
return true;
}
}
When ever i click on any option from drop down it change the flag of $root.emptyTextFlag = true and due to this in the text box it clear the text and after 1/10 second it again make it false which make me enable to type in the text box. Its working now.. m so happy :) :P
I have two radio buttons (they're not dynamically generated):
<input type="radio" name="orderbydescending" [(ngModel)]="orderbydescending" [value]="['+recordStartDate']">Ascending<br>
<input type="radio" name="orderbydescending" [(ngModel)]="orderbydescending" [value]="['-recordStartDate']">Descending
How do I make one of the radio buttons checked by default?
Thank you!
Edit
The button's values are being passed through this pipe (i.e. not a component, per se...not sure this is worth mentioning?). The app is pretty simple, and the radio buttons are just hardcoded into app.component. Is the pipe the correct place to initialize which radio button is checked by default?
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'orderBy', pure: false})
export class OrderByPipe implements PipeTransform {
static _OrderByPipeComparator(a:any, b:any):number{
if((isNaN(parseFloat(a)) || !isFinite(a)) || (isNaN(parseFloat(b)) || !isFinite(b))){
//Isn't a number so lowercase the string to properly compare
if(a.toLowerCase() < b.toLowerCase()) return -1;
if(a.toLowerCase() > b.toLowerCase()) return 1;
}
else{
//Parse strings as numbers to compare properly
if(parseFloat(a) < parseFloat(b)) return -1;
if(parseFloat(a) > parseFloat(b)) return 1;
}
return 0; //equal each other
}
transform(input:any, [config = '+']): any{
if(!Array.isArray(input)) return input;
if(!Array.isArray(config) || (Array.isArray(config) && config.length == 1)){
var propertyToCheck:string = !Array.isArray(config) ? config : config[0];
var desc = propertyToCheck.substr(0, 1) == '-';
//Basic array
if(!propertyToCheck || propertyToCheck == '-' || propertyToCheck == '+'){
return !desc ? input.sort() : input.sort().reverse();
}
else {
var property:string = propertyToCheck.substr(0, 1) == '+' || propertyToCheck.substr(0, 1) == '-'
? propertyToCheck.substr(1)
: propertyToCheck;
return input.sort(function(a:any,b:any){
return !desc
? OrderByPipe._OrderByPipeComparator(a[property], b[property])
: -OrderByPipe._OrderByPipeComparator(a[property], b[property]);
});
}
}
else {
//Loop over property of the array in order and sort
return input.sort(function(a:any,b:any){
for(var i:number = 0; i < config.length; i++){
var desc = config[i].substr(0, 1) == '-';
var property = config[i].substr(0, 1) == '+' || config[i].substr(0, 1) == '-'
? config[i].substr(1)
: config[i];
var comparison = !desc
? OrderByPipe._OrderByPipeComparator(a[property], b[property])
: -OrderByPipe._OrderByPipeComparator(a[property], b[property]);
//Don't return 0 yet in case of needing to sort by next property
if(comparison != 0) return comparison;
}
return 0; //equal each other
});
}
}
Edit 2
So in component.app.ts I've edited my export class AppComponent{ to the following:
export class AppComponent {
artists = ARTISTS;
currentArtist: Artist;
orderbydescending = ['-recordStartDate'];
showArtist(item) {
this.currentArtist = item;
}
}
This works in terms of preventing the previous errors, but it doesn't actually make the radio button selected. It still appears as though it's unselected - even though it functions as though it is. Does this make sense?
If you're doing this in Angular 2+ with 2 way binding, in the component where this HTML is being used, you could just try initializing the value associated with the input.
// in your component ts file
orderbydescending: boolean = true;
and you could leave the HTML the same. Although, you seem to have 2 radio buttons associated with the same data value, orderbydescending. I don't know if that's what you intend, but it looks like it could cause problems.
Here's some code from my personal side project to give you a better idea.
#Component({
selector: 'gmu-home-page',
templateUrl: './home-page.component.html',
styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
// here you would put your variables
myFlag: boolean = true;
constructor() { }
ngOnInit() {
}
}
If orderbydescending and recordStartDate are members of your component class:
#Component({
...
})
export class MyComponent {
public recordStartDate: any = ...
public orderbydescending: any = +recordStartDate;
...
}
the appropriate radio button will be checked if you assign the radio button values with [value]:
<input type="radio" name="order" [(ngModel)]="orderbydescending" [value]="+recordStartDate">Ascending<br>
<input type="radio" name="order" [(ngModel)]="orderbydescending" [value]="-recordStartDate">Descending
In the case shown above, the ascending order radio button will be checked by default because the orderbydescending variable is initially set to +recordStartDate.
Note: The variables in my sample code are of type any because I don't know exactly what kind of data you are using. Your data will probably have a more specific data type.
Try this in the component
radioValue = {valueAsc: 'Asc', valueDesc: 'Desc'} ;
orderbydescending = 'Asc';
and in the template put this
<input type="radio" name="radioGroup" [(ngModel)]="orderbydescending" [value]="radioValue.valueAsc">Ascending
<input type="radio" name="radioGroup" [(ngModel)]="orderbydescending" [value]="radioValue.valueDesc">Descending
With this the first radio button is checked, if you don't want any radio button selected assign to the variable orderbydescending null.
orderbydescending = 'null;
I was working in angular project, There I had come across a situation in which I need to validate custom component having a textbox.
<dollar-text-validate ng-model="ctrl.value" required name="myDir"></dollar-text-validate>
My Component
angular.module("myApp", []);
angular.module("myApp").controller('MyController',function(){
var ctrl = this;
ctrl.value = 56;
});
angular.module("myApp").component('dollarTextValidate',{
bindings: {
ngModel :'='
},
template: '<div><input type="text" ng-focus="ctrl.focus()" ng-blur="ctrl.blur()" ng-model="ctrl.amount1"><input type="hidden" ng-model="ctrl.ngModel"></div>',
controller: function() {
var ctrl = this;
// ctrl.amount1 =
ctrl.amount1 =ctrl.ngModel===undefined||ctrl.ngModel==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
ctrl.focus=function(){
ctrl.amount1 = ctrl.amount1 === undefined ? '' : ctrl.amount1.slice(1);
ctrl.ngModel = ctrl.amount1;
console.log(ctrl.ngModel);
}
ctrl.blur=function(){
ctrl.ngModel = ctrl.amount1;
ctrl.amount1 = ctrl.amount1==='' ? '' :'$'+ctrl.ngModel;
console.log(ctrl.ngModel);
}
},
controllerAs:'ctrl'
})
This component is used to set $ symbol in front of entered value. So $ appended value will be available in textbox and original value which is to be validated in hidden field.
How can I validate hidden field. I tried with required attribute in hidden tag but nothing happening. Also tried with custom tag.
Sorry to break it to you, but you might wanna go for directive, and then use $parsers, $formatter and $validators properties of ngModelController.
Component can be used for this, but it is just easier with normal directive.
angular.module('myApp', []);
angular.module("myApp").directive('dollarTextValidate', function() {
return {
require: 'ngModel',
link: function($scope, $element) {
var regexp = /^\$(\d+(\.\d+)?)$/;
var ngModel = $element.controller('ngModel');
ngModel.$formatters.push(function(value) {
return value ? '$' + value : '';
});
ngModel.$parsers.push(function(value) {
var matched = value.match(regexp);
if (matched) {
return +matched[1];
}
});
ngModel.$validators.greaterThan10 = function (modelVal, viewVal) {
var value = modelVal || viewVal;
return value > 10;
};
},
controllerAs: 'ctrl'
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="myApp" ng-form="form">
dollar-text-validate = <input dollar-text-validate ng-model="value" required name="myDir"><br>
number input = <input type="number" ng-model="value" required><br>
value = {{value}}<br>
errors = {{form.myDir.$error}}
</div>