I have a component (componentA) that is using another component (componentB) in its HTML.
ComponentB has a button where the title is something else, but I would like to change the name of the button in ComponentA.
ComponentA uses componentB button to navigate to another page, but ComponentB has button that opens up a form. The navigation works and everything, but I would just like to change the name of the buttons when its on different page.
ComponentA.html
<div>
<component-b (click)="buttonEdit()"></component-b>
</div>
ComponentA.ts
public buttonEdit(): void {
this.router.navigate(['/users']);
}
ComponentB.html
<button (click)="openModal()">Add Users</button>
ComponentB.ts
#Input() buttonEdit: () => void;
You can try this
Component B.tmpl:
<button (click)="openModal()">{{buttonName}}</button>
Component B.ts:
#Input() buttonEdit: () => void;
#Input() buttonName: string = 'Add Users';
Component A.ts:
public buttonEdit(): void {
this.router.navigate(['/users']);
}
public buttonName(): string {
...
return buttonName;
}
component A.tmpl:
<div>
<component-b (click)="buttonEdit()" [buttonName]="buttonName()"></component-b>
</div>
Try this
Component A.tmpl:
<div>
<component-b (click)="buttonEdit()" [buttonName]="buttonName()" [backgroundColor]="backgroundColor()"></component-b>
</div>
Component A.ts:
public backgroundColor(): string {
...
return backgroundColor;
}
Component B.tmpl:
<div [ngStyle]="{'background-color': backgroundColor}"></<div>
Component B.ts:
#Input() backgroundColor: string = 'green';
Related
I have two .ts files (editor.ts and editor_settings.ts), Corresponding to editor.ts i have creater editor.html file. Now what i am trying to call function inside editor_settings.ts on button click in editor.html.
editor.ts
import { EditorSetting } from '../app/editorSetting.component';
export class PadComponent implements OnInit, OnDestroy { ---- }
constructor(
private component: EditorSetting
) { }
submit() {
let userCode = this.component.editor.getValue();
console.log('Inside pad.componet.ts');
console.log(userCode);
}
editor.html
<button id="submit" type="button" class="btn btn-sm btn-run" (click)="submit()" [disabled]="loading"
style="background: #FF473A">
<i class="fa fa-play" aria-hidden="true"></i>
<span *ngIf="loading">Running</span>
<span else> Run </span>
</button>
Now, on button click in editor.html, i want to call function which is inside editor_settings.ts.
editor_settings.ts
export class EditorComponent implements OnInit, OnDestroy, OnChanges {--}
I am facing the following error:
inline template:0:0 caused by: No provider for EditorComponent!
To communicate two components that are not related to each other, you can use a service.
#Injectable({
providedIn: 'root',
})
export class YourService {
private yourVariable: Subject<any> = new Subject<any>();
public listenYourVariable() {
return this.yourVariable.asObservable();
}
public yourVariableObserver(value ?: type) {
this.yourVariable.next(value);
}
You import in yours components where you want use it this service.
import{ YourService } from ...
In Edit component :
submit(){
this.yourService.yourVariableObserver();
}
while in Editor_setting.ts
ngOnInit() {
this.sub=this.yourService.listenYourVariable().subscribe(
variable => {
this.callyourFunction();
}
)
}
Don't forget to unsubscribe to prevent memory leak
ngOnDestroy() {
this.sub.unsubscribe()
}
Another aproach valid if your editor is inside the editorSetting
<editor-setting>
<editor></editor>
</editor-setting>
Use Host in constructor
constructor(#Optional #Host() private component: EditorSetting) { }
Goodday, This is probably a nooby question but I can't get it to work.
I have a simple service which toggles an boolean, if the boolean is true the class active should appear on my div and if false no class.. Simple as that. But the boolean gets updated, but my view doesn't react to it. Do I somehow have to notify my view that something has changed ?
Service:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ClassToggleService {
public menuActive = false;
toggleMenu() {
this.menuActive = !this.menuActive;
}
}
View (left menu component):
<div id="mainContainerRightTop" [class.active]="classToggleService.menuActive == true">
Toggle point (top menu component):
<a id="hamburgerIcon" (click)="classToggleService.toggleMenu()">
This because you are changing a value on a service not on the component, so Angular don't need to update the component, because it did not change. If you want to update the view of your component when a service element is modified, you have to use an Observables and Subjects, and subscribe to them. In this way when the element is changed, it automatically notify all the subscribed components.
#Injectable({
providedIn: 'root'
})
export class ClassToggleService {
public menuSubject: Subject<boolean> = new BehaviorSubject<boolean>(false);
public menuActive = this.menuSubject.asObservable();
toggleMenu(val : boolean) {
this.menuSubject.next(val);
}
}
And in your component just implement OnInit interface and subcribe to the observable in the your service:
public localBool = false;
ngOnInit() {
this._myService.menuActive.subscribe(value => this.localBool = value);
}
ComponentToggleMenu() {
this._myService.toggleMenu(!this.localBool);
}
Then your html:
<div id="mainContainerRightTop" [class.active]="localBool">
<a id="hamburgerIcon" (click)="ComponentToggleMenu()">
Why we need service, this should be integrated with component class. As a general rule, you are not supposed to call service method in template file.
export class TestComponent implements OnInit{
public menuActive = false;
toggleMenu() {
this.menuActive = !this.menuActive;
}
}
Template:
<div id="mainContainerRightTop" [class.active]="menuActive">
In Angular i have some template defined as a constant, The template renders fine and all, but I want to attach an event to an element from the html constant. It doesn't seem like i can bind the function as I would normally do when the template is defined in the #Component definition.
Here is a stripped piece of code describing what I have tried.
export class SomeComponent implements OnInit {
public ngOnInit(): void {
const someHtml = `
<a (click)="someFunction()" href="someUrl">link</a>
`
.. some code that uses the const
}
public someFunction() {
console.log("clicked")
}
}
How would I go about this?
I have 2 components: parent and child.
I want to send an array from parent to child when I click on a button(from parent-component) and call a function generate() for every object in array(in child-component).
I have tried something with #Output() EventEmitter but I am not sure of this approach.
parent component
export class ViewCalendarsComponent implements OnInit {
#ViewChildren(MonthHeaderComponent) months: any[];
#Output() monthsList: EventEmitter<Date[]> = new EventEmitter();
selectedMonths: any[];
test() {
this.monthsList.emit(this.selectedMonths);
}
}
child component
export class MonthHeaderComponent implements OnInit {
ngOnInit() {
}
generate(date: Date) {
// code here....
}
show() {
for (let i = 0; i <= monthsList.length; i++)
{
generate(monthsList[i]);
}
}
}
and in parent html
<button class="primary" (click)="test()"> Show </button>
<div class="right view-calendar">
<child *ngFor="let selectedMonth of selectedMonths" [monthsList]="show($event)"></child>
</div>
How can I send this array and use it as parameter in a method?
Just use the #input instead of output. And when the input value get modifies, use the ngOnChanges to catch the changes.
child component
export class MonthHeaderComponent implements OnInit {
#Input monthsList: Date[]
ngOnChanges(){
// catch changes
}
}
In the parent template, pass the array.
<child *ngFor="let selectedMonth of selectedMonths" [monthsList]="monthArr"></child>
We have a standard modal within our application.
<ng-template [ngIf]="true" #editDataModal>
<div class="modal-header">
<h5 class="modal-title">Edit Modal</h5>
<button type="button" class="close" aria-label="Close" (click)="onCancelClicked()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" #editDataModalBody>CUSTOM COMPONENT GOES HERE</div>
</ng-template>
We want to be able to pass in a custom component as our body. Is there a way in ngx bootstrap to do this?
It appears that the modal appears outside the main content so we can't find it using ViewChild.
We are calling it using the modal service. Like so:-
constructor(
private modalService: BsModalService
) { }
ngOnInit() {
this.modalConfig = {
ignoreBackdropClick: true
};
this.ModalRef = this.modalService.show(this.editModal, this.modalConfig);
}
The modal component could obtain and publish the ViewContainerRef. For example, it could use a BehaviorSubject. The parent component can create the custom component and add it to the modal when the viewContainerRef is published. I only did it this way instead of just a getter because ViewChild is not valid until afterViewInit so you need a way to handle that.
// EditModalComponent
export class EditModalComponent implements AfterViewInit {
#ViewChild("editDataModalBody", {read: ViewContainerRef}) vc: ViewContainerRef;
public bodyRefSubject: BehaviorSubject<ViewContainerRef> = new BehaviorSubject<ViewContainerRef>(null);
constructor(
public bsModalRef: BsModalRef,
public vcRef: ViewContainerRef
) {}
ngAfterViewInit() {
this.bodyRefSubject.next(this.vc);
}
onCancelClicked() {
console.log('cancel clicked');
this.bsModalRef.hide()
}
}
And in the parent component:
// Parent Component
export class AppComponent {
bsModalRef: BsModalRef;
bodyContainerRef: ViewContainerRef;
customComponentFactory: ComponentFactory<CustomComponent>;
modalConfig: ModalOptions;
constructor(
private modalService: BsModalService,
private resolver: ComponentFactoryResolver
) {
this.customComponentFactory = resolver.resolveComponentFactory(CustomComponent);
}
openModalWithComponent() {
this.modalConfig = {
ignoreBackdropClick: true,
}
this.bsModalRef = this.modalService.show(EditModalComponent, this.modalConfig);
this.bsModalRef.content.bodyRefSubject.subscribe((ref) => {
this.bodyContainerRef = ref;
if (this.bodyContainerRef) {
this.bodyContainerRef.createComponent(this.customComponentFactory);
}
})
}
}
Another way without using ViewChild is to place a directive on the div instead of #editDataModalBody and that directive can inject the ViewContainerRef and publish it using a service or similar.