Angular est-il vraiment plus compliqué que React ?

Quand on débute avec Angular, il est facile de se sentir découragé face à la multitude de concepts à assimiler. Cette complexité peut inciter à se tourner vers des frameworks comme React, qui semblent plus simples à première vue. Mais est-ce vraiment le cas ?

Abonnez-vous à notre chaîne

Pour profiter des prochaines vidéos sur Angular, abonnez-vous à la nouvelle chaîne YouTube !

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Créer une librairie Angular

Pourquoi créer une librairie ?

Imaginez une boîte à outils que vous pourriez partager avec vos collègues ou d'autres développeurs. C'est exactement ce qu'est une librairie Angular. Voici quelques raisons pour lesquelles vous pourriez vouloir créer une librairie :

  1. Réutilisation du code : Au lieu de réécrire les mêmes composants ou services dans chaque projet, vous pouvez les centraliser dans une librairie et les réutiliser facilement.

  2. Maintenance simplifiée : Les mises à jour et les corrections de bugs peuvent être effectuées à un seul endroit, puis propagées à tous les projets utilisant la librairie.

  3. Cohérence : Une librairie permet d'assurer une cohérence visuelle et fonctionnelle entre différents projets, particulièrement utile pour les grandes organisations.

  4. Collaboration : Les librairies facilitent le partage de code entre équipes ou avec la communauté open-source.

  5. Modularité : Vous pouvez diviser votre application en modules plus petits et plus gérables, chacun pouvant être développé et testé indépendamment.

  6. Optimisation des performances : Les librairies bien conçues peuvent être optimisées pour la taille du bundle et les performances, bénéficiant à tous les projets qui les utilisent.

EXEMPLE CONCRET

Imaginons que vous travaillez sur plusieurs projets Angular qui nécessitent tous une gestion des utilisateurs. Au lieu de recréer les composants de carte utilisateur, de liste d'utilisateurs, et les services associés dans chaque projet, vous pourriez les regrouper dans une librairie my-user-lib. Cette approche vous fera gagner du temps, réduira les duplications de code et assurera une expérience utilisateur cohérente à travers vos applications.

Création de la librairie

1. Générer la librairie

Commençons par créer une nouvelle librairie avec la CLI Angular :

bash
ng new my-workspace --no-create-application
cd my-workspace
ng generate library my-user-lib

Nom de la librairie

Choisissez un nom explicite pour votre librairie qui décrit bien sa fonction. D'après la documentation officielle, le nom de la librairie devrait éviter de commencer par ng- car c'est un préfixe réservé pour les librairies officielles d'Angular.

Ajouter une application

Si vous souhaitez créer une application qui utilise votre librairie, vous pouvez en ajouter une avec la commande ng generate application my-app.

2. Structure du projet

Après la génération, vous aurez une structure comme celle-ci :

my-workspace/
├── projects/
│   └── my-user-lib/
│       ├── src/
│       │   ├── lib/
│       │   ├── public-api.ts
│       │   └── test.ts
│       ├── ng-package.json
│       ├── package.json
│       └── tsconfig.lib.json
└── package.json

3. Création d'un composant

Créons un composant réutilisable pour notre librairie :

ts
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { User } from './user.interface';

@Component({
  selector: 'lib-user-card',
  standalone: true,
  imports: [CommonModule],
  template: `
    @if (user) {
      <div class="user-card">
        <h2>{{ user.name }}</h2>
        <p>{{ user.email }}</p>
      </div>
    }
  `,
  styles: `
    .user-card {
      padding: 1rem;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
  `
})
export class UserCardComponent {
  @Input() user: User | undefined;
}
ts
export interface User {
  id: number;
  name: string;
  username: string;
  email: string;
}

4. Export des composants

Pour rendre votre composant accessible, vous devez l'exporter dans le public-api.ts :

ts
export * from './lib/user-card/user-card.component';
export * from './lib/user-card/user.interface';

5. Ajouter des dépendances (optionnel)

Si votre librairie nécessite des dépendances externes, vous pouvez les spécifier dans le fichier package.json de votre librairie. Voici un exemple de structure pour le package.json (ici avec lodash en dépendance) :

json
{
  "name": "my-user-lib",
  "version": "1.0.0",
  "peerDependencies": {
    "@angular/common": ">=17.0.0",
    "@angular/core": ">=17.0.0"
  },
  "dependencies": {
    "tslib": "^2.3.0",
    "lodash": "^4.17.21"
  },
  "sideEffects": false,
  "license": "MIT",
  "author": "Votre Nom",
  "repository": "https://github.com/votre-username/my-user-lib",
  "publishConfig": {
    "access": "public"
  }
}
json
{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../dist/my-user-lib",
  "lib": {
    "entryFile": "src/public-api.ts"
  },
  "allowedNonPeerDependencies": [
    "lodash"
  ]
}

Expliquons chaque section :

  • Les peerDependencies sont les dépendances que votre librairie s'attend à trouver dans le projet hôte. Typiquement, il s'agit des packages Angular core.

CONSEIL

Utilisez >= pour la version d'Angular pour permettre la compatibilité avec les versions futures, tant que votre librairie reste compatible.

  • Les dependencies sont les packages externes dont votre librairie a besoin pour fonctionner. Ces dépendances seront installées automatiquement avec votre librairie.

ATTENTION

