I have a form model with some indicators that are represented as checkboxes. Currently they model their value as true/false in the form object json. What I would like is for their value to convert from a boolean to a number, 1/0 respectively. Is there a smart way to do this?
Example code:
#Component
template: `
<form [formGroup]="myForm" (ngSubmit)="save(myForm.value)">
<input type="checkbox" id="myToggle" formControlName="myToggle"/>
</form>
`
export class MyComponent implementes OnInit{
private myForm:FormGroup;
myToggle: number;
constructor(private _fb:FormBuilder) {}
ngOnInit() {
this.myForm = this._fb.group({
myToggle: [0]
});
}
Hopefully the above is demonstrating that I'm trying to set the type of "myToggle" to a number. Initializing the form is setting the default to 0 and correctly leaving the checkbox unchecked. However, updating the checkbox to checked will set the form value to true instead of 1. I want it to be updated to 1. Via this question I see that there are some options for converting booleans to numbers. However, I'm unsure of exactly how to implement this with the reactive form model.
FormControl has registerOnChange method, which allows you to specify a callback executed after every change.
Having your example you can access control let ctrl = this.myForm.controls["myToggle"] (I'd prefer create this manually) and then you can do sth like ctrl.registerOnChange(() => ctrl.patchValue(ctrl.value ? 1 : 0));.
Related
I have an Angular 2.4.0 application I'm working on with a form that has some backing Javascript validating/formatting a couple of the fields. When the formatting of the fields completes, the view does not update if the value returned from the formatting matches the original value attached to the model. Is there a way to force the view to update? Since there isn't a model change, forcing a component refresh hasn't had any effect. I'm guessing I'll need to update the view separately with something like jQuery, but I wanted to check if there was a better solution first.
Component:
export class Component {
field: string
formatField(updatedField: string) {
this.field = updatedField.replace(new Regexp("[^\\d]", "g"), ""); // remove everything except numbers
}
}
HTML:
<html>
<body>
<input type="text" [ngModel]="field" (ngModelChange)="formatField($event)">
</body>
</html>
In the above example, if the model is "1", then I enter "1;['];[", and formatField returns "1", the screen will still display "1;['];[" when I'm expecting "1" to display instead (which is the returned value of the formatField call).
Edit: fixed ngModelUpdate to ngModelChange in example (typo). Added Angular 2 version to description.
To refresh the view you need to explicitly run the change detector after you make a change to the model, then you can modify the model and ngModel will update the value.
constructor(private cd: ChangeDetectorRef) {}
formatField(updatedField: string) {
this.field = updatedField;
this.cd.detectChanges();
this.field = updatedField.replace(new RegExp("[^\\d]", "g"), ""); // remove everything except numbers
}
What is the best to handle reference type data, for example json data, bind to a component. For example,
<my-component [data]="jsonData"></my-component>
where in .ts jsonData= {'key1':'value1','key2','value2'}
Now if on button click I change my jsonData as
{'key1':'valueX','key2','value2'}
Angular won't be able to detect this change, hence doesn't run ngOnChanges(),because angular only checks reference of the data-binding changed or not as a result view won't be refreshed. How should I deal with it?
I can put logic to refresh my view in ngDoCheck() but it is called a lot of times. If json is large, then processing it each time ngDoCheck runs can be an expensive operation.
One possible solution I could think of is to make a service for that component, and on button click, pass the new json to that service, service will publish that event with changed data to my component and component, listening for the events of service, will refresh the view with that new data.
Is there any easier way to handle JSON/Array changes bind to component?
You can use a setter for #Input property in your component.
Intercept input property changes with a setter
Use an input property setter to intercept and act upon a value from the parent.
#Input() set data(jsondata: any) {
this._data = jsondata;
}
Refer to the documentation here for more details.
Update after comments:
Yes you can implement ngOnChanges as well for this,
as the documentation states:
You may prefer this approach to the property setter when watching multiple, interacting input properties.
See the below example from the documentation,
export class MyComponent implements OnChanges {
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
for (let propName in changes) {
let changedProp = changes[propName];
let to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
console.log(`Initial value of ${propName} set to ${to}`);
} else {
let from = JSON.stringify(changedProp.previousValue);
console.log(`${propName} changed from ${from} to ${to}`);
}
}
}
}
You can get hold of the old and new values of your members using previousValue and the currentValue properties respectively.
I save simple key in a variable in a component using angular 4, when the app closed every value will erased and i know it.
this is a simple sample :
export class LoginComponent implements OnInit {
data : any;
constructor() {
this.data = "Hello";
}
}
I just want to know is there a way using browser console to show value in this.data without console.log()?
Yes you can.
Start by finding some HTML that belongs to your component in your page. Then, inspect it.
In Chrome, you will see a $0 besides it. That's a variable reference.
Now, go into your console and type
ng.probe($0).componentInstance
This will log you your whole component, with the variables that are in it. You can simply give it a reference
const myRef = ng.probe($0).componentInstance
Then delete your component as you want, and log it again from the console directly
console.log(myRef) // or shorthand
myRef
I have an input field where a user can search different user in my app. But I want to make a search in such a way that when the user types in the input it should simultaneously search.
I have implemented my fuction which calls my back-end but i just want to know how can i send the value of input field with every keystroke.
Please help
You should handle the ngModelChange Angular event (see this stackblitz). It takes into account any kind of change in the input field (key strokes, cut/paste with context menu, etc.), and ignores key strokes that do not result in an actual change (e.g. pressing and releasing Shift by itself does not trigger ngModelChange).
<input [ngModel]="searchStr" (ngModelChange)="modelChange($event)" />
import { Component } from "#angular/core";
import { FormsModule } from "#angular/forms";
#Component({
})
export class AppComponent {
public searchStr: string = "";
public modelChange(str: string): void {
this.searchStr = str;
// Add code for searching here
}
}
Use (keyup) event on your input and pass the ngModel value to your service
<input #term (keyup)="search(term.value)" type="text" class="form-control search-text-indent searchbar-positioning" >
and in component.ts
search(term: string): void {
//pass it to your service
}
I would use a form control to expose the valueChanges observable. This observable emits every time the value of the form control changes.
<input [formControl]="search">
This allows you to control the flow of the typeahead search
// component.ts
ngOnInit(){
this.search.valueChanges
.pipe(
// debounce input for 400 milliseconds
debounceTime(400),
// only emit if emission is different from previous emission
distinctUntilChanged(),
// switch map api call. This will cause previous api call to be ignored if it is still running when new emission comes along
switchMap(res => this.getSearchData(res))
)
.subscribe(res => {
this.result = res;
})
}
With this, you are controlling how often the api call gets called (debounceTime & distinctUntilChanged) and ensuring the order of which the api calls finish (switchMap). Here is a stack blitz demoing this.
I did create a directive which prevents any character to be typed in an input if it doesn't match a given pattern.
import { Directive, Input, Output, HostListener, EventEmitter } from "#angular/core"
#Directive({
selector: '[ngModel][typingPattern]'
})
export class TypingPatternDirective {
#Input() typingPattern: string
#Input() ngModel
#Output() ngModelChange = new EventEmitter()
#Input() global: boolean = true // Global modifier
private oldValue: any
/* On key down, we record the old value */
#HostListener('keydown', ['$event'])
onKeyDown($event) {
this.oldValue = $event.target.value
}
#HostListener('input', ['$event'])
onInput($event) {
if (!$event.target.value.match( new RegExp(this.typingPattern, this.global ? 'g' : '')) ) {
$event.target.value = this.oldValue.trim()
}
this.ngModelChange.emit($event.target.value)
}
#HostListener('paste', ['$event'])
onPaste($event) {
this.onInput($event)
}
}
And here is how I use it on a input element:
<input type="text" [ngModel]="..." [typingPattern]="$[0-9]{0,8}^" required>
The only bug I currently have happens if in that particular example I type any characters like h. The key is gonna be prevented by my directive, but the required property is gonna considered that a character has been added and thus set the input to valid even though my ngModel value is empty and nothing is displayed. I probably need to event.preventDefault() but I am not sure where.
I manage to get around this issue by encapsulating the this.ngModelChange.emit($event.target.value)
in a setTimeout()
by doing so, the input validation is retriggered again after, and thus the state of my input get updated correctly (the directive can thus be used correctly with required or other validators). It works for now, but it's definitely a bit hacky, and a better handler of the events should lead to a better solution.
If i'm understanding what you're trying to do, you want to have your input filtered out by the regex passed to typingPattern. If so, then your ngModelChange EventEmitter should be emitting the 'new value' after it has been checked (and cleaned) by the RegEx. So, if the user types, in sequence: 1,2,y, then your ngModelChange should emit: 1, 12, 12.
If the above is true, then you need to capture the old value of your input BEFORE the keydown event. Then, on each keydown, if the new character is acceptable, you can ngModelChange.emit the value of your input. If, however, the new character is not acceptable, then you should emit the old value that you previously stored.
Does that make sense?