Reputation: 13
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
Upvotes: 1
Views: 1887
Reputation: 18839
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.
Upvotes: 1