Il faut penser à ajouter allowedNonPeerDependencies dans le ng-package.json si vous utilisez des dépendances qui ne sont pas des peerDependencies. De plus, si vous tester votre librairie en développement, il faut penser à ajouter la dépendance à la racine du projet avec npm install <nom-de-la-dependance>.

6. Build et publication

Pour construire votre librairie :

bash
ng build my-user-lib

ATTENTION

Assurez-vous de mettre à jour la version dans le package.json de votre librairie avant chaque publication.

Pour publier sur npm :

bash
cd dist/my-user-lib
npm publish

Utilisation de la librairie

Dans un projet Angular, vous pouvez maintenant installer et utiliser votre librairie :

bash
npm install my-user-lib

Tester en développement

Si vous souhaitez tester votre librairie en développement, vous n'êtes pas obligé de la publier sur npm. Vous pouvez directement l'utiliser dans votre code. Mais il faut penser à builder la librairie avant.

Vous pouvez builder en fond avec la commande ng build my-user-lib --watch qui va reconstruire la librairie à chaque changement.

Exemple d'utilisation :

ts
import { Component } from '@angular/core';
import { UserCardComponent } from 'my-user-lib';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [UserCardComponent],
  template: `
    <lib-user-card [user]="currentUser"></lib-user-card>
  `
})
export class HomeComponent {
  currentUser = {
    id: 1,
    name: 'John Doe',
    username: 'johndoe',
    email: '[email protected]'
  };
}

Points d'entrée multiples

Lorsque votre librairie grandit, il peut être intéressant de la diviser en plusieurs points d'entrée (aussi appelés points d'entrée secondaires). Cette approche est notamment utilisée par Angular Material où chaque composant a son propre point d'entrée :

typescript
import { MatGridListModule } from '@angular/material/grid-list';
import { MatInputModule } from '@angular/material/input';

TIP

  1. Meilleur tree shaking : Le tree shaking est un processus qui élimine le code non utilisé. Par exemple, si vous n'utilisez que le composant UserCard, le code du UserList ne sera pas inclus dans votre application finale
  2. Optimisation du code splitting : Le code splitting permet de découper votre application en plusieurs fichiers qui seront chargés uniquement quand nécessaire. Par exemple, si un utilisateur ne va jamais sur la page de liste des utilisateurs, le code de cette fonctionnalité ne sera jamais téléchargé
  3. Gestion des dépendances plus flexible : Chaque point d'entrée peut avoir ses propres dépendances. Par exemple, le module de liste d'utilisateurs pourrait utiliser une librairie de tableau, tandis que le module de carte utilisateur pourrait utiliser une librairie de carte
  4. Meilleure séparation des fonctionnalités : Organisation plus claire du code, comme avoir tous les composants liés aux utilisateurs dans un même dossier

Création d'un point d'entrée secondaire

Prenons l'exemple d'un point d'entrée pour la gestion des utilisateurs. Voici la structure à créer :

my-workspace/
└── projects/
    └── my-user-lib/
        ├── users/                  # Nouveau point d'entrée
        │   ├── src/
        │   │   ├── user-list/
        │   │   │   └── user-list.component.ts
        │   │   └── user-card/
        │   │       └── user-card.component.ts
        │   ├── ng-package.json
        │   └── public-api.ts
        └── src/...                 # Point d'entrée principal
ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { User } from '../../user.interface';

@Component({
  selector: 'lib-user-list',
  standalone: true,
  imports: [CommonModule],
  template: `
    @for (user of users; track user.id) {
      <div class="user-item">
        {{ user.name }}
      </div>
    }
  `
})
export class UserListComponent {
  users: User[] = [];
}
ts
export * from './src/user-list/user-list.component';
export * from './src/user-card/user-card.component';
json
{
  "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
  "lib": {
    "entryFile": "public-api.ts"
  }
}

CONSEIL

Choisissez une structure logique pour vos points d'entrée. Par exemple, vous pouvez regrouper par :

  • Fonctionnalité (auth, users, admin...)
  • Type de composant (forms, layout, utils...)
  • Domaine métier (billing, shipping, inventory...)

Utilisation des points d'entrée

Une fois configuré, vous pouvez importer spécifiquement ce dont vous avez besoin :

typescript
// Import depuis le point d'entrée principal
import { UserService } from 'my-user-lib';

// Import depuis le point d'entrée secondaire
import { UserListComponent } from 'my-user-lib/users';

Dépendances entre points d'entrée

Les points d'entrée peuvent dépendre les uns des autres. Par exemple, si vous avez un point d'entrée core avec des services partagés :

ts
export * from './src/services/user.service';
ts
import { UserService } from 'my-user-lib/core';

@Component({
  // ...
})
export class UserListComponent {
  constructor(private userService: UserService) {}
}

ATTENTION

Évitez les dépendances circulaires entre les points d'entrée. ng-packagr détectera ces problèmes et générera une erreur.

Configuration TypeScript

Pour que l'IDE reconnaisse correctement les imports, ajoutez les chemins dans le tsconfig.json à la racine du projet :

json
{
  "compilerOptions": {
    "paths": {
      "my-user-lib": [
        "./projects/my-user-lib"
      ],
      "my-user-lib/*": [
        "./projects/my-user-lib/*"
      ]
    }
  }
}

Chaque mois, recevez en avant-première notre newsletter avec les dernières actualités, tutoriels, astuces et ressources Angular directement par email !