-
Notifications
You must be signed in to change notification settings - Fork 0
Angular
Composant personnalisé avec gestion du formControl simple : https://medium.com/angular-in-depth/dont-reinvent-the-wheel-when-implementing-controlvalueaccessor-a0ed4ad0fafd
Résultat final :
<app-slide-toggle formControlName="automatic">Automatique ?</app-slide-toggle>Exemple : https://github.com/Bludwarf/CodeBank/tree/master/Angular/ControlValueAccessor
Détection à l'intérieur d'une propriété (comme un array ou un object) avec KeyValueDiffers : https://stackoverflow.com/a/46330816/1655155.
Ensuite pour rafraichir les MatTableDataSource, on peut utiliser cette technique : https://stackoverflow.com/a/49224747/1655155.
Exemple : https://gist.github.com/Bludwarf/1334767e0124f78141f42dfa60e530c8.
Alternative : utiliser uniquement des objets non mutables (comme un ReadonlyArray), pour forcer la création d'une nouvelle instance.
ControlValueAccessor : si on veut détecter une modification sur un control, il ne faut pas affecter directement la valeur du control mais faire appel à la méthode setValue. Exemple :
const control = this.formGroup.controls.myControl;
control.setValue(newValue);
control.markAsDirty(); // Pour marquer le formulaire comme modifié par l'utilisateurRécupérer tous les éléments de toutes les pages d'une API : PageResultUtils
On peut utiliser un attribut booléen sous cette forme dans le template :
<app-component monAttribut></app-component>Ce qui est équivalent à :
<app-component [monAttribut]="true"></app-component>Dans ce cas, l'attribut a pour valeur la chaîne vide. Cela se traduit comme suit dans le composant :
private monAttributBoolean = false;
@Input() set monAttribut(value: boolean|string) {
this.monAttributBoolean = value === '' || !!value;
}
get monAttribut(): boolean {
return this.monAttribut;
}Exemple de code :
export class ParentComponent {
@ViewChildren(ChildComponent)
children: QueryList<ChildComponent>;
}<app-other-component [otherProperty]="children.length" />L'erreur apparaît parce que la valeur de otherProperty change, puisque'au 1er cycle children n'est pas encore initialisé, alors qu'au 2e cycle, elle l'est.
Quand un composant parent se base sur ses enfants pour calculer ses propriétés, on peut empêcher l'erreur en déclenchant manuellement la détection de changements, juste après l'initialisation des enfants annotés @ViewChildren.
Exemple :
export class ParentComponent implements AfterViewInit {
@ViewChildren(ChildComponent)
children: QueryList<ChildComponent>;
ngAfterViewInit(): void {
this.changeDetectorRef.detectChanges();
}
}C'est une des solutions possibles détaillées dans l'article, mais indiquée comme problématique (cf. avant dernière section de l'article).
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ParentComponent implements AfterContentChecked {
@ViewChildren(ChildComponent)
children: QueryList<ChildComponent>;
ngAfterContentChecked(): void {
this.changeDetectorRef.markForCheck();
}
}Solution acceptable ? Ça paraît bizarre de faire un markForCheck dans un ngAfterContentChecked...
Si le composant le permet, on peut uniquement changer le mode de détection de changement, sans le déclencher manuellement.
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ParentComponent {
@ViewChildren(ChildComponent)
children: QueryList<ChildComponent>;
}Par défaut, QueryList ne gère que les composants concrets. Si on souhaite utiliser une interface ou une classe abstraite, il faut ajouter l'annotation suivante dans chaque composant concret :
@Component({
// ...
// Indique à Angular que ce composant doit apparaître dans une QueryList<AbstractComponent>
providers: [{provide: AbstractComponent, useExisting: forwardRef(() => ConcreteComponent)}],
})
export class ConcreteComponent extends AbstractComponentAjouter la config Karma pour ng test :
{
"projects": {
"<mon-projet>": {
"architect": {
"test": {
"options": {
"karmaConfig": "./karma.conf.js"
}
}
}
}
}
}Lancer certains tests :
ng test --include='**/notes.spec.ts'Lancer un évènement setTimeout dans un composant déclenche une détection de changement Angular, qui peut conduire à un boucle sans fin. Dans ce cas utiliser plutôt :
this.ngZone.runOutsideAngular(() => {
// code...
})- Différence entre
runOutsideAngularetrun?
Exemple d'erreur lors du début de la migration 10 -> 11 :
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: cuetube@0.1.0
npm error Found: @angular-devkit/build-angular@0.1002.4
npm error node_modules/@angular-devkit/build-angular
npm error dev @angular-devkit/build-angular@"~0.1102.19" from the root project
npm error
npm error Could not resolve dependency:
npm error dev @angular-devkit/build-angular@"~0.1102.19" from the root project
npm error
npm error Conflicting peer dependency: @angular/compiler-cli@11.2.14
npm error node_modules/@angular/compiler-cli
npm error peer @angular/compiler-cli@"^11.0.0 || ^11.2.0-next" from @angular-devkit/build-angular@0.1102.19
npm error node_modules/@angular-devkit/build-angular
npm error dev @angular-devkit/build-angular@"~0.1102.19" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error C:\Users\MLA20849\AppData\Local\npm-cache\_logs\2025-12-12T16_35_53_260Z-eresolve-report.txt
npm error A complete log of this run can be found in: C:\Users\MLA20849\AppData\Local\npm-cache\_logs\2025-12-12T16_35_53_260Z-debug-0.log
Le 1er bloc indique que la version installée (dans node_modules) (0.1002.4) ne correspond pas à la version demandée (par le package.json) (~0.1102.19) :
npm error Found: @angular-devkit/build-angular@0.1002.4
npm error node_modules/@angular-devkit/build-angular
npm error dev @angular-devkit/build-angular@"~0.1102.19" from the root project
Il y a donc un conflit de versions.
La solution consiste donc à supprimer la dépendance dans le dossier node_modules et supprimer le fichier (attention avec ça ! on peut faire des montées de version de lib non souhaitées) puis retenter la migration.package-lock.json
- Pour éviter le problème, devrait-on exécuter la migration Angular et Material via une seule commande ?
- Mettre en place un système pour surcharger les environnements Angular par des variables d'environnements (exemple)
- Ajouter les secrets correspondants côté CI/CD (exemple) (doc)
| Méthode | Source de la donnée | Liée au routeur* | Visible dans l'URL | Accessible hors constructeur |
|---|---|---|---|---|
ActivatedRouteSnapshot.data |
Config de route statique (data: {...}) |
✅ | ❌ | ✅ |
resolve (Resolvers) |
Fonction async exécutée avant activation, alimente .data
|
✅ | ❌ | ✅ |
getCurrentNavigation().extras.state |
Passé impérativement via navigate(..., { state })
|
❌ | ❌ | ❌ |
history.state / Location.getState()
|
Même state que ci-dessus, lu autrement | ❌ | ❌ | ✅ |
ActivatedRoute.paramMap |
Paramètres de route (:id) |
✅ | ✅ | ✅ |
ActivatedRoute.queryParamMap |
Query params (?foo=bar) |
✅ | ✅ | ✅ |
withComponentInputBinding() |
Bind auto de params/queryParams/data vers @Input()
|
✅ | ✅ (selon la source) | ✅ |
Matrix params (;param=value) |
Segment d'URL, via paramMap
|
✅ | ✅ | ✅ |
| Service partagé / store | État applicatif, hors router | ❌* | ❌ | ✅ |
* Colonne "Liée au routeur" :
- ✅ : Ancrées à l'URL/la config de route (
data,resolve,params,withComponentInputBinding) - ✅ : Deep-link / Refresh OK : cette donnée est reconstructible depuis l'URL seule.
- ✅ : Recalculées à l'identique à chaque activation, peu importe comment on est arrivé.
- ❌ : Ancrées à l'événement de transition (
state, service partagé) - ❌ : N'existent que le temps d'une navigation précise (rien en deep-link, pas de navigation préalable)
- ❌ : Seul canal capable de transporter une donnée d'une route à une autre lors d'une redirection.
- ❌* : Exception "Service partagé / store" : Survit au refresh si
sessionStorage/localStorage(mais pas de deep-link)
Pour choisir rapidement :
- Donnée qui doit survivre à un refresh ou être partageable →
datade route,resolve, params/queryParams. - Donnée volatile, sensible ou complexe (objet non sérialisable en URL) transmise entre deux vues dans la même session →
statede navigation ou service partagé. - Donnée qui doit être prête avant l'affichage du composant →
resolve.