Exploring the power of dynamic templates in Angular 4

After exploring numerous blog posts and discussions, I have yet to discover a solution for my template dilemma. The issue revolves around our straightforward icon component, where users can specify the desired icon and size directly in the template:

import {Component, Input} from '@angular/core';

@Component({
   selector: 'comp-icon',
   template: `
        <span class="{{ class }}" *ngIf="icon === 'error'" >
          <svg [attr.height]="size" viewBox="0 0 48 48" [attr.width]="size" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
        </span>
        <span class="{{ class }}" *ngIf="icon === 'success'" >
          <svg [attr.height]="size" viewBox="0 0 48 48" [attr.width]="size" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>
        </span>
    `
})
export class IconComponent {
   @Input() icon: string;
   @Input() size: number;
   @Input() class: string;

   constructor() {
   }
}

The challenge lies in the repetitive nature of this setup and the need to incorporate custom SVG images from various applications using the library that employs this component. My current approach aims to convey the intended functionality like so:

import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {Icons} from './icons';

@Component({
   selector: 'comp-icon',
   template: `
        <span class="{{ class }}">
            <ng-container [ngTemplateOutlet]="iconContent"></ng-container>
        </span>
    `
})
export class IconComponent implements OnInit {
   @Input() icon: string;
   @Input() size: number;
   @Input() class: string;

   @ViewChild('iconContent') iconContent: any;

   constructor() {
   }

   ngOnInit() {
       this.iconContent = (Icons.find(icon => icon.name === this.icon) || { name: '', content: '' }).content;
   }
}

Furthermore, the icons.ts file outlines the structure:

export interface IIcon {
    name: string;
    content: string;
}

export const Icons: IIcon[] = [
    {
        name: 'error',
        content: `<svg [attr.height]="{SIZE}" [attr.width]="{SIZE}" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
        </svg>`
    },
    {
        name: 'success',
        content: `<svg [attr.height]="{SIZE}" [attr.width]="{SIZE}" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
        </svg>`
    },
];

Is it feasible to dynamically alter the contents of a component section, particularly an SVG element, using this method? While I've heard about directives that could potentially help, I haven't quite grasped how to implement them effectively.

Answer №1

If you're looking to bind properties, consider using innerHTML or creating a component based on Günter Zöchbauer's recommendation.

<div [innerHTML]="iconContent">
</div>

Another approach could involve defining icons in an array like this:

export const Icons: IIcon[] = [
    {
        name: 'error',
        width: '50px',
        height: '50px',
        viewBox: '0 0 48 48',
        xmlns: "http://www.w3.org/2000/svg"
    }
];

In your component's HTML, you can then do the following:

<svg [attr.width]="iconContent.width" [attr.height]="iconContent.height" [attr.viewBox]="iconContent.viewBox" [attr.xmlns]="xmlns"></svg>

Answer №2

To achieve this task correctly, I realized that utilizing ngComponentOutlet was the right approach from the start. Following guidance from this thread, I successfully implemented it in a similar fashion:

import { Compiler, Component, ElementRef, Input, NgModule, NgModuleFactory, OnInit, ViewChild } from '@angular/core';
import { IconsProvider } from './icons.provider';

@Component({
   selector: 'comp-icon',
   template: `
        <ng-container *ngComponentOutlet="dynamicIconComponent;
                        ngModuleFactory: dynamicIconModule;"></ng-container>
    `
})
export class IconComponent implements OnInit {
   @Input() icon: string;
   @Input() size: number;
   @Input() class: string;

   public dynamicIconComponent: any;
   public dynamicIconModule: NgModuleFactory<any>;

   constructor (private _iconsProvider: IconsProvider, private _compiler: Compiler) {
   }

   ngOnInit() {
      const selectedIcon: string = this._iconsProvider.getIcon(this.icon);

      if (selectedIcon != null) {
          this.dynamicIconComponent = this.createNewIconComponent(selectedIcon, this.class, this.size);
          this.dynamicIconModule = this._compiler.compileModuleSync(this.createIconComponentModule(this.dynamicIconComponent));
      }
   }

   private createIconComponentModule(iconComponentType: any) {
       @NgModule({
           imports: [],
           declarations: [
               iconComponentType
           ],
           entryComponents: [iconComponentType]
       })
       class RuntimeIconComponentModule {}

       return RuntimeIconComponentModule;
   }

   private createNewIconComponent(iconContents: string, iconClass: string, iconSize: number) {
       @Component({
           selector: 'dynamic-icon-component',
           template: `<span class="{{ iconClass }}">${iconContents}</span>`
       })
       class DynamicIconComponent implements OnInit {
           private iconContents: string;
           private iconClass: string;
           private size: number;

           ngOnInit() {
               this.iconContents = iconContents;
               this.iconClass = iconClass;
               this.size = iconSize;
           }
       }

       return DynamicIconComponent;
   }
}

I am grateful for the valuable input and suggestions that eventually led me to discover the solution. Thanks.

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 is the reason for an optional object property being assigned the 'never' type?

I'm having trouble understanding the code snippet below: interface Example { first?: number, second?: { num: number } } const example: Example = { first: 1, second: { num: 2 } } function retrieveValue<Object, Key exte ...

