I'm new to Angular, just a question on data binding on input element. Below is the code:
<input id="test" [paModel]="newProduct.name" (input)="newProduct.name=$event.target.value" />
and directive class:
#Directive({
selector: "input[paModel]"
})
export class PaModel {
...
#Input("paModel")
modelProperty: string;
#HostBinding("value")
fieldValue: string = "";
#Output("paModelChange")
update = new EventEmitter<string>();
#HostListener("input",["$event.target.value"])
updateValue(newValue: string) {
this.fieldValue = newValue;
this.update.emit(newValue);
}
}
I'm confused here, why we need #HostBinding("value") on fieldValue?
when we type sth in the input, isn't that the value of the input element will get updated to latest value automatically by the browser?
According to angular documentation HostBinding is:
Decorator that marks a DOM property as a host-binding property and supplies configuration metadata. Angular automatically checks host property bindings during change detection, and if a binding changes it updates the host element of the directive.
#HostBinding does quite the opposite of what #Input does, as #Input is meant for bringing the value into the Directive,
while #HostBinding is responsible for publishing the value out (updating the DOM).
Controlled VS Uncontrolled components:
By default input fields are not controllable which means field is handled by the DOM itself, which is the source of truth. It then stores its own state internally.
Having our ngModel directive capturing input events, the DOM can no longer detect the changes itself once changes are being made,
therefore, we need to take control over the field by:
Raising an event.
Updating the internal value of the field.
Related
In current (2020) Angular, I have two components that are intended to share the state of activeProject through a service. I have the following defined on an ApplicationProjectService:
private activeProjectSource = new BehaviorSubject(undefined);
activeProject$ = this.activeProjectSource.asObservable();
set activeProject(v: any) {
this.activeProjectSource.next(v);
}
get activeProject() {
return this.activeProjectSource.value;
}
I'm using BehaviorSubject in the service since I want components to get the current value upon subscribing without any change. The getter/setter is there because I was doing some other binding directly to a service property, which I've since learned is not recommended.
The two sibling components that eventually trace back to a common parent, but I'm not using #Input() or #Output() or any parameter passing in the DOM:
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = activeProject;
});
Each component is binding to the this.activeProject property in their respective component using [(ngModel)]:
<input type="checkbox" [(ngModel)]="activeProject.someProperty">
Question
If each component obtained what I thought was a copy of activeProject through this.appProjectService.activeProject$.subscribe(), how is it working that a change to the local property in one component is reflected in the other? In the end this is the behavior I want, but I can't understand why it works. Is there some passing by reference that I'm not understanding in rxjs observables?
sIf you have 2 components, the both local variables activeProject use the same reference of activeProject. ngModel is bound to a property of this reference. So it's working, because a change in a component only update the property of the reference, and does not change the reference. You can even use a variable activeProject without wrapping it in a BehaviorSubject.
I know this should be in comment but this much of letters comment won't accept.
Forget about RxJS for a while.
Now you have getter and setter for your property.
You set activeProjectValue in your service.
Now when you subscribe it in one component, you will get the object which will be passed by reference. Same for the other component. As both components accessing same object they are passed by reference.
If you have to break the reference, to use it differently.
Also each component obtained what I thought was a copy of activeProject .... this means they copy by refenrence of object.
I know, you know how to break reference, but this is just for sake for future viewers
To break the reference of object you can use JSON.parse(JSON.stringify(*ObjectName*)
In your example
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = JSON.parse(JSON.stringify(activeProject));
});
I'm new to Angular, just a question on Lifecycle Hook Methods. I know that:
ngOnInit() is called after Angular has set the initial value for all the input
properties that the directive has declared.
ngOnChanges() is called when the value of an input property has changed and also just before the ngOnInit method is called.
let's say we have an input attribute like:
// custom directive class
#Input("pa-attr")
numOfProduct: number;
and
//template.html
<tr [pa-attr]="getProducts().length ...
and I click add button and add a new product, so the total number of products changes, so my question is:
in my case ngOnChanges() get called, ngOnInit() won't be called because ngOnInit() only get called in the first time the input property has been initialized with a value.
But how does the input property numOfProduct get updated after ngOnChanges() get called? Is it a special lifecycle method called ngUpdateInputProperties(the name won't match, but you get the idea) that does the job as:
ngUpdateInputProperties {
latestValue = ... //retrieve latest value
numOfProduct = latestValue ;
}
or such lifecycle method doesn't exist and the numOfProduct won't have the latest value, we can only get access to the latest value in ngOnChanges() method?
If you change any values in your component, it will change detection to recheck all the data bound values, then it will pass any updates to your child component.
You can test this yourself by placing a log entry inside if your getProducts() method, then tracking its value in the child component. You'll notice it gets called when updates happen in the component and you should see the updates in the child component.
Hope this helps.
I'm currently in the process of creating a stepper component using Vue with Vuex. Each step is a component that holds a input fields. Each step stores the values of the input fields in the Vuex store. When going to a previous step, the already available data should be loaded from the store and displayed in the respective input field.
I'm using a custom input component that implements the v-model directive correctly.
<custom-input v-model="amount"
v-bind:type="'number'"></custom-input>
"amount" is defined in the data function:
data: function () {
return {
amount: null
}
}
Now I'm trying to set the value of the v-model property when the component gets mounted.
mounted() {
this.amount = this.$store.state.fields.amount.value;
}
Through debugging tools I can see that the store holds the correct value. The same is the case for the amount data-property of the component.
I've also tried setting the property using the set method like this:
this.$set(this.$data, 'amount', this.$store.state.fields.amount.value);
But it still does not show up in the custom-input.
How do I set the data property used in v-model correctly so that it shows up in the input field?
EDIT
The custom-input component is implemented like this:
<input type="'text'" v-on:input="onInput">
onInput: function (event) {
this.$emit('input', event.target.value);
}
The problem was that I did not actually bind the value property within the custom-input component. Adding this fixed the problem:
<custom-input ... :value="value" />
I have component let's say "middle" with prop "element" from let's day "top" component - it's object and I use references to populate and update some data. This object has properties "id" and "cost". On middle component, I have select with v-model to element.id.
If user change selection, I checked some data in middle component method and set new cost. And it works fine - if I just use {{ element.cost }} to display data, everything is fine.
But I also sends this element.cost to let's say "deep" component - I use it to render custom input field and this cost is populated as value. This component use internal data to store and update model because I also use Cleave.
beforeMount () {
// model is from middle (parent)
this.value = this.model
}
and watch changes:
watch: {
value: function (newValue) {
// here some validation
this.$emit('input', this.value)
}
}
And it works fine - if I modify input, middle element also has this changes.
The problem is when I modify element.cost on "middle" level - after change in selection. Even if I use $this.set, even if I use this.$forceUpdate, input on "deep" is not updated. I tried to add watcher inside deep t monitor model, but it doesn't see any changes from parent. What's wrong?
I am developing a Web Application using React and Redux. I am new to React and Redux. Now, I am having an issue using it together, React and Redux. The problem is I cannot change the input field value when I set the value of the field with the state value.
In my component, I have a property called, event in the state which is coming from connect like this.
function mapStateToProps(state)
{
return {
submittingForm : state.editEvent.submittingForm,
formErrors : state.editEvent.formErrors,
event: state.editEvent.event,
};
}
As you can see the event filed is coming from the reducer. In my component, I can retrieve the event like this.
this.props.event.name
Now, what I am trying to do is when I edit the event, I like to maintain the values of the event in the input field.
Therefore, I render the state value in the input field like this.
<TextField
name="location"
label="Location"
value={this.props.event? this.props.event.location: ""} />
It is maintaining the value in the input field. But the problem is I cannot change the input field now. I can focus on the text input field, but when I type in anything using keyboards, the value is not changing and just keeps displaying the state value. How can I fix the issue?
If it is slow, I would use something like debounce function (see here: https://lodash.com/docs/#debounce). Essentially, it is useful, when you have groups of often events, and you want to call some function, only when there is a certain timeout elapsed since the last event.
So, instead of emitting a new event every time something is changed, you can emit it only after 300-500ms after user finished typing. This looks like batching of all the changes into a single update. If this is something acceptable for your problem, here is how you can do it:
var debouncedOnChange = _.debounce(current_onChange_method, 300 or another time in ms);
Then in <TextField> set onChange to debouncedOnChange.