How to pass data from a component to another component - html

When I click to edit I can edit but when I try to append a kind to the parent it takes the parent infoprmation and not create a new VI.
With angular dialog I made like this but with component I dont know.
This is with dialog
<div class="dropdown-menu-item" (click)="openValueItemEditDialog({valueItem: valueItem})">Edit</div>
This is the TS
openValueItemEditDialog(editOptions: EditOptions) {
this.dialog.open(ValueItemEditDialogComponent, {data: editOptions, disableClose: true});
}
And this is the TS of dialog
constructor(private store: Store<ApplicationState>, #Inject(MAT_DIALOG_DATA) public editOptions: EditOptions) {
}
ngOnInit() {
if (this.editOptions.valueItem) {
this.editedValueItem = _.cloneDeep(this.editOptions.valueItem);
} else {
this.editedValueItem = emptyValueItem();
}
export interface EditOptions {
valueItem?: ValueItem;
appendToParentId?: string;
planning?: number;
}
But without dialog I dont know how to pass the data
showChild = false
edit(editOptions: EditViOptions) {
this.showChild = !this.showChild;
if (editOptions.valueItem) {
return editOptions.valueItem;
} else if (editOptions.appendToParentId) {
return editOptions.appendToParentId;
}
this.activeSelected = this.valueItem.id;
} //this open and closes the component
}
This is the Html of Service matrix row
<app-edit-dialog-vi [showMePartially]="showChild"></app-edit-dialog-vi>
<div *ngIf="valueItem.level < 7" class="dropdown-menu-item" (click)="edit({appendToParentId: valueItem})">Append2</div>
Here is the edit-dialog-vi
<div *ngIf="showMePartially" class="container">
</div>
In the constructor I dont know what to write
This is the TS file and interface what I have created for the Edit vi dialog
constructor(private store: Store<ApplicationState>, public editOptions: ServiceMatrixRowComponent) { }
ngOnInit() {
if (this.editOptions.valueItem) {
this.editedValueItem = _.cloneDeep(this.editOptions.valueItem);
console.log(this.editedValueItem, '***');
} else {
this.editedValueItem = emptyValueItem();
}
export interface EditViOptions {
valueItem?: ValueItem;
appendToParentId?: string;
planning?: number;
}

I don't know if I understood your question well, but you can try this :
ParentHtml
<app-edit-dialog-vi [data]="dataToPass" [showMePartially]="showChild"></app-edit-dialog-vi>
<div class="dropdown-menu-item" (click)="edit(valueItem.level < 7 ? {appendToParentId: valueItem.id} : {valueItem: valueItem})">Edit2</div>
Parent Ts
Add a property dataToPass, and set it to the data you want to send to the child.
showChild = false;
dataToPass: EditViOptions = null;
edit(editOptions: EditViOptions) {
this.showChild = !this.showChild;
if (editOptions.valueItem || editOptions.appendToParentId) {
this.dataToPass = editOptions;
}
this.activeSelected = editOptions.valueItem.id;
}
Child Ts
Add an input named data to get the data sent :
#Input() showMePartially = false;
#Input() data: EditViOptions = null;
constructor(private store: Store<ApplicationState>, public editOptions: ServiceMatrixRowComponent) { }
ngOnInit() {
if (this.data.valueItem || this.data.appendToParentId) {
this.editedValueItem = _.cloneDeep(this.data.valueItem);
console.log(this.editedValueItem, '***');
} else {
this.editedValueItem = emptyValueItem();
}
export interface EditViOptions {
valueItem?: ValueItem;
appendToParentId?: string;
planning?: number;
}

To learn better i will suggest you just look at the below link. It will let you know how to pass data from Parent-> Child and Child-->Parent component in different ways.
https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/
Hope it gonna help you.
Thanks

Related

Pass data from parent component to child component using input, how to implement?

There is a parent component with all the logic of the chart, you need to transfer data to the child component using #Input (), that is, so that I can display the chart in any of the components using #Input.
The parent component is logs.component and the child component is echarts.component. It is necessary to pass the data to LoadEcharts(), it contains all the logic of the Chart, that is, that I could call it on any html component
logs.components.ts
export class LogsComponent implements OnInit {
sideNavStatus: boolean = false;
subscription!: Subscription;
logs!: Logs[];
constructor(private dataService: DataService) {
}
columnDefs = [
{ headerName: 'Username', field: 'username', flex: 1},
{ headerName: 'Event', field: 'event', flex: 1 },
{ headerName: 'Date', field: 'date', flex: 1 }
];
ngOnInit() {
this.LoadLogs();
this.LoadEcharts();
}
LoadLogs(): void {
this.dataService.getLogs().subscribe(logs => this.logs = logs);
}
LoadEcharts(): void {
const chartDom: HTMLElement = document.getElementById('main') as HTMLElement;
const myChart = echarts.init(chartDom);
this.subscription = this.dataService.getLogs().subscribe(data => {
myChart.setOption(this.initBasicEchart(data))
})
}
private initBasicEchart(data: Logs[]) {
const result: any = {};
data.forEach(el => {
const date = el.date.toString().substring(0, 10);
if (!result[el.event]) {
result[el.event] = {};
if (!result[el.event][date]) {
result[el.event][date] = 1;
}
} else {
if (!result[el.event][date]) {
result[el.event][date] = 1;
} else {
result[el.event][date] += 1;
}
}
});
const login = {
x: Object.keys(result.Login),
y: Object.values(result.Login)};
const reg = {
c: Object.keys(result.Registration),
z: Object.values(result.Registration)};
return {
title: {
text: 'Graphic login and registration.'
},
tooltip: {},
xAxis: {
type: 'category',
data: (reg.c, login.x)
},
yAxis: {
},
series: [
{
name: 'Login',
type: 'bar',
data: login.y,
},
{
name: 'Registration',
type: 'bar',
data: reg.z,
}
]
};
}
}
logs.component.html
<div class="container-fluid g-0">
<app-header (sideNavToggled)="sideNavStatus = $event;"></app-header>
<main>
<app-sidebar [sideNavStatus]="sideNavStatus"
[ngClass]="{'app-side-nav-open': sideNavStatus}"></app-sidebar>
<div class="display-area p-3" [ngClass]="{'display-area-shrink': sideNavStatus}">
<p class="fs-1 fw-bold fst-italic text-center">Login and registration statistics.
</p>
<app-aggreed
*ngIf="logs"
[logs]="logs"
[columnDefs]="columnDefs"
></app-aggreed>
</div>
</main>
</div>
<app-echarts
></app-echarts>
<app-footer></app-footer>
echarts.component.html
<div class="container-fluid g-0">
<app-header (sideNavToggled)="sideNavStatus = $event;"></app-header>
<main>
<app-sidebar [sideNavStatus]="sideNavStatus"
[ngClass]="{'app-side-nav-open': sideNavStatus}"></app-sidebar>
<div class="display-area p-3" [ngClass]="{'display-area-shrink': sideNavStatus}">
<div
id="main"
style="width: 500px; height: 300px"
>
</div >
</div>
</main>
</div>
<app-footer></app-footer>
export class EchartsComponent implements OnInit {
sideNavStatus: boolean = false;
subscription!: Subscription;
constructor(private dataService: DataService) {
}
ngOnInit(): void {
}
}
I tried to pass methods to the child component through the input but nothing comes out
From your code sample, I'm assuming you're trying to access the EchartsComponent ViewChild in the logs component and pass data to it.
Here's an example of how you can do that, minus some pieces from your sample code for brevity...
class LogsComponent {
#ViewChild(EchartsComponent)
echartsComponent: EchartsComponent;
LoadEcharts(): void {
const chartDom: HTMLElement = document.getElementById('main') as HTMLElement; // <-- not sure what this is
const myChart = echarts.init(chartDom); // <-- not sure what this is
this.subscription = this.dataService.getLogs().subscribe(data => {
this.echartsComponent.logs = data;
// `logs` doesn't exist in the current code for EchartsComponent.
// You'll need to add it.
});
}
}
What I don't see in your EchartsComponent is a logs property to set. If you're using a ViewChild in the parent component, you don't have to use #Input on the ViewChild instance, you have programmatic access to the component and can set properties or call methods.
If you want to use #Input(), you can do that too:
class EchartsComponent {
#Input()
logs: Logs[];
}
// logs.component.ts
class LogsComponent {
LoadEcharts(): void {
this.subscription = this.dataService.getLogs().subscribe(data => {
this.logs = data;
})
}
}
// logs.component.html
<app-echarts [logs]="logs"></app-echarts>
In this scenario, when the observable for getLogs() emits, the property logs is set to the new value. That value, being a new reference is passed to the child component via its input.
Hope that helps you out.

Syncfusion NumericTextbox disable select

I am using an Angular NumericTextbox from Syncfusion in my application. We ran in the issue that when you click on the input it will automaticly select it. Is there a way to disable it?
Issue:
https://gyazo.com/a72bd4aaf4ebda7a98256d31e3959a48
Docs:
https://ej2.syncfusion.com/angular/documentation/numerictextbox/getting-started/
HTML:
<ejs-numerictextbox
[floatLabelType]="floatLabelType"
[enabled]="enabled"
[min]="min"
[max]="max"
[placeholder]="caption"
[format]="format"
[ngClass]="{
'e-success': (control?.dirty || control?.touched) && !control?.invalid,
'e-error': (control?.dirty || control?.touched) && control?.invalid,
'hum-show-required': !this.hideRequired,
'hum-required': isRequired()
}"
[currency]="currency"
(change)="updateControlValue($event)"
(blur)="handleBlur($event)"
></ejs-numerictextbox>
TS
export class FormNumberComponent extends FormBaseComponent {
#ViewChild(NumericTextBoxComponent, { static: true }) valueAccessor: ControlValueAccessor;
#Input() format: string = 'n0';
#Input() min = 0;
#Input() max: number;
#Input() currency = 'EUR';
private busy: boolean;
constructor(injector: Injector, stateService: StateService) {
super(injector);
this.initLogging(false, 'FormNumberComponent');
this.currency = stateService.getCurrency();
}
updateControlValue(event: any): void {
console.log(event);
setTimeout(() => {
// todo - hacky way to fix the issue (integration of ejs with form needs to be refactored)
const formControl: NumericTextBoxComponent = this.valueAccessor as NumericTextBoxComponent;
if (!isObjectEmpty(formControl) && !formControl.isDestroyed) {
this.busy = true;
formControl.focusIn();
formControl.focusOut();
this.busy = false;
}
});
}
handleBlur(e) {
if (!this.busy) {
super.handleBlur(e);
}
}
}
Your requirement to disable the auto select functionality of the Numeric textbox inputs can be achieved by using the click event. please check the code below,
Code snippet
<ejs-numerictextbox
value="10"
(click)="OnClick($event)">
</ejs-numerictextbox>
OnClick(args): void {
var position = args.srcElement.selectionEnd;
args.srcElement.selectionStart = args.srcElement.selectionEnd = position;
}
Sample: https://stackblitz.com/edit/angular-vgqmzs-i93zpr?file=app.component.ts

Angular: ExpressionChangedAfterItHasBeenCheckedError when trying to disable button

I use mat-dialog to edit details of my profile page. I'm getting an ExpressionChangedAfterItHasBeenCheckedError when I click the 'Edit age' button and the dialog window pops up.
I decided to extract the styling of all edit dialogs into a single edit.component:
edit.component.html
<div class="navigation-control">
<mat-icon (click)="onCancelButtonClicked()"
class="close-button">close</mat-icon>
</div>
<div class="content-main">
<ng-content select=".content-main"></ng-content>
</div>
<div class="content-bot">
<button mat-raised-button
(click)="onCancelButtonClicked()">Cancel</button>
<button mat-raised-button
(click)="onActionButtonClicked()"
[lnDisableButton]="actionButtonDisabled">{{actionButtonValue}}</button>
</div>
edit.component.ts
#Component({ selector: 'ln-edit', ... })
export class EditComponent {
#Input() actionButtonValue: string;
#Input() actionButtonDisabled: boolean;
#Output() cancelButtonClicked = new EventEmitter<void>();
#Output() actionButtonClicked = new EventEmitter<void>();
onCancelButtonClicked() {
this.cancelButtonClicked.emit();
}
onActionButtonClicked() {
this.actionButtonClicked.emit();
}
}
To avoid the ExpressionChangedAfterItHasBeenCheckedError when trying to disable buttons and controls, I used this snippet. But that didn't solve this issue.
disable-button.directive.ts
#Directive({ selector: '[lnDisableButton]' })
export class DisableButtonDirective {
#Input('lnDisableButton') isDisabled = false;
#HostBinding('attr.disabled')
get disabled() { return this.isDisabled; }
}
The following is the contents of a mat-dialog window. This gets instantiated when I click the 'Edit age' button. When I remove the [actionButtonDisabled]="actionButtonDisabled", the error goes away, but obivously I need that line to make the functionality disable the button.
age-edit.component.html
<ln-edit [actionButtonValue]="actionButtonValue"
[actionButtonDisabled]="actionButtonDisabled"
(cancelButtonClicked)="onCancelButtonClicked()"
(actionButtonClicked)="onActionButtonClicked()">
<form [formGroup]="ageForm"
class="content-main">
<ln-datepicker formControlName="birthday"
[appearance]="'standard'"
[label]="'Birthday'"
class="form-field">
</ln-datepicker>
</form>
</ln-edit>
I handle the disabling/enabling the button in the 'ts' part of the mat-dialog popup.
age-edit.component.ts
#Component({ selector: 'ln-age-edit', ... })
export class AgeEditComponent implements OnInit, OnDestroy {
ageForm: FormGroup;
private initialFormValue: any;
actionButtonDisabled = true;
private unsubscribe = new Subject<void>();
constructor(
private editPhotoDialogRef: MatDialogRef<AgeEditComponent>,
private fb: FormBuilder,
#Inject(MAT_DIALOG_DATA) public dialogData: Date) { }
ngOnInit() {
this.initializeAgeForm();
this.loadDataToAgeForm(this.dialogData);
this.trackFormDistinct();
}
private initializeAgeForm(): void {
this.ageForm = this.fb.group({
birthday: null,
});
}
loadDataToAgeForm(birthday: Date | null): void {
if (!birthday) { return; }
this.ageForm.setValue({ birthday });
this.initialFormValue = this.ageForm.value;
}
get birthdayAC() { return this.ageForm.get('birthday') as AbstractControl; }
get actionButtonValue(): string {
return this.birthdayAC.value ? 'Update age' : 'Add age';
}
onCancelButtonClicked(): void {
this.editPhotoDialogRef.close();
}
onActionButtonClicked(): void {
this.editPhotoDialogRef.close({ ... });
}
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
ngOnDestroy() { ... }
}
I suspect this has something to do with content projection, but I'm not sure.
(...or perhaps with my custom 'ln-datepicker'?)
Any ideas?
Thanks.
From what I can tell, the problem resides in trackFormDistinct() method:
trackFormDistinct(): void {
this.ageForm.valueChanges.pipe(
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(val => {
(this.formValueNotDistinct(this.ageForm.value, this.initialFormValue)
|| this.birthdayAC.value === null)
? this.actionButtonDisabled = true
: this.actionButtonDisabled = false;
});
}
Looks like because of this.ageForm.valueChanges, will have different values in the 2 change detection cycles. I think this.ageForm.valueChanges emits due to <ln-datepicker>.
In a tree of form controls, if one node calls setValue, all its ancestors will have to be updated. I've written more about how Angular Forms work in this article.
I'm thinking of 2 alternatives:
skip the first emission of ageForm since it indicates the initialization of the form control tree, so this is irrelevant to the logic inside subscribe's callback.
this.ageForm.valueChanges.pipe(
skip(1),
distinctUntilChanged(), // TODO: needed?
takeUntil(this.unsubscribe)
).subscribe(/* .... */)
initialize actionButtonDisabled with false, since the error complains that it switched from true to false
actionButtonDisabled = false;

Angular directive *ngIf not working as intended

I have this block of html in my template to show or hide the div.
<div *ngIf="csvVisible">
<p>Paragraph works</p>
</div>
This is my component.
export class SettingsComponent implements OnInit {
csvVisible: boolean = false;
private dataSource: string[];
#ViewChild(MatTable, { static: true }) table: MatTable<any>;
constructor(private dialog: MatDialog, private templateParserService: TemplateParserService) { }
ngOnInit() {
this.templateParserService.subscribe({
next(result: string[]) {
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},
error(error: Error) {
console.log(error.message);
}
});
}
Eventhough the DIV is hidden at start, it doesnt automatically show / hide on the csvVisible value change. Value of csvVisible is properly set when the observer emits data. [hidden]="csvVisible" isn't working either.
Edit :
Subscriber registration on the service is done by the following code.
private subject = new Subject<string[]>();
public subscribe(observer: any): Subscription {
return this.subject.subscribe(observer);
}
Since you are using Object inside subscribe, this points to current subscribe object, Instead of using subscribe({next:()}) try using this way
component.ts
this.templateParserService.subscribe((result: string[])=>{
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},(error: Error)=>{
console.log(error.message);
});

Using service.ts variables on multiple components

I've set up next.service.ts with 3 variables (user, action, rest) and made setters(updateNext()) and getters (getUser, getAction, getRest). I've got to use the setter to change the variables in one component (stock-management component) and retrieved these variables in another component (inventory-record component) but I can't seem to retrieve them from another component (inventory-record-filled component).
I've tried returning a string ("TEST") in the getter and it worked, but when I tried returning a variable, it just returned nothing/empty string.
export class NextService {
private action: string;
private user: string;
private restraunt: string;
constructor() { }
updateNext(actions, users, restraunts) {
this.action = actions;
this.user = users;
this.restraunt = restraunts;
}
getAction(): string {
return this.action;
}
getUser(): string {
return this.user;
}
getRest(): string {
return this.restraunt;
}
export class InventoryRecordComponent implements OnInit {
name = '';
rest = '';
action = '';
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
document.getElementById('dne').style.display = 'none';
}
onSubmit(f: NgForm) {
const x = document.getElementById('dne');
if (!this.next.updateCode(this.code)) {
x.style.display = 'block';
f.resetForm();
} else {
this.next.updateCode(this.code);
location.replace('inventory-record/qty');
}
}
}
export class InventoryRecordFilledComponent implements OnInit {
name: string;
action: string;
rest: string;
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
}
}
Each component have its respective html files with {{ name }} {{ action }} {{ rest }}
If you need your component to behave as a Simpleton (where it contains the same values regardless of where in the application it is used) you must set its providedIn value to "root", like so:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class NextService {
// The rest of the code stays the same
}
The description for that can be found here: https://angular.io/guide/singleton-services#providing-a-singleton-service
If you don't do that, each component that imports NextService will have it's own instance of NextService, with its own isolated values. If you want the values of a service to be available everywhere that the service is used in, then you want the service to be a Simpleton, so you must follow the steps.
Following the steps above is not the only way to make your component a Simpleton, but as the link mentions, it is the preferred way to do that.
Hope that helps!