Encountering a type error when attempting to assign an interface to a property

In my Angular project, I am working with typescript and trying to assign the IInfoPage interface to some data. export interface IInfoPage { href: string; icon: string; routing: boolean; order: number; styleType: string; ...

Tips for retrieving an object from an array with Angular and Firestore

Currently, I am attempting to retrieve an object from Firestore using the uid so that I can return a specific object as a value. I have implemented a function in order to obtain the object 'Banana'. getFruit(fruitUid: string, basketUid: string) ...

How can Angular CLI prevent the submission of an HTML form unless a previous option has been selected

I am currently working on a form that contains select fields with various options pre-populated. <form [formGroup]="selectVehicleForm"> <select formControlName="Manufacturer"> <option *ngFor='let ...

The absence of @angular/forms/FormsModule is causing issues

I am facing an issue with importing the FormsModule from @angular/[email protected] while working with Angular 2 RC5. It seems like a simple task, but I am encountering difficulties. import { FormsModule } from '@angular/forms'; Surprising ...

Is there a method in Angular to restrict change detection to only the current component and its descendant components?

Is there a way to trigger an event in the child component without traversing the entire component tree from the parent? import { Component } from '@angular/core' @Component({ selector: 'my-app', template: '<b>{{ te ...

Developing a Customized Filtering Mechanism in Angular 8

I have some experience working in web development, but I am relatively new to Angular. My current project involves creating a simple filter for a table's column based on user input. However, I'm facing an issue where typing in a single letter fil ...

Dynamic content within an Angular Swiper

Greetings, Experts! I am a newcomer to angular and have successfully created 3 components: Swiper YouTube frame Map display Currently, I am facing a challenge where I need to utilize the swiper component multiple times on the homepage. The first instanc ...

Error: Unable to access the property 'fn' of an undefined object in electron version 2 using Angular 6

I am currently utilizing Angular 6.0.3 and electronjs 2.0.2 with the package.json configuration shown below: { "name": "test", "version": "1.0.0", "license": "MIT", "main": "electron-main.js", "author": { "name": "Moh ...

(NG2-CHARTS) Unable to connect to the Chart Type as it is not recognized as a valid property for binding

I encountered an issue with my Chart Component where I am seeing the error message below. I have successfully imported ChartsModule into my app.module.ts file, but I am unsure why this error is occurring? Can't bind to 'ChartType' since ...

Incorrect date generated by Moment.js from Unix timestamp

Is there a way to store unixtime as a Moment.moment state? Using moment(timestamp) seems to provide a different date. const [date, setDate] = useState<moment.Moment | null>(null); const timestamp = Math.floor(date.getTime() / 1000); setDate(m ...

Using the double question mark operator in React JS and Typescript

The .NET framework includes the Null coalescing operator, which can be utilized in the following manner: string postal_code = address?.postal_code; Is it possible to achieve the same functionality in React JS? It seems that we can accomplish this using ...

Establishing a default value for a select using reactive forms

I need assistance with a function that looks like this: cancel(): void { this.form.reset(); this.router.navigate([], { queryParams: { activeOnly: false } }); } Here is how it appears in the HTML: <select class="form-control-sm" id="select-process ...

How can I use JavaScript to update the content inside HTML tags with a specific string?

I have a situation where I need to replace a string in two different ways Input: Parameters-->string, AnnotationName, input Case 1: And I should input <i>Annotaion</i> as <b>input</b> Output : { displayData: `And I should inp ...

What is the best way to create a universal function that can return a promise while also passing along event

I created a specialized function that waits for an "EventEmitter" (although it's not completely accurate as I want to use it on other classes that have once but don't inherit from EventEmitter): export function waitEvent(emitter: { once: Function ...

Steering templateUrl Value Modification on the Go in Angular 2

Looking for a way to dynamically change the template URL at runtime in order to switch between different views rendered in my component. Is there a solution available for this? For example, I want my component to have both grid and list view options, bu ...

Why are polyfills-es2015, vendor-es2015, main-es2015, and other files blocked from loading because the MIME type is not allowed (text/html

Hey there! I've encountered an issue during the deployment of my Angular app. When I serve it from Angular locally, everything works perfectly fine. However, when I deploy it on a Tomcat server, I encounter errors and I can't see anything related ...

Icon positioned to the left within the text box

Hey there! I'm new to NativeScript and I've been struggling to place an icon inside a textbox. Can someone please help me out? Expected Output: https://i.stack.imgur.com/xvoZG.png Code <GridLayout columns="*, *" rows=& ...

Utilize the power of PIXI.js to effortlessly convert your canvas into a high-quality image without encountering

I'm currently working on exporting the entire canvas as a PNG using PIXI.js within a React app that incorporates react-pixi. My version is 6.5 and I've been trying out the following code: // MyComponent.tsx <button onClick={exportImage} /> ...

What is the process for importing a .webp file into a TypeScript file to be utilized in an Angular project down the line?

I created a Weapon class with at least one icon. export default abstract class Weapon { icons: string[]; // Possibly incorrect type constructor(iconOrIcons: string | string[]) { this.icons = typeof iconOrIcons === 'string' ? [iconOrIcons ...