Hello I have two components. Component 1 has a input-element to upload videos. It has a variable file
<input
type="file"
accept=".mp4"
#file
/>
Now I have Component2 which is a shared component:
<button (click)="file.click()">upload video</button>
Now I need the "file" variable passed to the shared component to execute the file.click();
I can't take the input element in the shared component
https://stackblitz.com/edit/angular-ivy-9wi6qs?file=src/app/component1/component1.component.html
This is as simple as sending the value into the component:
#Component({
selector: 'app-component1',
templateUrl: './component1.component.html',
styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit {
constructor() { }
public fileAddress: string;
public fileChanged(event) {
// read the file when it changes and store it locally
if(event.target.files.length > 0)
this.fileAddress = event.target.files[0].name;
}
}
ngOnInit() {
}
}
<!-- bind the event handler -->
<input type="file" accept=".mp4" (change)="fileChanged($event)" />
<!-- bind the local variable to an input on the child component -->
<app-upload-video [fileAddress]="fileAddress"></app-upload-video>
#Component({
selector: 'app-upload-video',
templateUrl: './upload-video.component.html',
styleUrls: ['./upload-video.component.css']
})
export class UploadVideoComponent implements OnInit {
//input variable to receive the value inside the component
#Input()
public fileAddress: string;
constructor() { }
ngOnInit() {
}
}
Related
I have a problem with reloading my child component from parent component in Angular.
Here is an example of what I want to do.
This is my child component
import { Component } from "#angular/core";
#Component({
selector: "app-child",
template: `<p> {{ticks}} </p>`,
styleUrls: ["./child.component.css"]
})
export class ChildComponent {
ticks = Date.now().valueOf();
constructor() {}
update(): void {
this.ticks = Date.now().valueOf();
}
}
And here is my parent component:
import { Component, OnInit, ViewChild } from '#angular/core';
import { ChildComponent } from './../child/child.component';
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
})
export class ParentComponent implements OnInit {
#ViewChild(ChildComponent, { static: false }) childC: ChildComponent;
showChild: boolean = true;
constructor() {}
ngOnInit() {}
onUpdateChild() {
this.childC.update();
}
}
Parent Component HTML :
<p>
parent works!
<button (click)="onUpdateChild()">update</button>
<app-child *ngIf="showChild"></app-child>
<app-child *ngIf="showChild"></app-child>
</p>
The main problem is that if I use my child component multiple time, and trying to click on “update” button, it just updates one of my child component that is used in parent component, but I want to update all same child component in parent component, here is what happens when you click on “update” button, only first value will change, not both.
How can I fix it ?!?
You can use #ViewChildren and QueryList to do what you are looking for.
#ViewChildren(ChildComponent) childrenC!: QueryList<ChildComponent>;
And your function would look like:
onUpdateChild() { //I would rename it to onUpdateChildren
if(this.childrenC) {
this.childrenC.forEach((childC: ChildComponent) => {
this.childC.update();
});
}
}
If you wanted your child component to update on it's own - as stated by a comment to your original post - this is an example of what you could do so you wouldn't even need the update button:
import { Component, OnInit } from "#angular/core";
#Component({
selector: "app-child",
template: `<p> {{ticks}} </p>`,
styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
ticks = Date.now().valueOf();
constructor() {}
ngOnInit(): void {
setInterval(() => {
this.ticks = Date.now().valueOf();
}, 1000); //updates every 1 second
}
}
Another option you can do without using a button is to use #Input with the ticks property that updates the value from the parent through an input.
I have a dialog box that is displaying a separate child component. The child component is below:
#Component({
selector: 'userEdit',
templateUrl: './edituser.component.html',
styleUrls: ['./edituser.component.css']
})
export class EditUserComponent implements OnInit {
public theName: string;
public theAddress: string;
constructor() {
this.theName = '';
this.theAddress = '';
}
ngOnInit() {
}
}
The dialog box code and template are below:
#Component({
selector: 'app-userdialog',
templateUrl: './userdialog.component.html',
styleUrls: ['./userdialog.component.css']
})
export class UserDialogComponent implements OnInit {
#ViewChild('userEdit', {static: false})
userEdit: EditUserComponent;
constructor(
public dlgRef: MatDialogRef<UserDialogComponent>,
#Inject(MAT_DIALOG_DATA) public theData: UsrStuff) { }
ngOnInit() {
}
ngAfterViewInit() {
console.log('Name: ' + this.userEdit.theName);
}
addUser() {
// TODO: implement adding a user
}
closeBox() {
this.dlgRef.close();
}
}
and
<div id="attribdlg">
<h3>Add New User</h3>
<userEdit theName="" theAddress=""></userEdit>
<mat-dialog-actions>
<button mat-raised-button color="primary" (click)="addUser()">Add User</button>
<button mat-raised-button color="primary" (click)="closeBox()">Done</button>
</mat-dialog-actions>
</div>
Based on the documentation and examples Ihave seen, this setup should enable me to print to the console the value pf userEdit's theName property in the ngAfterViewInit() function.
Unfortunately, this does not appear to be working.When the console log is called, I get the following failure message:
null: TypeError: Cannot read property 'theName' of undefined
Obviously, there is some kind of initialization of the child component that is supposed to happen, but I do not see this being done anywhere in the documentation! I am missing something.
How can I get this child component and its properties available to my dialog?
Two options:
Set an id to the component you wish to have with ViewChild():
TypeScript:
#ViewChild('userEdit', {static: false})
HTML:
<userEdit #userEdit theName="" theAddress=""></userEdit>
Select by directive or component:
TypeScript:
#import { EditUserComponent } from '...';
#ViewChild(EditUserComponent, {static: false})
HTML:
<userEdit theName="" theAddress=""></userEdit>
I highly recommend you to use app perfix for the component's selector!!!
#Component({
...
selector: 'app-user-edit',
...
})
I am trying to create a time-picker. The picker will be opened when the user focuses on a text-box. Now, a single page may contain multiple text-boxes, for each of which the picker should be opened. The issue I am facing is, I get the values from time-picker for different text-boxes, but when binding to ngModel, any selected value gets bound to all the text-boxes.
Let me show you my approach:
component.html
<input type="text" [(ngModel)]="pickerData" (focus)="initPicker($event)" id="f-1" />
<input type="text" [(ngModel)]="pickerData" (focus)="initPicker($event)" id="f-2" />
<div #timepicks></div> <!-- Here where the picker will be dynamically Injected -->
component.ts
import { Component, OnInit, ViewChild, Output, EventEmitter, HostListener, ViewContainerRef,
ComponentFactoryResolver } from '#angular/core';
import { TimepickComponent } from './timepick/timepick.component';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
pickerData: any;
#ViewChild('timepicks', {read: ViewContainerRef, static: false}) timepicks: ViewContainerRef;
constructor(
private _componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
}
initPicker = (event) => {
this.timepicks.clear();
let pickerComponentFactory =
this._componentFactoryResolver.resolveComponentFactory(TimepickComponent);
//Dynamically creating the ' TimepickComponent ' component
let pickerComponentRef = this.timepicks.createComponent(pickerComponentFactory);
(<TimepickComponent>(pickerComponentRef.instance)).pickerId = event.target.id; // Passing id
pickerComponentRef.instance.pickData.subscribe(res => {
this.pickerData = res;
pickerComponentRef.destroy();
});
}
}
Timepickcomponent.ts
.....
.....
#Input() pickerId: any;
#Output() pickData = new EventEmitter();
.....
.....
setClose = () => {
this.pickData.emit(this.valueHolder); // Emitting as output
}
Current Output
Screenshot 1
screenshot 2
As it can be seen, screen1 is opening based on text-box id, but in screen2, when I select and set, it gets populated in another text-box. Ideally, the selected picker from a text-box should bind with that particular text-box.
Any help would be appreciated.
I will use Document Service to access the element and set the value for clicked input control:
HTML:
<input type="text" (focus)="initPicker($event)" id="f-1" />
<input type="text" (focus)="initPicker($event)" id="f-2" />
<div #timepicks></div>
Removed Two way data binding
TS:
import { Component, OnInit, ViewChild, Output, EventEmitter, HostListener, ViewContainerRef, ComponentFactoryResolver, Inject } from '#angular/core';
import { TimepickComponent } from './timepick/timepick.component';
// Added Document reference
import { DOCUMENT } from '#angular/common';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'timepicker';
isVisible: boolean = false;
pickerData: any;
#ViewChild('timepicks', { read: ViewContainerRef, static: false }) timepicks: ViewContainerRef;
constructor(
#Inject(DOCUMENT) private document: HTMLDocument,
private _componentFactoryResolver: ComponentFactoryResolver
) { }
ngOnInit() {
}
initPicker = (event) => {
this.timepicks.clear();
let pickerComponentFactory = this._componentFactoryResolver.resolveComponentFactory(TimepickComponent);
let pickerComponentRef = this.timepicks.createComponent(pickerComponentFactory);
(<TimepickComponent>(pickerComponentRef.instance)).pickerId = event.target.id;
pickerComponentRef.instance.pickData.subscribe(res => {
console.log(event.target.id);
if (res) {
(<HTMLInputElement>this.document.getElementById(event.target.id)).value = res;
}
pickerComponentRef.destroy();
});
}
}
Import Inject from #angular/core and DOCUMENT from #angular/common
Injected in constructor: #Inject(DOCUMENT) private document: HTMLDocument
Used (<HTMLInputElement>this.document.getElementById(event.target.id)).value = res; to set the value attribute of clicked input element
Working_Demo
The problem here is you were binding both the input boxes with same variable pickerData. So, even if you change any one of them, the change will be reflected for both the values.
Since you have two textboxes you need two variables to store their values, like pickerData1 and pickerData2.
While calling the initPicker() you need to pass one more parameter i,e in which textbox the datepicker is currently being opened and store the date in that respective variable.
Html code
<input type="text" [(ngModel)]="pickerData1" (focus)="initPicker($event, 1)" id="f-1" />
<input type="text" [(ngModel)]="pickerData2" (focus)="initPicker($event, 2)" id="f-2" />
TS code
initPicker = (event, index) => {
this.timepicks.clear();
let pickerComponentFactory =
this._componentFactoryResolver.resolveComponentFactory(TimepickComponent);
//Dynamically creating the ' TimepickComponent ' component
let pickerComponentRef = this.timepicks.createComponent(pickerComponentFactory);
(<TimepickComponent>(pickerComponentRef.instance)).pickerId = event.target.id; // Passing id
pickerComponentRef.instance.pickData.subscribe(res => {
if (index === 1)
this.pickerData1 = res;
elseif (index === 2)
this.pickerData2 = res;
pickerComponentRef.destroy();
});
Trying to do child to parent communication with #Output event emitter but is no working
here is the child component
import { Component, OnInit, Output, Input, EventEmitter } from '#angular/core';
#Component({
selector: 'app-emiter',
templateUrl: './emiter.component.html',
styleUrls: ['./emiter.component.css']
})
export class EmiterComponent implements OnInit {
#Output() emitor: EventEmitter<any>
constructor() { this.emitor = new EventEmitter()}
touchHere(){this.emitor.emit('Should Work');
console.log('<><><><>',this.emitor) // this comes empty
}
ngOnInit() {
}
}
this is the html template
<p>
<button (click)=" touchHere()" class="btn btn-success btn-block">touch</button>
</p>
The console.log inside the touchHere it shows nothing
even if I put this inside the parent component it show nothing as well
parent component
import { Component , OnInit} from '#angular/core';
// service I use for other stuff//
import { SenderService } from './sender.service';
// I dont know if I have to import this but did it just in case
import { EmiterComponent } from './emiter/emiter.component'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
user: any;
touchThis(message: string) {
console.log('Not working: ${message}');
}
constructor(private mySessionService: SenderService) { }
}
and here is the html template
<div>
<app-emiter>(touchHere)='touchThis($event)'</app-emiter>
</div>
Parent component template:
<app-emitor (emitor)='touchThis($event)'></app-emiter>
In parent template #Output should be 'called', not the child method.
Also, see: https://angular.io/guide/component-interaction#parent-listens-for-child-event
Here’s an example of how we write a component that has outputs:
#Component({
selector: 'single-component',
template: `<button (click)="liked()">Like it?</button>`
})
class SingleComponent {
#Output() putRingOnIt: EventEmitter<string>;
constructor() {
this.putRingOnIt = new EventEmitter();
}
liked(): void {
this.putRingOnIt.emit("oh oh oh");
}
}
Notice that we did all three steps: 1. specified outputs, 2. created an EventEmitter that we attached
to the output property putRingOnIt and 3. Emitted an event when liked is called.
If we wanted to use this output in a parent component we could do something like this:
#Component({
selector: 'club',
template: `
<div>
<single-component
(putRingOnIt)="ringWasPlaced($event)"
></single-component>
</div>`
})
class ClubComponent {
ringWasPlaced(message: string) { console.log(`Put your hands up: ${message}`);
} }
// logged -> "Put your hands up: oh oh oh"
Again, notice that:
putRingOnIt comes from the outputs of SingleComponent
ringWasPlaced is a function on the ClubComponent
$event contains the thing that wasemitted, in this case a string
<app-emiter (emitor)="touchThis($event)" ></app-emiter>
By using #Output() you should apply the event you need to emit in the directive of the emitter component.Adding the name of the variable to the the directive and but the emitted over function inside the quotation passing the $event.
touchHere() is the method from which you are binding some value to emit with your EventEmitter. And your EventEmitter is 'emitor'.
So your code will work if you simply do the below:
<app-emiter (emitor)='touchThis($event)'></app-emiter>
I would like to pass the parent component's FormGroup to its child for the purpose of displaying an error-message using the child.
Given the following parent:
parent.component.ts
import { Component, OnInit } from '#angular/core'
import {
REACTIVE_FORM_DIRECTIVES, AbstractControl, FormBuilder, FormControl, FormGroup, Validators
} from '#angular/forms'
#Component({
moduleId: module.id,
selector: 'parent-cmp',
templateUrl: 'language.component.html',
styleUrls: ['language.component.css'],
directives: [ErrorMessagesComponent]
})
export class ParentCmp implements OnInit {
form: FormGroup;
first: AbstractControl;
second: AbstractControl;
constructor(private _fb: FormBuilder) {
this.first = new FormControl('');
this.second = new FormControl('')
}
ngOnInit() {
this.form = this._fb.group({
'first': this.first,
'second': this.second
});
}
}
I would now like to pass the form:FormGroup variable above to the child component below:
error-message.component.ts
import { Component, OnInit, Input } from '#angular/core'
import { NgIf } from '#angular/common'
import {REACTIVE_FORM_DIRECTIVES, FormGroup } from '#angular/forms'
#Component({
moduleId: module.id,
selector: 'epimss-error-messages',
template: `<span class="error" *ngIf="errorMessage !== null">{{errorMessage}}</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
})
export class ErrorMessagesComponent implements OnInit {
#Input() ctrlName: string
constructor(private _form: FormGroup) { }
ngOnInit() { }
get errorMessage() {
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl);
// for (let propertyName of ctrl.errors) {
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched) {
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
// }
// }
return null;
}
The constructor formGroup represents the FormGroup of the parent - in its present form it does not work.
I am trying to follow this obsolete example at http://iterity.io/2016/05/01/angular/angular-2-forms-and-advanced-custom-validation/
In the parent component do this:
<div [formGroup]="form">
<div>Your parent controls here</div>
<your-child-component [formGroup]="form"></your-child-component>
</div>
And then in your child component you can get hold of that reference like so:
export class YourChildComponent implements OnInit {
public form: FormGroup;
// Let Angular inject the control container
constructor(private controlContainer: ControlContainer) { }
ngOnInit() {
// Set our form property to the parent control
// (i.e. FormGroup) that was passed to us, so that our
// view can data bind to it
this.form = <FormGroup>this.controlContainer.control;
}
}
You can even ensure either formGroupName or [formGroup] is specified on your component by changing its selector like so:
selector: '[formGroup] epimss-error-messages,[formGroupName] epimss-error-messages'
This answer should be sufficient for your needs, but if you want to know more I've written a blog entry here:
https://mrpmorris.blogspot.co.uk/2017/08/angular-composite-controls-formgroup-formgroupname-reactiveforms.html
For Angular 11 I tried all the above answers, and in different combinations, but nothing quite worked for me. So I ended up with the following solution which worked for me just as I wanted.
TypeScript
#Component({
selector: 'fancy-input',
templateUrl: './fancy-input.component.html',
styleUrls: ['./fancy-input.component.scss']
})
export class FancyInputComponent implements OnInit {
valueFormGroup?: FormGroup;
valueFormControl?: FormControl;
constructor(
private formGroupDirective: FormGroupDirective,
private formControlNameDirective: FormControlName
) {}
ngOnInit() {
this.valueFormGroup = this.formGroupDirective.form;
this.valueFormControl = this.formGroupDirective.getControl(this.formControlNameDirective);
}
get controlName() {
return this.formControlNameDirective.name;
}
get enabled() {
return this.valueFormControl?.enabled
}
}
HTML
<div *ngIf="valueFormGroup && valueFormControl">
<!-- Edit -->
<div *ngIf="enabled; else notEnabled" [formGroup]="valueFormGroup">
<input class="input" type="text" [formControlName]="controlName">
</div>
<!-- View only -->
<ng-template #notEnabled>
<div>
{{valueFormControl?.value}}
</div>
</ng-template>
</div>
Usage
Note that I had to add ngDefaultControl otherwise it would give no default value accessor error in console (if somebody knows how to get rid of it without error - will be much appreciated).
<form [formGroup]="yourFormGroup" (ngSubmit)="save()">
<fancy-input formControlName="yourFormControlName" ngDefaultControl></fancy-input>
</form>
this is an example of child component used inside parent formGroup :
child component ts:
import { Component, OnInit, Input } from '#angular/core';
import { FormGroup, ControlContainer, FormControl } from '#angular/forms';
#Component({
selector: 'app-date-picker',
template: `
<mat-form-field [formGroup]="form" style="width:100%;">
<input matInput [matDatepicker]="picker" [placeholder]="placeHolder" [formControl]="control" readonly>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-icon (click)="clearDate()">replay</mat-icon>`,
styleUrls: ['./date-picker.component.scss']
})
export class DatePickerComponent implements OnInit {
public form: FormGroup;
public control : FormControl;
#Input() controlName : string;
#Input() placeHolder : string;
constructor(private controlContainer: ControlContainer) {
}
clearDate(){
this.control.reset();
}
ngOnInit() {
this.form = <FormGroup>this.controlContainer.control;
this.control = <FormControl>this.form.get(this.controlName);
}
}
css date picker :
mat-icon{
position: absolute;
left: 83%;
top: 31%;
transform: scale(0.9);
cursor: pointer;
}
and used like this :
<app-date-picker class="col-md-4" [formGroup]="feuilleForm" controlName="dateCreation" placeHolder="Date de création"></app-date-picker>
Parent Component :
#Component({
selector: 'app-arent',
templateUrl: `<form [formGroup]="parentFormGroup" #formDir="ngForm">
<app-child [formGroup]="parentFormGroup"></app-child>
</form> `
})
export class ParentComponent implements {
parentFormGroup :formGroup
ngOnChanges() {
console.log(this.parentFormGroup.value['name'])
}
}
Child Component :
#Component({
selector: 'app-Child',
templateUrl: `<form [formGroup]="childFormGroup" #formDir="ngForm">
<input id="nameTxt" formControlName="name">
</form> `
})
export class ChildComponent implements OnInit {
#Input() formGroup: FormGroup
childFormGroup :FormGroup
ngOnInit() {
// Build your child from
this.childFormGroup.addControl('name', new FormControl(''))
/* Bind your child form control to parent form group
changes in 'nameTxt' directly reflect to your parent
component formGroup
*/
this.formGroup.addControl("name", this.childFormGroup.controls.name);
}
}
The ngOnInit was important - this did not work in the constructor.
And I prefer looking for the FormControlDirective - its the first one found in the child component's ancestor hierarchy
constructor(private formGroupDirective: FormGroupDirective) {}
ngOnInit() {
this.formGroupDirective.control.addControl('password', this.newPasswordControl);
this.formGroupDirective.control.addControl('confirmPassword', this.confirmPasswordControl);
this.formGroup = this.formGroupDirective.control;
}
I would do this in this way, i have passed child form data as group to parent so you can have separated form data in submit call.
Parent:
<form [formGroup]="registerStudentForm" (ngSubmit)="onSubmit()">
<app-basic-info [breakpoint]="breakpoint" [formGroup]="registerStudentForm"></app-basic-info>
<button mat-button>Submit</button>
</form>
Child:
<mat-card [formGroup]="basicInfo">
<mat-card-title>Basic Information</mat-card-title>
<mat-card-content>
<mat-grid-list
[gutterSize]="'20px'"
[cols]="breakpoint"
rowHeight="60px"
>
<mat-grid-tile>
<mat-form-field appearance="legacy" class="full-width-field">
<mat-label>Full name</mat-label>
<input matInput formControlName="full_name" />
</mat-form-field>
</mat-grid-tile>
</mat-grid-list>
</mat-card-content>
</mat-card>
Parent.ts:
export class RegisterComponent implements OnInit {
constructor() { }
registerForm = new FormGroup({});
onSubmit() {
console.warn(this.registerForm.value);
}
}
Child.ts
export class BasicInfoComponent implements OnInit {
#Input() breakpoint;
#Input() formGroup: FormGroup;
basicInfo: FormGroup;
constructor() { }
ngOnInit(): void {
this.basicInfo = new FormGroup({
full_name: new FormControl('Riki maru'),
dob: new FormControl(''),
});
this.formGroup.addControl('basicInfo', this.basicInfo);
}
}
Here in your child form components #Input() formGroup: FormGroup; part would be reference of parent component
I would pass the form as an input to the child component;
#Component(
{
moduleId: module.id,
selector: 'epimss-error-messages',
template: `
<span class="error" *ngIf="errorMessage !== null">{{errorMessage}}</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
})
export class ErrorMessagesComponent implements OnInit {
#Input()
ctrlName: string
#Input('form') _form;
ngOnInit() {
this.errorMessage();
}
errorMessage() {
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl)
// for (let propertyName of ctrl.errors) {
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched) {
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
// }
// }
return null;
}
And of course you'll need o pass the form from the parent component to the child, which you can do it in different ways , but the simplest is :
Somewhere in your parent ;
<epimss-error-messages [form]='form'></epimss-error-messages>
If you want to access the parent from the child component, you can access parent property of the FormControl instance, https://angular.io/api/forms/AbstractControl#parent
To get the parent error:
const parent = control.parent;
const errors = parent.errors;