Angular 4 Readonly input change detection - javascript

I have following code with readonly input. am trying to change the value if this readonly input from TS File, but, I am unable to detect change from any function. Below is the example.
<input type="text" class="form-control" id="field1" name="field1" readonly [(ngModel)]="field1" (ngModelChange)="onChange()">
ngModelChange is not working in this senario.

You can try to use the (change) instead of (ngModelChange).
Here is some explanation to help you understand this.

Use input like this:
(input)="handle($event)"
And Handle event in your typescript accordingly.

FormControl to the rescue!!
FormControls and Reactive forms are great and it will save your day! :)
Mark ReactiveFormsModule in your app.module, import FormControl to your component aaand... For demo I used a simple timeout to change the value to trigger the valueChanges:
myControl = new FormControl('')
constructor() { }
ngOnInit() {
setTimeout(() => {
this.myControl.setValue('something');
}, 2000);
this.myControl.valueChanges.subscribe((data) => {
console.log('changed!')
});
}
And template:
<input type="text" readonly [formControl]="myControl">
StackBlitz
P.S if this is input is part of a template driven form, this wouldn't work, since ngModel and formControl shouldn't be used together. So you would be notified of that. So in that case I suggest you go reactive way ;)

Related

Angular state not updating until click anywhere

Very new to Angular, and just trying to get a feel for it. I have an input component:
import { Component } from '#angular/core';
#Component({
selector: 'app-input',
templateUrl: './input.component.html',
})
export class InputComponent {
q = '';
queryChange(value: string) {
this.q = value;
}
}
It's html:
<div>
<input #query type="text" (change)="queryChange(query.value)" />
<button>Search</button>
<div>{{ q }}</div>
</div>
When I type into the input, the {{ q }} doesn't update until I click anywhere on the screen, or hit enter. Almost like refocusing. Coming from React I'm confused as to why this happens with the Angular's (change) rather than updating as I type.
My first thought was that maybe because I'm passing the value of the input to queryChange(query.value) instead of passing the event value like I would usually do in React.
I think the problem is about the DOM and not Angular. You should use (input) instead of (change) if you want the event to trigger every time you type.
<input #query type="text" (input)="queryChange(query.value)" />
See this StackBlitz, as well as change and input MDN references. Specifically, MDN says about change:
Unlike the input event, the change event is not necessarily fired for each alteration to an element's value.

Vue.js v-bind has a delay, form submits before value is changed

i'm trying to update a hidden input with an entered value from a SweetAlert modal (basically a prompt).
The code below does not work, the form submits but the hidden field value is null.
HTML:
<input type="hidden" name="input" v-model="value">
JavaScript:
this.value = websiteId;
event.target.submit();
The below code however does seem to work! But not really much point in using Vue.js if i'm going to just plain old JavaScript.
HTML:
<input type="hidden" class="input-value-web" name="input" value="0">
JavaScript:
document.querySelector('.input-value-web').value = websiteId;
event.target.submit();
When you change the value of a Vue instance's data property, like you're doing with this.value = websiteId, that bound property won't update the value of the <input> until the Vue instance's next update.
However, the next update won't occur until after everything in the method has been executed.
To get around this, use the $nextTick method to wait until the Vue instance has updated before executing event.target.submit().
Here's an example:
methods: {
submitForm() {
this.value = websiteId;
this.$nextTick(() => {
event.target.submit();
});
}
}

Angular: How to force uppercase in an input field

