#How to dynamically change/insert component in another html tag or component

40 messages ยท Page 1 of 1 (latest)

cunning tiger
#

So I want something like

<button [appLoader]="isLoading">Login</button>

to turn into this if isLoading = true

<button [appLoader]="isLoading"><Loader/></button>

I did this before, but I used stuff not supported by Angular, like innerHTML, textContent...And I forgot how I did it.
I do remember using ViewContainerRef , but I forgot how I inserted it in the host.

It should replace the original content, because the default behavior is inserting as sibling.

quasi gulch
#
<button [appLoader]="isLoading">
@if (isLoading) {
  <Loader />
} @else {
  Login
}
</button>

No need for ViewContainerRef or any other fancyness

cunning tiger
#

no ifs

#

I want to reuse it

#

I did it with only Directive, and I lost those files because I formated my pc

#

Simpler example on how the directive looked like


@Directive({
  selector: '[appLoader]',
  standalone: true,
})
export class LoaderDirective {
  constructor() {
    this.init();
  }

  @Input() appLoader: boolean = false;

  init() {
    this.showLoader();
  }

  showLoader() {}

  hideLoader() {}
}

quasi gulch
#

A directive cannot (in a good/supported way) modify the content of its container. This should be a component

cunning tiger
#

Ok, so the only way is if statement?

quasi gulch
#

Well, you can still do it with ViewContainerRef if you insist, but that won't let you remove things. And then you still need an <ng-container> to mark the place where you want to insiert (because ViewContainerRef always inserts as sibling)

cunning tiger
#

But how did i remove things? That what I couldnt figure out, i managed to remove the original content with directive, maybe using ElementRef

quasi gulch
#

I would make it a component and dynamically render <ng-content>

#

Yes you may be able to do it by using ElementRef, but this is getting very "hacky" and not really using Angular's intended APIs

cunning tiger
#

I see, so it possible, it bad/not recommended

quasi gulch
#

It probably falls over with hydration/ssr ๐Ÿ™‚ But I am not 100% on that.

#

I definitely wouldn't do it that way, because that is more like using direct DOM apis instead of angular

cunning tiger
#

OK. Thanks for the help.

#

Found something better, but I'm a bit stuck

#

Instead of a directive, I did selector: 'app-loader, [loader]', (I forgot you can do that).

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

@Component({
  selector: 'app-loader, [loader]',
  standalone: true,
  imports: [],
  templateUrl: './loader.component.html',
  styleUrl: './loader.component.css',
})
export class LoaderComponent {}

Is it possible to make loader active with out app-loader having @Input so like:
````<button (click)="onRegister()" loader>Create account</button>```
loader depends on isLoading but I dont want to use @Input

quasi gulch
#

How else is the value getting to the component then?

cunning tiger
#

it does not need to get to the component, something like isLoading ? loader : '' maybe a better way?

quasi gulch
#

But the component needs to know whether to show the loading spinner or the content.

cunning tiger
#

but it will show it anways if loader is there or am i missing something?

#

Because I'm looking at it and it works, just the condition is missing.

quasi gulch
#

No, the element (<button loader>) is a component now so it will render that component's template. If there is no <ng-content> there the content will disappear.

cunning tiger
#

I want the content to disappear

quasi gulch
#

Then why have it in the first place?

cunning tiger
#

user clicks

 <button (click)="onRegister()" loader>Loader </button>
#

Or is there a better way?

quasi gulch
#

So then you need @if again like I showed to switch between these two.

cunning tiger
#

That what I'm trying to avoid

quasi gulch
#

You can't have a condition without @if ๐Ÿ˜„

cunning tiger
#

I don't like too many ifs, the more buttons the more ifs

#

What about going back to @Input? how to prevent component from showing if false?

#

the component

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

@Component({
  selector: 'app-loader, [loader]',
  standalone: true,
  imports: [],
  templateUrl: './loader.component.html',
  styleUrl: './loader.component.css',
})
export class LoaderComponent {}
quasi gulch
#

In the component template:

@if (isLoading) {
  <Loader />
} @else {
  <ng-content />
}

And then

<button loader [isLoading]="isLoading">Submit</button>
#

You can be a bit sneaky and have isLoading be the selector

#

Then it can just be <button [isLoading]="isLoading">

cunning tiger
#

It can't get any better.

#

Thanks a lot.