I'm trying to create a child component (it's a form), which appears within a parent component in a modal.
The form (child component) must change according to the type chosen in the parent through several buttons (each button is a different type).
When you click on the button, the child component must modify the form according to the type chosen in the parent. The form controls change properly, but the template is not modified. The code is the following:
Parent method that opens the modal and calls the child component
show(value: string){
this.type = value;
this.entityFormComponent.type = this.type;
this.entityFormComponent.ngOnInit();
// Set submitAttemp to false for the new form
this.entityFormComponent.submitAttemp = false;
this.contentModal.show();
}
Method ngOnInit of the child component
ngOnInit(): void {
if(this.creating){
this.entity = new Entity();
} else {
this.entityService.get(this.nameToEdit, this.type)
}
// Initialize the form and add the corresponding attributes according to the type
this.entityForm = new FormGroup({});
this.entityHelper.getEntityFormAttributes(this.entityForm, this.type, this.entity);
}
So far it seems that everything is correct and the form group that is generated is adequate every time you click on the parent button, but the inputs of the form do not appear. They are generated through an auxiliary method, which returns true or false through the type and attribute of the input that is passed:
<div class="col-md-6" *ngIf="entityHelper.getEntityInputsAttributes('shadow', type)">
The type in the view is "undefined", but in the component appears correctly. Why doesn't the view update the type correctly? What do I have to do to update it?
For parent child component interactions, it is advised to use the Input directive.
If you have different types of forms you could use an enum. And bind each enum entry to each of the buttons by adding an action to the button that sets a property with its correspondent type. For example:
onClick(type: FormTypes) {
this.formType = type;
}
You parent template would look something like
<child-component [typeOfForm]=formType </child-component>
Your child component would need to execute changes when a new formType is provided therefore you would need to do something like
#Input()
set typeOfForm(typeOfForm: FormType) {
//make the necessary changes on form type change.
}
Also to avoid manually executing the ngOnInit function would can surround your child component tag with a div and have it showing or not with a boolean property on the typescript code like so:
<div *nfIf="showForm">
<child-component [typeOfForm]=formType </child-component>
</div>
doing this your show function would only set the showForm property to true.
Hope this helps, for more detailed information on component interaction I suggest you check out this documentation Angular Component interaction
Related
I'm currently implementing a form with an undo-function in Angular and I can't figure out how to connect the #Input values of the child component to fields of an array in a seperate 'dataService'. Initially I just implemented the whole form in one component with Angular's FormGroup class which worked fine. Whenever I pressed the undo button the array would be set to a previous state and the changes would be reflected in the input fields. I then decided it would be cleaner and more future proof to move the inputs to a seperate child component, so I could customize them better. The problem is now, that the array field that I pass as an Input to the child component is always passed as a primitive string and looses its connection to the actual array. How can I pass it in a way that I can connect it to the inputs value via two-way data binding so the changes in the input get reflected in the array and vice versa?
The code looks like the following:
Parent template:
...
<input-field id="name" name="name" [(value)]="dataService.projectData.info.projectDetails.name"></input-field>
...
Child component:
...
export class InputFieldComponent implements OnInit {
#Input() value: any;
#Output() valueChange: EventEmitter<any> = new EventEmitter<any>();
#Input() id: string;
#Input() name: string;
#Input() class: string;
...
onBlur() {
this.valueChange.emit(this.value);
}
Child template:
...
<input [(ngModel)]="value" (blur)="onBlur()" type="text" id="{{id}}" class="{{class}}">
...
The way it behaves right now is that the default string value from the array is passed to the child component and displayed in the input field. When I type something in the input only the 'value' attribute inside the child component is being changed (probably because it's not bound to the actual array). The content in the array stays the same and the undo function doesn't work.
The way I would want it is that once the input field looses focus (onBlur) the value in the array is updated and once I click on 'undo' the changes in the array get reflected in the input field.
Thanks in advance for any suggestions!
Angular version: 14.0.7
My application is mixed with vanilla JS and React components, and I have a component that I would like to rerender by changing the props from outside the react component. How would I accomplish this? And maybe I need to be using "state" instead?
This component is only shown when a user clicks a dropdown link in a list of items. Because the list can be quite long, I am trying to be efficient with creating only one component and then changing the props (rerendering) depending on which item in the list if clicked.
This is the code the initially creates the component with references to the "root" and "element"
// Render AdjustDateFormulaComponent
let rootContainer = document.getElementById('rc_dateFormulaComponent');
if (rootContainer) {
this.dataFormulaWidgetRoot = createRoot(rootContainer);
this.dataFormulaWidgetElement = createElement(AdjustDateFormulaComponent, {
id: null,
entityType: 'taskTemplateEntry',
onSave: _this._dateFormulaUpdated, // callback method
});
this.dataFormulaWidgetRoot.render(this.dataFormulaWidgetElement);
}
When item is clicked, rerender by changing props (specifically the "id" field) and show/move in the appropriate dropdown div
_this.$widgetDiv.find('.dueDateAdjustDropdown').on('show.bs.dropdown', function () {
// Change props/rerender here with correct id
this.dataFormulaWidgetElement.props = { id: 124 }; // <--- THIS DOESNT WORK OBVIOUSLY
// When dropdown shown, move react component into dropdown div
$(document.getElementById('rc_dateFormulaComponent'))
.detach()
.appendTo($(this).find('.dropdownContent'));
});
Many thanks in advance.
I have a case where I want to remove an instance of a component from an array (in parent component) on a button click from inside child component based on some condition.
In the image, 'Add BU' button is in parent component and the three text fields below (namely 'BU', 'G Value' and 'C Value') and one cross button are coming from child component.
On each 'Add BU' button click a new row from child component is created in UI.
I used the following code to push instances of child to an array in parent :-
Now I have question regarding 2 points here :-
1. How may I delete the same specific child instance from the array in parent, on the same instance cross button click (cross button is inside child) ?
2. How may I send the values added in each text field in each row (i.e. 'BU', 'G Value' and 'C Value') on 'Add' button click ?
Some similar question has already been asked in here and I have used similar approach given in the accepted answer : Add component to dom on click of button in angular2. And my first question is also being asked as the last comment in image below :
To delete the child instance:
<payment-option
*ngFor="let bu of buList; index as i"
(delete)="removeBU(i)>
</payment-option>
removeBU(i: number) {
this.buList.splice(i, 1);
}
To get the data on add:
#ViewChildren(PaymentOptionComponent) paymentOptionComps!: QueryList<PaymentOptionComponent>;
add() {
const data = {};
this.paymentOptionComps.forEach(comp => {
// Get data from comp and add to data here.
});
}
Problem statement :
Parent component having <form> tag and some <input> tags inside it, and child component also have some <input> tags, parent component has one <submit> and we are validating form fields on submit the form.
How to validate the child component <input> fields from parent component on submit the form ?
Requirement :
If a parent component has a form containing child components with input components in their template, then these input components should be validate on click if submit from the parent component.
Findings :
There are lot of posts in SO having same problem statement but did not find any suitable solution.All the below posts validate the whole form but my requirement is to validate each field in child component.
Angular 2 validation together with the child component
Allow template-driven form inputs across a component hierarchy to register with a single parent form
How to check the validity of the child component form within the parent component in Angular 4
We can achieve it using template driven technique as well. Find below the steps :
From parent component to child component we have to pass submit button event.
<button type="button" (click)="enterForm(parentForm)">Submit</button>
Here, parentForm is the form reference.
call child component method using #ViewChild decorator from parent to pass submit button event on click of submit.
#ViewChild('validateChildComponentForm') private ChildComponent: ChildComponent;
Pass the reference of child form using #ViewChild decorator into the child component.
#ViewChild('smartyStreetForm') form;
enterForm(parentForm) {
this.submitted = true;
this.ChildComponent.validateChildForm(this.submitted);
if(!parentForm.valid || !this.childFormValidCheck) {
return;
} else {
// success code comes here.
}
}
Now in child component method we will check that if parent form is submitted and child component form is valid then emit true otherwise false into the parent component. we will use #Output decorator to emit the isChildFormValid value into the parent component.
#Output() isChildFormValid: EventEmitter<any> = new EventEmitter<any>();
public validateChildForm(data: any) {
if (data === true) {
if(this.form.valid === true) {
this.isChildFormValid.emit(true);
} else {
this.isChildFormValid.emit(false);
}
}
}
Now in parent component we will get the isChildFormValid value.
private isChildFormValid(formValid: any) {
this.childFormValidCheck = formValid;
}
Pictorial representation :
This question may sound a little complicated. But i am stuck here, so i am throwing it here.
I am rendering a child component multiple times in parent with different properties and an array.
Parent contains an array of headings, i am passing it to child components and mapping select options with that array. If an heading is selected from any of the child components, that heading should display as disabled in other child components.
Child:
{
this.props.xarray.map((heading, index) => {
if (heading.headingis.toLowerCase().indexOf(this.state.field) != -1) {
this.setcheckstate(); //ignore this function
this.props.actionis(index, 'disabled'); //Using this function (passed from parent) to disable selected option
return <option value={heading.headingis} key={index} selected disabled={heading.disabled}>{heading.headingis}</option>
}else{
return <option value={heading.headingis} key={index} disabled={heading.disabled}>{heading.headingis}</option>
}
})
}
Parent's actionis function:
handlerHeading(index, disabled) {
xarray[index]['disabled'] = disabled;
}
It's working somehow BUT problem is
If first component is rendered, it will disable that value
Then 2nd component is rendered, it will disable that value
along with previous one.
and so on...
But in first component, only one value is disabled, in 2nd component 2 values are disabled and in 3rd component 3 values are disabled. And so on...
But i want if one value is disabled in component 6, it should get disabled in all previous components.
Have a look at following images for example.
Component #1 Rendered:
Component #4 Rendered:
I have resolved this, by changing xarray to state of parent.
This:
state = {xarray: xarray}
And:
handlerHeading(index, disabled) {
xarray[index]['disabled'] = disabled;
this.setState({ xarray: xarray });
}
For some reason you cannot change Props as you desire. See This Question