I'm working with angular (typescript) and I have a modelform in html where the user has to insert a code field and a description field.
The code field must always be entered by the user, always in uppercase.
I found and followed this question:
How to convert input value to uppercase in angular 2 (value passing to ngControl)
but the last letter that the user inserts remains lowercase however ..
The fundamental thing is that the database always comes in all uppercase (I also put a limit of 4 characters that works properly)
this is my code now, but as written above it does not work properly:
<input type="text" id="code" #code class="form-control" formControlName="code" maxlength="4"
(input)="code.value=$event.target.value.toUpperCase()">
has anyone found a quick, functional and fast solution?
thank you!
You can simply add oninput="this.value = this.value.toUpperCase()" in your <input> tag and it will instantly convert any input in your input field to Uppercase.
If you're working with ngModel, you can use ngModelChange to do it this way with JavaScript's .ToUpperCase().
<input [ngModel]="person.Name" (ngModelChange)="person.Name = $event.toUpperCase()">
Based on #Ali Heikal answer it should be:
<input #code formControlName="code" (input)="code.value = code.value.toUpperCase()" type="text">
Or use .toLocaleUpperCase() if necessary.
This worked for me. I just used keyup and whenever a user adds a character it automatically converts it to Uppercase, not just in the UI but in the model as well.
(keyup)="form.patchValue({name: $event.target.value.toUpperCase()})"
A bit late, but I'm surprised to don't see the css alternative.
Add on the input field:
.input-upper{
text-transform: uppercase;
}
You could create an Angular Directive which transforms all user input to uppercase.
The Directive extends DefaultValueAccessor:
#Directive({
selector: 'input[toUppercase]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => UpperCaseInputDirective),
},
],
})
export class UpperCaseInputDirective extends DefaultValueAccessor {
#HostListener('input', ['$event']) input($event: InputEvent) {
const target = $event.target as HTMLInputElement;
const start = target.selectionStart;
target.value = target.value.toUpperCase();
target.setSelectionRange(start, start);
this.onChange(target.value);
}
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef, false);
}
}
Use the Directive like this on the input:
<input name="myTdfInput" type="text" [(ngModel)]="value" toUppercase>
The solution works in both Reactive Forms and Template Driven Forms.
See working demo on Stackblitz: https://stackblitz.com/edit/angular-input-field-to-accept-only-numbers-czivy3?file=src/app/to-uppercase.directive.ts
Credits for the solution go to: https://twitter.com/BartBurgov (See his tweet here: https://twitter.com/BartBurgov/status/1582394428748009477?s=20&t=RiefRcZ1B8IF42wp2rWzaQ)
You can use a pattern attribute to limit the allowed input. You may also want to assist the user by upper-casing their input.
<input type="text" pattern="[A-Z]*" name="example" />

Angular: Call markAsDirty() in Custom Input Component from NgForm

I implemented a custom component which is a wrapper for an input with NgModel. I connected them with a ControlValueAccessor. It works well, I can easily access values from my parent component.
But if I try to call markAsDirty() the touched flag is only changing on my component, it has no effect to my input inside the component. I will give you an example:
// Parent Component
onSubmit(form: NgForm) {
this.form.controls.registerEmail.markAsDirty();
}
// Thats how the component looks like in my form:
<form #form="ngForm" (ngSubmit)="onSubmit(form)" [ngClass]="{'has-error': !form.valid}">
<form-text label="E-Mail" name="registerEmail" email required placeholder="" [(ngModel)]="eMail"></form-text>
</form>
// Result
<form-text label="E-Mail" name="registerEmail" class="ng-untouched ng-invalid ng-dirty">
<label for="form-text-2">E-Mail</label>
<input class="input-control invalid ng-untouched ng-pristine ng-invalid" type="text" id="form-text-2">
</form-text>
As you can see the form-text has the "ng-dirty" class, the input inside remains pristine.
To implement my custom component I used one the many instructions you find on the web. Here is the one I used: angular2 custom form control with validation json input
I want to mark every input field as dirty when the submit button is pressed. Because my validation shows up, when you blur the input.
I figured out that there is the problem my component inherits from ControlValueAccessor. The only connection between my component and my NgForm is over its NgModel. The NgForm can use my component as FormControl because it has its own NgModel. Over events it's possible to pass values in two directions. But it's not possible with methods like markAsDirty() or markAsTouched(). Inside the component it's no problem. But my NgForm has no real access to components. Only to NgModel.
Is there any way to implement that? I thought it's not that hard to figure it out, but I am struggling for a long time with that. My only solution for the moment is to iterate over every input with jQuery to fire a focus. There must be a cleaner solution for that.
Thx
You can pass as input in the component which implements ControlValueAccessor the dirty property of the form and then update the state of your inner input using ReactiveFormsModule and FormControl.
The component which holds your form:
<form #myForm="ngForm" (submit)="onSubmit($event)">
<my-input name="my-input" [(ngModel)]="myInput" [formDirty]="myForm.dirty"></my-input>
<button type="submit">Submit</button>
</form>
Then in the component which implements ControlValueAccessor:
ngOnChanges({ formDirty }: SimpleChanges) {
if (formDirty.currentValue) {
this.inputCtrl.markAsDirty();
} else {
this.inputCtrl.markAsPristine();
}
}
Here you can find the relevant snippet.
You need to call onTouched() (this._onTouchedCallback) from inside your component when you want the controls status set to touched. Same for this._onChangeCallback
For example by adding (ngModelChange)="onTouched(value)" to the input tag in my-custom-input
Copied from:
https://github.com/angular/angular/issues/10151
The problem is that there is no easy way to get any notification when dirty state changes. See https://github.com/angular/angular/issues/10887.
Helper directive:
#Directive({
selector: 'my-input'
})
export class MagickDirective implements DoCheck {
constructor(private control:NgModel){
}
ngDoCheck(): void {
//you can do whatever you want
if(this.control.dirty) {
(this.control.valueAccessor as MyInputComponent).setDirty(true);
}
}
}

