Exporting an Angular 2 component to use in a template

I am interested in creating a custom Tabs component that has the ability to display content from [root] within itself. It functions perfectly when using selectors in html tags (<tab1>), but there are instances where the selector is unknown. Since Tabs act as components, I need a method to render the component onto the view.

In my Tab.ts file, the goal is to show the root component inside the viewport

@Component({
    selector: 'tab',
    template: 
     `<div [hidden]="!active">
          <ng-content></ng-content>
     </div>`
})
export class Tab {
    @Input('tabTitle') title: string;
    @Input() active = false;
    @Input('root') root: any;
}

Next, we have the Tabs.ts which connects tabs together (allowing for easy switching between them)

@Component({
    selector: 'tabs',
    template:
      `<div class="tabbar">
        <div class="tab-button" *ngFor="let tab of tabs"  (click)="selectTab(tab)">
          <a href="#">{{tab.title}}</a>
        </div>
      </div>

      <ng-content></ng-content>`
})

export class Tabs implements AfterContentInit {
  @ContentChildren(Tab) tabs: QueryList<Tab>;

  ngAfterContentInit() {
    let activeTabs = this.tabs.filter((tab) => tab.active);
    if (activeTabs.length === 0) {
      this.selectTab(this.tabs.first);
    }
  }

  selectTab(tab: Tab) {
    this.tabs.forEach(tab => tab.active = false);
    tab.active = true;
    if (this.menu) {
      this.menu.active = false;
    }
  }
}

Here is a template that allows for the use of tabs and works consistently

<tabs>
  <tab tabTitle="Tab 1">
    <tab-home></tab-home>
  </tab>
  <tab tabTitle="Tab 2">
    <tab-second></tab-second>
  </tab>
  <tab tabTitle="Tab 3">
    Tab 3 content
  </tab>
</tabs>

My ultimate goal is to achieve this structure:

<tabs>
  <tab *ngFor="let tab of tabs" [title]="tab.title">      
    {{ tab.component }}
  </tab>
</tabs>

Rather than using tab.component, I aim to display the rendered view of the component without specifying a directive. This way, I can inject the rendered view from the component into the template regardless of what the directive may be.

Answer №1

Instead of using a directive with @ContentChildren, consider assigning a local variable.

<another-component #item></another-component>

After that, simply pass the name of the variable as a string to ContentChildren:

@ContentChildren('item') 

Answer №2

After exploring various solutions, I ultimately settled on a slightly tweaked approach shared by Günter Zöchbauer in response to this query: Angular 2 dynamic tabs with user-click chosen components

To incorporate an HTML tag into TypeScript, I leveraged wapsee's solution and implemented @ViewChild within the component template.

The key modification was made to the Tab.ts file, which now appears as follows:

@Component
  selector: `tab`,
  template: 
    `<div [hidden]="!active">
      <div #viewport></div>
      <div class="nav-decor"></div>
    </div>`
})
export class Tab {
  @Input('tabTitle') title: string;
  @Input() active = false;
  @Input('tabIcon') icon: string;
  @Input() root: Type<Component> = DefaultTabComponent;

  componentRef: ComponentRef<Component>;
  @ViewChild('viewport', {read: ViewContainerRef}) target: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  ngAfterViewInit() {
    let factory = this.componentFactoryResolver.resolveComponentFactory(this.root);
    this.componentRef = this.target.createComponent(factory);
  }
}

With these adjustments, I can now effortlessly inject a list of objects as tabs using *ngFor

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What causes the ngIf directive to update the view of an HTTP observable only upon reloading the page?

