I'm learning Angular.
I want to create custom directive, but doesn't work ! (it's not app for production, it's a discover app ^^)
I have no error, the project build ! But the directive don't work !
To create the directive, I use : ng generate directive directive-name
Can you help me to resolve my problem ?
The code :
Component - app.component.ts
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { POKEMONS } from './mock-pokemon-list';
import { Pokemon } from './pokemon';
import { AppModule } from './app.module';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterOutlet,
AppModule
],
templateUrl: 'app.component.html',
styles: [],
})
export class AppComponent implements OnInit {
pokemonList: Pokemon[] = POKEMONS;
pokemonSelected: Pokemon|undefined;
ngOnInit(): void {
console.log(this.pokemonList);
}
selectPokemon(pokemonId: string) {
const id: number = +pokemonId;
const pokemon: Pokemon|undefined = this.pokemonList.find(pokemon => pokemon.id == id);
if(pokemon) {
console.log(`Vous avez cliqué sur le pokémon ${pokemon.name}`);
this.pokemonSelected = pokemon;
} else {
console.log(`Vous avez demandé un pokémon qui n'existe pas.`);
this.pokemonSelected = pokemon;
}
}
}
Template - app.component.html
<h1 class="center" >Liste de pokémons</h1>
<input
#input
(keyup.enter)="selectPokemon(input.value)"
type="number"
/>
@if(pokemonSelected) {
<p>Vous avez sélectionné le pokémon : {{ pokemonSelected.name }}</p>
}
@else {
<p>Aucune correspondance.</p>
}
<div class="container" >
<div class="row" >
@for (pokemon of pokemonList; track pokemon) {
<div class="col s12 m4 s6">
<div class="card horizontal" appBorderCard>
<div class="card-image">
<img [src]="pokemon.picture">
</div>
<div class="card-stacked">
<div class="card-content">
<p class="header">{{ pokemon.name }}</p>
<p><small>{{ pokemon.created }}</small></p>
</div>
</div>
</div>
</div>
} @empty {
<p>Aucun élément dans la base.</p>
}
</div>
</div>
<router-outlet />
The directive - border-card.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appBorderCard]'
})
export class BorderCardDirective {
constructor(private el: ElementRef) {
this.setHeight(180);
this.setBorder("#f5f5f5");
}
@HostListener('mouseenter') onMouseEnter() {
this.setBorder("#009688");
}
@HostListener('mouseleave') onMouseLeave() {
this.setBorder("#f5f5f5");
}
private setHeight(height: number) {
this.el.nativeElement.style.height = `${height}px`;
}
private setBorder(color: string) {
this.el.nativeElement.style.border = `solid 4px ${color}`;
}
}
Module - app.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BorderCardDirective } from './border-card.directive';
@NgModule({
declarations: [],
imports: [
CommonModule,
BorderCardDirective
]
})
export class AppModule { }
The stack :
Angular CLI: 17.3.2
Node: 21.6.2 (Unsupported)
Package Manager: npm 10.5.0
Angular:
...
Package Version
------------------------------------------------------
@angular-devkit/architect 0.1703.2 (cli-only)
@angular-devkit/core 17.3.2 (cli-only)
@angular-devkit/schematics 17.3.2 (cli-only)
@schematics/angular 17.3.2 (cli-only)
Thanks for advance
standalone: true
directive
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appBorderCard]',
standalone: true,
})
export class BorderCardDirective {
...
app.component
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { POKEMONS } from './mock-pokemon-list';
import { Pokemon } from './pokemon';
import { AppModule } from './app.module';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterOutlet,
AppModule,
BorderCardDirective,
],
templateUrl: 'app.component.html',
styles: [],
})
export class AppComponent implements OnInit {
...
Ensure that the directive is not set to standalone: true
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({ selector: '[appBorderCard]', }) export class BorderCardDirective { ...
Add the directive to the declarations and exports array of the app.module.ts, only if we export the directive, it will be visible to app.component.ts
app.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BorderCardDirective } from './border-card.directive';
@NgModule({
declarations: [
BorderCardDirective,
],
imports: [
CommonModule,
],
exports: [
BorderCardDirective,
],
})
export class AppModule { }
If you are using standalone app component, please ensure you have imported the directive on the app.components imports array! Since we are adding to the imports array, we need to set standalone: true
on the @Directive({selector: 'some selector', standalone: true })
object of the directive file:
imports: [
...
BorderCardDirective,
...
]
If you are using modular approach, also same thing ensure app.module.ts
has the directive added to the declarations
array if it's not standalone, or to the imports
array if it's standalone!
If not imported then Angular will take appBorderCard
as an HTML attribute and will not throw an error!
So, if the component/directive is standalone and used in a module, it belongs on the imports
array, if not it belongs on the declarations
array.
When using the standalone approach, we are limited to using only standalone true component/directive
with them being added to the imports
array!