I need help about angular component comunication.
I have Parent component and children.
In parent component i have list. I would like to click of item list and move data {name, text} to children component and set it to children component where is froala editor.
In Angular, there are two ways you can do implement component intercommunication.
Pass data from parent to child with input binding
#Component({
selector: 'app-parent',
template: `
<app-child [childMessage]="parentMessage"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentMessage = "message from parent"
constructor() { }
}
#Component({
selector: 'app-child',
template: `
Say {{ message }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Input() childMessage: string;
constructor() { }
}
Using Angular Services(This method is used when the components communicating are directly related ). But from you are describing you can do it better with angular service. When you click the item list, you can call the function of services that pushes the data to the BehaviourSubject. After that, all the component that are subscribed to that Behaviour subject will get the data you passed.
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
Related
I want to call a function of another component which has no relation on the click
servive File
import { Injectable } from '#angular/core';
#Injectable()
export class ShareDetailsService {
clicked : boolean = false;
constructor() { }
}
component 2
#Component({
selector: 'app-component2',
templateUrl: './component2.component.html',
styleUrls: ['./component2.component.css']
})
export class Component2Component implements OnInit {
constructor(shareDetailsService : ShareDetailsService) { }
ngOnInit() {
if(shareDetailsService.clicked){
console.log("clicked");
}
}
Component 1
#Component({
selector: 'app-component1',
templateUrl: './component1.component.html',
styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit {
constructor(shareDetailsService : ShareDetailsService) { }
ngOnInit() {
if(shareDetailsService.clicked){
console.log("clicked");
}
}
someFunction(){
}
Component 1 Html
<div (click)="someFunction()"></div>
When I call the function using the click event it should call the function of the another component , I created a service file and where I am able to change the boolean variable.
I Want to call the function when there is a change in the boolean Variable rather than calling the function on ngOnit()
Use a subject to subscribe to it.
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs'
#Injectable()
export class ShareDetailsService {
clicked$ = new Subject();
}
// Any component can post and subscribe to it
#Component({
selector: 'app-component2',
templateUrl: './component2.component.html',
styleUrls: ['./component2.component.css']
})
export class Component2Component implements OnInit {
constructor(shareDetailsService : ShareDetailsService) {
this.sharedDetailsService.clicked$.subscribe(() => console.log('clicked')
}
onClick() {
this.sharedDetailsService.clicked$.next(); // dispatch the click
}
}
appcomp.html
`<app-child (callemit) = parentFunc($event)> </app-child>`
appcomp.ts
`
import { Component, OnInit, EventEmitter } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.comp.html'
})
export class AppComponent {
ngOnInit() {}
parentFunc(event){
console.log(event)
}
}
`
childcomp.html
childcomp.ts
`
#Component({
selector: 'app-child',
templateUrl: './app.child.component.html'
})
`
mydirective.ts
`
import { Directive, ElementRef, Input, Output, HostListener, EventEmitter } from '#angular/core';
#Directive({
selector: '[myDirective]'
})
export class myAppDirective {
constructor() {}
#Input ('myDirective') val: string;
#Output() callEmit = new EventEmitter();
#HostListener('click')
onClick() {
event.preventDefault();
this.callEmit.emit({event , val});
}
}
`
In the above code i am trying to call parentFunc from appcomp using eventemitter and not able to get this work. Please let me know what is wrong here.
I think you most call callEmit on child component
childcomp.html
and in child Component emit the CallEmit Which called from appComp
childcomp.ts
#Output() callEmit = new EventEmitter();
childFunc(){
this.callEmit.emit();
}
and finally use
appCom.html
`<app-child (callemit) = parentFunc($event)> </app-child>`
appcom.ts
parentFunc(event){
console.log(event)
}
https://stackblitz.com/edit/angular-exxhms
Try it on component
Im having some trouble figuring this out, basically I have a headerTitleService which I want to be able to dynamically set the title in my header component but for some reason when I set the title nothing shows up? Im not getting any errors so I can seem to figure out what the problem is..
headerTitle.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class HeaderTitleService {
title = new BehaviorSubject('');
constructor() { }
setTitle(title: string) {
this.title.next(title);
}
}
header.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../../services/headerTitle.service'
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
providers: [HeaderTitleService]
})
export class HeaderComponent implements OnInit {
title: string;
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.title.subscribe(updatedTitle => {
this.title = updatedTitle;
});
}
}
header.component.html
<h1>{{title}}</h1>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { HeaderTitleService } from '../../services/headerTitle.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
providers: [HeaderTitleService]
})
export class HomeComponent implements OnInit {
constructor(
private headerTitleService: HeaderTitleService
) { }
ngOnInit() {
this.headerTitleService.setTitle('hello');
}
}
The line providers: [HeaderTitleService] in each component means that they will be given one HeaderTitleService each, rather than one between them.
To fix this remove providers: [HeaderTitleService] from your components, and place it in your module definition instead:
#NgModule({
providers: [HeaderTitleService]
})
Move HeaderTitleService in providers of your module. With your implementation you receive new instance of the HeaderTitleService in each component.
Hope this helps.
I'm using a service to pass data between child and parent with the purpose being to update the parent ui with child specific info depending on what child is loaded.
Service:
import { Subject } from "rxjs/Subject";
export class ChildDataService {
private childDetails = new Subject<{}>();
childLoaded$ = this.childDetails.asObservable();
changedComponentName(option: {}){
this.childDetails.next(option);
}
}
Parent Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../core/helpers/child-data.service";
import { Subscription } from "rxjs/Subscription";
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent implements OnInit {
childDetails: {title?: string};
private subscription:Subscription;
constructor( private childDataService: ChildDataService) {
this.childDataService.childLoaded$.subscribe(
newChildDetails => {
console.log(newChildDetails);
this.childDetails = newChildDetails
});
}
}
Example Child Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../../core/helpers/child-data.service";
#Component({
selector: 'app-child-dashboard',
templateUrl: './child-dashboard.component.html',
styleUrls: ['./child-dashboard.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChildDashboardComponent implements OnInit {
constructor(private childDataService: ChildDataService) { }
public ngOnInit() {
this.childDataService.changedComponentName({
title: 'Dashboard'
});
}
}
Parent HTML:
<div class="subheader">
<h1>{{ childDetails?.title }}</h1>
<button *ngIf="childDetails?.title == 'Dashboard'">dashboard</button>
<button *ngIf="childDetails?.title == 'SecondChild'">Second Child</button>
</div>
With this setup I am getting the error "ExpressionChangedAfterItHasBeenCheckedError" when i click on a routerLink, now if i click on the same link a second time, the error persists but the correct button becomes visible. Spent all weekend getting nowhere with this, so any help would be greatly appreciated.
public ngOnInit() {
setTimeout(() => {
this.childDataService.changedComponentName({
title: 'Dashboard'
}), {});
}
it should work.
async ngOnInit() {
await this.childDataService.changedComponentName({
title: 'Dashboard'
});
}
I am trying to make import a service inside a component but when I call the Input coming from this service, in my template, it does not render anything.
Here is my entity:
export interface PageState {
step: string;
}
export class SimplePageState implements PageState {
step: string;
}
Here is my service:
import { Injectable } from '#angular/core';
import { PageState } from './state.entity';
#Injectable()
export class PageStateService {
getPageState(): Promise<PageState[]> {
const step = [{ 'step': '1' }];
return Promise.resolve(step);
// return this.http.get('/api/status');
}
}
I am importing and instantiating these in my main component:
import { Component, OnInit } from '#angular/core';
import { Module } from '../modules/core/module.entity';
import { ModuleService } from '../modules/core/module.service';
import { PageState } from './state.entity';
import { PageStateService } from './state.service';
#Component({
selector: 'df-esign',
templateUrl: './esign-page.html',
styleUrls: ['./esign-page.scss'],
providers: [ ModuleService, PageStateService ]
})
export class EsignComponent implements OnInit {
modules: Module[];
pageState: PageState[];
constructor(private moduleService: ModuleService, private pageStateService: PageStateService) { }
getModules() {
this.moduleService.getModules().then(modules => this.modules = modules);
}
getPageState() {
this.pageStateService.getPageState().then(pageState => this.pageState = pageState);
}
ngOnInit() {
this.getModules();
this.getPageState();
}
}
And finally, I am using SimplePageState inside of a particular component, this way:
import { Component, Input } from '#angular/core';
import { SimpleModule } from '../core/module.entity';
import { SimplePageState } from '../../core/state.entity';
#Component({
selector: 'df-module-page',
templateUrl: './module-page.html',
styleUrls: ['./module-page.scss'],
})
export class ModulePageComponent {
#Input() module: SimpleModule;
#Input() pageState: SimplePageState;
}
But trying to do {{pageState}} in my template gives me a blank result with no error.. Anybody can help? I've spent hours looking on the internet and trying to make it work.
Edit:
I am trying to use it inside a bread-crumbs component.
Here is the beginning of my module-view.html, which contains df-module-page as well as df-module-bread-crumbs:
<ng-container [ngSwitch]="module.type">
<template [ngSwitchCase]="'PageModule'"><df-module-page [module]="module" [pageState]="pageState"></df-module-page></template>
<template [ngSwitchCase]="'TextModule'"><df-module-text [module]="module"></df-module-text></template>
<template [ngSwitchCase]="'BreadCrumbModule'"><df-module-bread-crumb [module]="module" [pageState]="pageState" class="{{module.class}}"></df-module-bread-crumb></template>
I am calling SimplePageState in the bread-crumb-component too:
import { Component, Input, HostBinding } from '#angular/core';
import { SimpleModule } from '../core/module.entity';
import { SimplePageState } from '../../core/state.entity';
#Component({
selector: 'df-module-bread-crumb',
templateUrl: './module-bread-crumbs.html',
styleUrls: ['./module-bread-crumbs.scss']
})
export class ModuleBreadCrumbsComponent {
#Input() module: SimpleModule;
#Input() pageState: SimplePageState;
}
And I am trying to do an ngIf inside of module-breads-crumbs.html with a pageState condition which does not have any effect:
<div class="dfbreadcrumbs">
<ol *ngIf="module">
<li *ngFor="let crumb of module.slots.crumbs; let i = index" class="step_{{i + 1}}">{{crumb.text}}</li>
</ol>
</div>
<div *ngIf="pageState">ohohoh</div>
To pass data to an input you would need something like
<df-module-page [pageState]="pageState">
in the template of EsignComponent