I'm trying to figure out why onChange wont work on a children component, i have a component named AlertMethod and im calling from a container.
<AlertMethod
onChange={this.methodSelected}
label="Sms"
value="sms"
/>
and methodSelected it's just a console.log() to test if getting called
methodSelected = (event, data) => {
console.log(data);
};
here's an img to explain it better.
so finally that function it wont getting called. that's my problem.
EDIT
On my Container AlertSystem.js i want to update a state if a checkbox it's checked or not, so my checkbox component it's Checkbox.js it's a stateless function, it recive 2 props label and value nothing special. so in my container when my Checkbox Component CHANGED, update my state, so that's why i have a onChange={this.methodSelected} on the children component, because in the methodSelected function ill update my state with this.setState and stuff.
You aren't calling props.onClick in AlertMethod
Your child component AlertMethod is being passed the onSubmit prop but not using it.
Should look more like this:
export const AlertMethod = ({ label, value, onClick }) => {
return (
<div>
<Checkbox value={value} fluid label={label} onClick={onClick} />
</div>
)
}
Related
I want to detect changes on a Reactive Form while working in Edit Mode.
I tried using valueChanges but the issue is when I am loading an already submitted form for editing it, it is triggering events for each prefilled control.
While my requirement is to only trigger when user made any changes to the form.
Anyone can please suggest
Try to subscribe to valueChanges in the ngAfterViewInit() method.
export class MyComponent implements AfterViewInit {
ngAfterViewInit() {
this.myForm.controls['name'].valueChanges.subscribe(change => {
console.log(change);
});
}
...
Using valueChanges would be a correct approach for you.
I assume you are using setValue or patchValue to set the prefilled values. Both these take as optional emitEvent property. If you set it to false, it will not fire valueChanges. So.... when you set the prefill values, use that:
this.form.setValue({ test: "test" }, { emitEvent: false });
This action will now not trigger valueChanges.
all FormControl objects have methods such as markAsUntouched() or markAsPristine() so, in line with what #uminder has indicated, to set the entire form as pristine after either the trigger of ngAfterViewInit or directly after your form has initialized you could simply traverse all FormControl objects and set them as 'pristine'? Any changes thereafter by the user will -by definition- trigger the valueChanges of either the form or the control?
below a recursive method for setting all FormControl item as touched (to trigger validation settings) you can use the same for control.markAsUntouched or control.markAsPristine ?
export function markFormGroupTouched(formGroup: FormGroup): void {
const fa = new FormArray([]).constructor.name;
Object.values(formGroup.controls).forEach((control: any, i) => {
if (control.constructor.name === fa) {
control.controls.forEach((ctrl: any) => markFormGroupTouched(ctrl));
}
else{
control.markAsTouched();
}
});
}
The functions is recursive for FormArray so it will traverse the entire form.controls tree.
NOTE: the const fa = new FormArray([]).constructor.name; is to avoid
problems on minification of your code. Because typeof FormArray
comes back as FormGroup you will have to compare on the control
constructor, but the factory for the FormArray gets renamed on
minification, therefore you cannot hardcode ..=="FormArray" because that will not be the constructor name when minified. This method allows for that contingency.
I am new to React.js and recently I learned about controlled inputs in React.
Code:
Here's a sample implementation that I made:
import React, { useState } from 'react';
const MyForm = () => {
console.log('rendered'); // Line 5
const [text1, setText1] = useState('');
const [text2, setText2] = useState('');
const onSubmit = (evt) => {
evt.preventDefault();
console.log(text1, text2);
}
return (<form onSubmit={onSubmit}>
<input type="text" value={text1} onChange={ev => setText1(ev.target.value)} />
<input type="text" value={text2} onChange={ev => setText2(ev.target.value)} />
<input type="submit"/>
</form>);
};
Problem:
Performance.
Above implementation works correctly, but I noticed that every time one of the field changes, console.log('rendered'); at line 5 is called again, and the entire form seems to be re-rendered. I guess that this could cause some problem especially for more advanced forms with many input fields and heavy pre-processing, etc. Ideally only the field that has changed should be re-rendered.
So I was wondering if my understanding of controlled inputs and form is okay. If not what is more scalable way of implementing this?
Since the state changes, the component will re-render. this is normal. if you dont want that, you need to "export" your input fields to new components with their own state, but then you have to somehow ref these components back to your parent form component in order to get their current values when you are going to submit the form.
Check this link on how to use ref, but I think that the form should be way too heavy in order for you to consider such a senario of creating for each input its own state in order to avoid parent component re-rendering on every input change, or even change to uncontrolled component, which is not usually recommended.
I have a <textarea> within a template driven form of an Angular 7 project.
When editing an object, the form is prefilled with the current values. I want to automatically resize the <textarea> when the content has changed via the [(ngModel)]="property" binding by modifying the element-style.
area.style.overflow = 'hidden';
area.style.height = '0';
area.style.height = area.scrollHeight + 'px';
The code generally is working, but I cannot find a suitable event to trigger it.
Subscribing to the change event of the <textarea> is only working on keyboard input. Using (ngModelChange)="adjustTextAreaSize($event)" has the same behavior.
I tried to execute my resizing code at the end of the ngOnInit() function, but the actual html-control seems to not have any content yet at this point.
Does anyone have an idea which event could do the trick here?
Seemed a rather easy task in the beginning, but I'm breaking my had over this for over an hour now... can not be such a difficult task, can it?
Yes there is a very simple solution for this.
Wrap your textarea inside a form and try the code below:-
HTML
<form #form="ngForm">
<textarea>....</textarea>
</form>
TS
#ViewChild('form') ngForm: NgForm;
ngOnInit() {
this.subscription = this.ngForm.form.valueChanges.subscribe(resp =>
{
console.log(resp); // You get your event here
}
)
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
trigger a change event on a textarea when setting the value via ngModel Binding
This will cause infinite triggering if you do so.
If you don't want to monitor the input model change in a more reactive way, a quicker solution (but a bit hacky) will be simply wrap your code inside setTimeout in ngOnInit() or ngAfterViewInit() where you mentioned it was not working.
setTimeout(() => {
updateSize();
});
I am building a restaurant review website with react js,html and css. I need to make a child component RestaurantInput update a sibling component Restaurant list.
I created handlers which pass informations to App component(the parent) by a callback and when there is an input change in the RestaurantInput it get updated by the handlers. The App component pass then the information to RestaurantList component by props which will render the new restaurant on the UI.
Unfortunatly there is no rendering of the new restaurant . I do not know where i got it wrong. Is there anyone who can help?
I have tried to console log the Restaurants imported from a Json at my local pc. But it look like it was not updated either.
I went to the React js documentation but did not get any clear answer either.
Many solution are for when there is a proper JSON file from the back end and I could not figure out how to apply them in my current situation.
RestauranInput.jsx:
handlechange(e){
const name=e.target.name;
const value=e.target.value;
this.setState((prevState)=>{
prevState.restaurant[name]=value;
return{restaurant:prevState.restaurant};
});
}
handleSave=(e)=>{
this.props.onSave(this.state.restaurant);
this.setState({
restaurant:Object.assign({},Init_value),
error:{}});
e.preventDefault();
}
App.js:
class App extends React.Component {
constructor(props){
super(props);
this.handlerestaurantclick=this.handlerestaurantclick.bind(this);
this.saveRestaurant=this.saveRestaurant.bind(this);
this.state={restaurants:Restaurantlist,showcomponent:false,
restaurantClicked:-1,newrestaurant:{}}
}
saveRestaurant(restaurant){
if(!restaurant.key){
restaurant.key= Object.keys(this.state.restaurants).length;}
this.setState((prevState)=>
{
let restaurants=prevState.restaurants;
restaurants[restaurant.key]=restaurant;
return{restaurants};
});
}
RestaurantList.jsx:
let list=[];
restaurantArray.forEach((item,index)=>{
list.push(<Restaurant key={index} name=
{item.restaurantName}
adress={item.address} ratings={item.ratings} onClick=
{()=>this.handleclick(index)}> </Restaurant>)})
return(<div className="restaurant-list">
<Filter getmin_filter={this.state.handle_min} get_max=
{this.state.handle_max}/>
{list}
</div>);
}
props are not states if they changeĀ on the parent the child components are not rerender so you have to use "componentDidUpdate" check the link below
Re-render React component when prop changes
any communication that is not parent to child, you can either use events or states manager like redux
When I choose a google maps address autocompleted result from the dropdown, it doesn't pass to my parent component, but it does when I manually enter the address.
I'll start typing, and it's passing the info, and once I select an address it fills in the rest for me, but doesn't pass that data, even though it's showing in the input box.
I'm using this input (in the child template) to get addresses with a google map div:
<input
[(ngModel)]="address"
(ngModelChange)="addressChange.emit($event)"
id="pac-input"
name='address'
class="mapControls"
type="text"
placeholder="Enter Address"
>
in my child component:
#Component({
selector: 'gmap',
})
export class gMap{
#Input() address;
#Output() addressChange = new EventEmitter();
}
in my parent template:
<gmap [(address)]="address"></gmap>
and parent component:
address: string;
Obviously there's more code, but this is the important stuff I think. Anyone know how to make it so when I select an autocomplete result from the dropdown it triggers ngModelChange with the data chosen from the selection?
I sort of fixed it, I did this:
So since we're using typescript I changed google's all of
function()
to
() =>
then, under these functions of the google map code
searchBox.addListener('place_changed', () => {
places.forEach((place) => {
I called the emitter
this.addressChange.emit(place.formatted_address);
there's a huge delay, sometimes like 20 seconds, but it works!
Anyone know why there's a few-second delay between when I choose from the drop-down and the emitter goes off? I'd really like to remove that delay
If you use promise object then the response will be very fast.
return new Promise((resolve, reject) =>{
autocomplete.addListener('place_changed', () => {
var place = autocomplete.getPlace();
console.log("place "+place);
resolve(place);
});
})