I have the following code:
set dynamicData(val) {
this.editableData.emit(val);
}
get dynamicData() {
return this.formsData;
}
If i set dynamicData value from my component, the setter is triggered.
But if i set dynamicData from input with [(ngModel)] the actual value of dynamicData is changed, but the setter is not triggered for some reason.
Here's the input:
<input type="text" name="dynamicDataSender"
*ngIf="someCondition"
[(ngModel)]="dynamicData.sender"/>
Am i missing something?
The [(ngModel)] syntax can only set a data-bound property. If you need to do something more or something different, you can write the expanded form.
<input type="text" name="dynamicDataSender" #ref
*ngIf="someCondition" [ngModel]="dynamicData.sender" (ngModelChange)="dynamicData(ref.value)"/>
The ngModel data property sets the element's value property and the ngModelChange event property listens for changes to the element's value
ngModelChange will be fired on every keystroke you need to debounce value else event will be emitted for every keystroke and To debounce values you can use a Subject. A subject is both an observable and an observer. This means you can treat it as an observable and pass values to it as well.
debouncer= new Subject();
constructor() {
this. debouncer
.debounceTime(1000)
.subscribe((val) =>{
console.log(val);
this.editableData.emit(val);
});
}
set dynamicData(val) {
this.debouncer.next(value);
}
You reference [(ngModel)]="dynamicData.sender" instead of dynamicData. You should reference the object.
If dynamicData needs to be an event emitter, then you should not use it as a property bind, but instead, use an (onChange)="modelChanged($event)" handler, where you can emit the new values to the dynamicData. ( modelChanged(value){this.dynamicData.emit(value);} ) and use some other property as ngModel.
Related
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 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.
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 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.
In this example for a controlled textarea, it shows how to set an initial value and then update the value onChange. How can you clear this initial value when the field is focused for the first time without clearing any further input from the user.
Setting the initial value:
this.state = {
value: 'Please write an essay'
};
I assume you would do something like:
<textarea value={this.state.value} onChange={this.handleChange} onFocus={this.handleFocus} />
Then have:
handleFocus(event) {
this.setState({value: ''});
}
Although this isn't working. Any advice?
Codepen: https://codepen.io/anon/pen/KZVeWB?editors=0010
If you pass a function to a component, like an event handler, the "this" capture is lost. When the textarea calls your event handler, "this" will not refer to the component where that function was defined. I think it will actually refer to the event handler function its self.
The usual way of getting around this is binding 'this' to the event handler:
onFocus={this.onFocus.bind(this)}
You can also define your methods as lambda member variables if you're using ES7:
class MyComponnet {
onFocus = (e) => { ... }
render() {
return <textarea onFocus={this.onFocus} />
}
}
This works because lambdas in js automatically capture everything around them. it looks a bit cleaner.
Also, you should consider using a placeholder attribute instead:
https://www.w3schools.com/tags/att_textarea_placeholder.asp
You missed to bind your handleFocus method in a constructor function
this.handleFocus = this.handleFocus.bind(this)