I have created a Angular 7 web app data-driven display of navigation items and content to be shown when a nav item is selected.
I have the following nav item interface:
interface NavItem {
title: string;
icon: string;
link: string;
pages?: NavItem[];
}
Where it can be implemented like so for an Angular route:
title: 'Home',
icon: 'faCoffee', // from Material CSS
link: '/home', // Notice it is an Angular route
pages: null
Or like this:
title: 'Company Name',
icon: 'faCompany',
link: 'http://subdomain.company.com', // Notice it is an external URI
pages: null
I have the following in nav.component.html:
<div *ngFor="let navItem of navItems">
<!-- use a simple div for an item that has no children,
match up the styling to the expansion panel styles -->
<div class="nav-head" *ngIf="navItem.pages === null">
<a class="nav-link"
(click)="changeLink(navItem.link)" // (1)
routerLinkActive="selected">
<mat-icon>{{navItem.icon}}</mat-icon>
<span class="nav-link-text">{{navItem.title}}</span>
</a>
</div>
</div>
<app-content-display [hyperlink]="rawLink"></app-content-display> // (2)
In nav.component.ts
export class NavComponent {
rawLink: string = "/";
public navList: NavItem[];
changeLink(link: string){ // (1)
this.rawLink = link;
}
}
app-content-display directive is defined like so:
content-display.component.ts:
@Component({
selector: 'app-content-display', // (2)
templateUrl: './content-display.component.html',
styleUrls: ['./content-display.component.scss']
})
export class ContentDisplayComponent implements OnInit {
private _link = '';
isAngularLink = true;
@Input()
set hyperlink(hyperlink: string){ // When a setter is triggered by (1), the value is checked to determine if it is an Angular route. The output then decides whether to show an angular router-outlet or to show an iframe.
this._link = hyperlink;
this.isAngularLink = this.checkIfAngularLink();
if(this.isAngularLink){
this.router.navigate([`${this._link}`]);
}
}
get hyperlink(): string{
return this._link;
}
trustedLink = () => {
return this.sanitizer.bypassSecurityTrustResourceUrl(this.hyperlink); // Has to be a function as opposed to a property that gets
}
constructor(private sanitizer: DomSanitizer, private router: Router ) { }
ngOnInit() {}
checkIfAngularLink(){
if(this._link.indexOf('company.com') > 0){
return false;
}
return true;
}
}
On content-display.component.html:
<div *ngIf="isAngularLink">
<router-outlet></router-outlet>
</div>
<div *ngIf="!isAngularLink">
<iframe [src]="trustedLink()" width="100%" height="800px"></iframe>
</div>
The end result is:
The parent component (nav.component) shows a child component (content-display.component). When a link is clicked on parent component, the child component's hyperlink property receives the link, checks if it is an Angular route vs external resources.
- If a user clicks on a navigation link that points to an internal Angular page, router-outlet will show the output
- If a user clicks on a navigation link that points to an external URI, an iframe will be called to show the output.
I feel that child component (content-display.component) may know (i.e. not obeying SRP) and do too much and may need refactoring. However, I don't anticipate child component will be reused by other areas of the web app. Any thoughts on the architecture of my navigation components?