Angular 2 ngModel inside a function

I'm trying to pass a ngModel to a function and get the change,
I don't know the way to use.
this is what I got now:
<ion-input text-right
formControlName="monday"
type="number" pattern="[0-9]*"
placeholder="00.00"
[(ngModel)]="monday"
(keypress)="onChange(monday)">
</ion-input>
<ion-input text-right
formControlName="tuesday"
type="number" pattern="[0-9]*"
placeholder="00.00"
[(ngModel)]="tueasday"
(keypress)="onChange(tuesday)">
</ion-input>
.... and so on...
Then in my page.ts I got
monday: string = '';
tuesday: string = '';
etc...
onChange(input: string){
//I want the input to correspond to my ngModel so it gets updated
input = this.clientAction.transformInput(input);
}
I don't want to do:
this.monday = this.clientAction.transformInput(input);
Because as you may think I got all the day of the week so I don't want to have a function for every day like:
onChangeMonday(){};
onChangeTuesday(){};
I need something dynamic.
How can I resolve this issue?
Thanks in advance
[SOLUTION] #AJT_82
instead of using my ngModel and trying to update it, the solution was to access the controls from the form.
in your page.html
<ion-input text-right
formControlName="monday"
type="number" pattern="[0-9]*"
placeholder="00.00"
[(ngModel)]="monday"
(keypress)="onChange(monday, 'monday')">
</ion-input>
then in your page.ts
onChange(input: string, day: string){
this.rateForm.controls[day].setValue(this.clientAction.transformInput(input));
}
works like a charm now !!
Thanks #AJT_82
Since you have a form, I would suggest that you skip the ngModels altogether and make use of the form you have. Still a bit unsure about what this.clientAction.transformInput(input) is supposed to do, transform the values somehow, as you explained. You should be able to incorporate this, perhaps when you submit the form? Anyway, as said, you have your form values safely stored in the object created by the form, which would for you look something like this:
{
"monday":null,
"tuesday":null,
"wednesday":null,
// ... and so on
}
When you type in your fields you can capture every keypress with valueChanges, where you can access the complete form:
this.myForm.valueChanges.subscribe(res => {
console.log("all form values: ", res) // here is an object with all your form values
})
When you need, you can also access each form control by, here accessing monday:
console.log(this.myForm.controls['monday'].value)
So this get's rid of the hassle to use ngModel for each form value.
So these are a couple of ways you can intercept the values and then "transform" them at some point, when you want/need to. Probably best place to do so, is when submitting form, loop the values and transform them. And this is totally dynamic, as you wished for, and do not need 7 functions to transform each form control! ;)
Hope this helps you, and here's a plunker (check the console as well).
Plunker
When you are using ngModel why do you want to pass the same in to function
<ion-input text-right
formControlName="monday"
type="number" pattern="[0-9]*"
placeholder="00.00"
[(ngModel)]="monday"
(keypress)="onChange()">
</ion-input>
and handle using the ngModel itself as
onChange(){
input = this.clientAction.transformInput(this.monday);
}
Update 1:
I got your are looking transform the textbox value as the user types it.
Check Custom Directive

Categories