Presently, I am utilizing a template: <div *ngIf="(currentUser | async)?.can('book')">Book Now</div> accompanied by its component: readonly currentUser: Observable<CurrentUser>; constructor(private userService: UserSe ...

Having trouble setting up mongodb-memory-server 8 to work with jest

I am currently working on integrating the latest version of mongodb-memory-server with jest on a node express server. While following the guide provided in the mongodb-memory-server documentation (), I encountered some gaps that I am struggling to fill in. ...

Angular 2 - Implementing click event functionality to add an item

I've searched for a solution to this before, but the best answer I could find was from 9 months ago: here and it doesn't work with the latest Angular version. Is there a way to add a new item (<li> or any other) to my HTML with a simple cl ...

Error: Astra connection details for Datastax could not be located

Currently, I am attempting to establish a connection to DataStax Astra-db using the cassandra-client node module. Below is an example of my code: const client = new cassandra.Client({ cloud: { secureConnectBundle: 'path/to/secure-connect-DATABASE_NA ...

Incorporate the Angular router within markdown hyperlinks

When utilizing ngx-markdown to display my FAQ, I include links to both external resources (beginning with http) and internal content (starting with /). I am interested in passing the Angular router to my markedOptionsFactory so that I can easily navigate ...

The Angular router is causing an issue where when navigating back, my component does not reset to 0 as expected, resulting in

I'm currently working on an ionic-angular app and implementing a Register feature where users input their information step by step. The issue I'm facing is with the backward navigation functionality - when users go back using the arrow button, th ...

Is there a way to tally up the overall count of digits in a number using TypeScript?

Creating a number variable named temp in TypeScript: temp: number = 0.123; Is there a way to determine the total count of digits in a number (in this case, it's 3)? ...

Issue with IntelliJ: TypeScript Reference Paths Are Not Relative

I am currently using IntelliJ as my IDE, but I am facing an issue with configuring gulp-typescript to compile my typescript code. The problem arises from the fact that IntelliJ does not treat my reference paths relatively, instead it references them from m ...

What is the best way to sort a union based on the existence or non-existence of a specific

My API response comes in the form of a IResponse, which can have different variations based on a URL search parameter. Here is how I plan to utilize it: const data1 = await request<E<"aaa">>('/api/data/1?type=aaa'); const d ...

Use JavaScript's Array.filter method to efficiently filter out duplicates without causing any UI slowdown

In a unique case I'm dealing with, certain validation logic needs to occur in the UI for specific business reasons[...]. The array could potentially contain anywhere from several tens to hundreds of thousands of items (1-400K). This frontend operation ...

Retrieving an array of objects from a JSON file using Angular 2

I am encountering an issue where the class is not filled properly in an object obtained from a JSON array, resulting in an 'undefined' error. Here is the code snippet for retrieving the object: getFeatures() { return this.http.get('h ...

What is the best way to implement a generic parameter with constraints in an abstract method?

Take a look at this scenario: interface BaseArgs { service: string } abstract class BaseClass { constructor(name: string, args: BaseArgs) { this.setFields(args) } abstract setFields<T extends BaseArgs>(args: T): void } interface ChildA ...

What could be causing a compile error in my React and TypeScript application?

I recently downloaded an app (which works in the sandbox) from this link: https://codesandbox.io/s/wbkd-react-flow-forked-78hxw4 However, when I try to run it locally using: npm install followed by: npm start I encounter the following error message: T ...

Explaining the union type using a list of data types

Is there a way to create a union type that strictly limits values to 'a', 'b', 'c' when using a list like const list: string[] = ['a', 'b', 'c']? I know one method is: const list = ['a' ...

Is there a sorting data accessor available in swimlane ngx-datatable similar to the one found in the angular material table?

I am currently working on implementing ngx-datatable in my code. The row data consists of key value pairs, with the value being of type object. Unfortunately, the default sorting is not functioning as expected. In Angular Mat-Table, there is a property cal ...

Issues arise in Angular 4 when the "Subscribe" function is repeatedly invoked within a for/switch loop

My array of strings always changes, for example: ["consumables", "spells", "spells", "consumables", "spells", "consumables", "spells", "characters", "characters", "consumables"] I iterate through this array and based on the index, I execute different .su ...

Unable execute the Select All command in sqlite

I've been working on several queries, but unfortunately I've encountered an issue with this one that is not providing a helpful error code. The table I'm working with is 'Activity' and I simply want to select all records from it. ...

Exploring the TypeScript compiler API to read and make updates to objects is an interesting

I'm delving into the world of the typescript compiler API and it seems like there's something I am overlooking. I am trying to find a way to update a specific object in a .ts file using the compiler API. Current file - some-constant.ts export co ...

The mkdir function has encountered an invalid argument error with code EINVAL

I am encountering an issue with my Angular app build command. I have npm version 6.14 and node version 14.15.4 installed on my Windows 7 32-bit system. The error message I receive when running 'npm run build' is: 95% emitting index-html-webpack-p ...

Encountering errors while setting up routes with Browser Router

When setting up a BrowserRouter in index.tsx, the following code is used: import './index.css'; import {Route, Router} from '@mui/icons-material'; import {createTheme, ThemeProvider} from '@mui/material'; import App from &ap ...