Is it possible in Angular to dynamically bind a click event from a component to an existing component on the page. To illustrate, I include this screenshot.
The app consists of 4 simple components now; One shell component that defines the global layout, in the shell component I have a header component (blue outline) and a navigation component (red outline). The green component is the component loaded by the current route.
I'm updating the navigation component using a simple service, like this:
@Injectable({ providedIn: 'root' })
export class NavigationService {
private titleSource = new Subject<string>();
private actionsSource = new Subject<ActionButton[]>();
private menuItemsSource = new Subject<MenuItem[]>();
title = this.titleSource.asObservable();
actions = this.actionsSource.asObservable();
menuItems = this.menuItemsSource.asObservable();
constructor() {
}
setTitle(title: string) {
this.titleSource.next(title);
}
setActions(actions: ActionButton[]) {
this.actionsSource.next(actions);
}
setMenuItems(menuItems: MenuItem[]) {
this.menuItemsSource.next(menuItems);
}
}
The navgiation component simply uses this service like this:
@Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html'
})
export class NavigationComponent implements OnInit {
title: string = "";
actions: ActionButton[] = [];
menuItems: MenuItem[] = [];
constructor(private navigation: NavigationService) {
navigation.title.subscribe((title) => this.title = title);
navigation.actions.subscribe((actions) => this.actions = actions);
navigation.menuItems.subscribe((menuItems) => this.menuItems = menuItems);
}
}
And displays it like this:
<div class="navigation">
<div *ngIf="title.length" class="navigation__title">
<h1>{{title}}</h1>
</div>
<div class="navigation__menu">
<ul>
<li *ngFor="let menuItem of menuItems" [routerLinkActive]="['active']">
<a routerLink="{{menuItem.path}}">{{menuItem.title}}</a>
</li>
</ul>
</div>
<div class="navigation__actions">
<a *ngFor="let actionButton of actions" class="btn btn--primary">{{actionButton.title}}</a>
</div>
</div>
Then in the currently activated component (the green one), I set the title and items like this
....
constructor(private employeeService: EmployeeService, private navigation: NavigationService, private drawerService: NzDrawerService) {
navigation.setTitle('');
navigation.setActions([
{
path: '',
title: 'Add Employee'
}
]);
navigation.setMenuItems([
{
path: '/employees',
title: 'Employees'
},
{
path: '/teams',
title: 'Teams'
}
]);
}
....
In this same active component I also have this method defined:
createEmployee() {
const drawer = this.drawerService.create<CreateEmployeeComponent>({
nzContent: CreateEmployeeComponent,
nzClosable: false,
nzWidth: '33%',
});
drawer.afterClose.subscribe(data => {
this.loadEmployees();
});
}
Now my question is, if it's somehow possible to call this method when I click the button rendered on my navigation component. And also set it dynamically from the currently activated component like I'm already doing for the title and navigation items
You can use Object orientation
for this
Create a base-class
export class EmployeeCreateClass {
createEmployee() {
const drawer = this.drawerService.create<CreateEmployeeComponent>({
nzContent: CreateEmployeeComponent,
nzClosable: false,
nzWidth: '33%',
});
drawer.afterClose.subscribe(data => {
this.loadEmployees();
});
}
}
Make Navigation
extend it:
export class NavigationComponent extends EmployeeCreateClass implements OnInit {
...
The navigation HTML:
<button (click)="createEmployee()"></button>
Same thing with the components:
export class CurrentComponent extends EmployeeCreateClass implements OnInit {
....
The HTML:
<button (click)="createEmployee()"></button>
======================================================== Sorry I misunderstood the question, you can probably refine it further but maybe something like this:
@Injectable({ providedIn: 'root' })
export class NavigationService {
private titleSource = new Subject<string>();
private actionsSource = new Subject<ActionButton[]>();
private menuItemsSource = new Subject<MenuItem[]>();
private actionItems = [];
title = this.titleSource.asObservable();
actions = this.actionsSource.asObservable();
menuItems = this.menuItemsSource.asObservable();
constructor() {
}
setTitle(title: string) {
this.titleSource.next(title);
}
setActions(actions: ActionButton[]) {
this.actionItems = [];
for(let i = 0; i < actions.length; i++) {
this.actionItems.push(new Subject<void>());
}
this.actionsSource.next(actions);
}
setMenuItems(menuItems: MenuItem[]) {
this.menuItemsSource.next(menuItems);
}
}
Then in the HTML of navigation:
<a *ngFor="let actionButton of actions; let i = index" class="btn btn--primary" (click)="actionButtonClicked(i)">{{actionButton.title}}</a>
Then in the TS of the navigation:
actionButtonClicked(i: number) {
this.navigation.actionItems[i].next();
}
Then in your component:
navigation.setActions([
{
path: '',
title: 'Add Employee'
}
]);
// the bottom should get triggered every time actionButton gets `next`ed.
navigation.actionItems[0].subscribe(_ => { this.createEmployees(); });
Keep in mind this is me going at it at one go without an IDE but hopefully it helps you.