Comment développer des bibliothèques angulaires localement

Nov 25 2022
Salut ! Dans cette histoire, je voudrais vous montrer comment travailler avec les bibliothèques Angular localement et les développer sans avoir besoin de les pousser constamment vers le référentiel distant :) DRY - comment éviter la répétition de code Si vous travaillez sur un projet Angular volumineux et complexe , je suis sûr que vous serez confronté à un problème de duplication de code. Par exemple, concentrons-nous sur le "look" de votre application - toutes les pages doivent probablement avoir un style cohérent (vous ne voulez pas confondre vos utilisateurs et avoir des boutons d'apparence différente sur chaque page).
Photo de Ryunosuke Kikuno sur Unsplash

Salut ! Dans cette histoire, je voudrais vous montrer comment travailler avec les bibliothèques Angular localement et les développer sans avoir besoin de les pousser constamment vers le référentiel distant :)

DRY - comment éviter la répétition du code

Si vous travaillez sur un projet Angular volumineux et complexe, je suis sûr que vous serez confronté à un problème de duplication de code. Par exemple, concentrons-nous sur le "look" de votre application - toutes les pages doivent probablement avoir un style cohérent (vous ne voulez pas confondre vos utilisateurs et avoir des boutons d'apparence différente sur chaque page). Ok, c'est facile - vous pouvez simplement déplacer ces styles courants dans le styles.scssfichier ou utiliser le motif 7–1 pour le concevoir de manière élégante.

Mais que se passe-t-il si vous avez des composants communs , qui doivent non seulement avoir l'air, mais aussi se comporter de la même manière ? C'est… toujours facile ! Créez simplement un module partagé dans votre application, définissez-y vos composants et exportez-les dans la définition du module. Super !

Maintenant, concentrons-nous sur le problème le plus complexe. Et si votre entreprise a plus d'une application Angular ? Et chaque application doit se ressembler, par exemple :

  • avoir le même en-tête et pied de page
  • ont le même style CSS d'entreprise
  • avoir la même manière d'afficher les formulaires, les erreurs de validation et autres messages

Création de bibliothèque

Vous trouverez ici la page de documentation officielle sur la création de bibliothèques . Suivons les étapes décrites ici et créons notre bibliothèque :

ng new my-shared-workspace --no-create-application
cd my-shared-workspace
ng generate library my-buttons

  • my-shared-workspace est un espace de travail commun pour vos bibliothèques
  • my-buttons est un exemple de bibliothèque que vous pouvez réutiliser dans d'autres projets

{
  "name": "@my-company/my-buttons",
  "version": "0.0.1",
  "peerDependencies": {
    "@angular/common": "^14.2.0",
    "@angular/core": "^14.2.0"
  },
  "dependencies": {
    "tslib": "^2.3.0"
  }
}

my-shared-workspace/projects/my-buttons/src/lib> ng g c fancy-button

Le composant est créé, nous pouvons donc mettre à jour son modèle pour afficher un bouton

<button>fancy-button works!</button>

@NgModule({
  declarations: [
    MyButtonsComponent,
    FancyButtonComponent
  ],
  imports: [
  ],
  exports: [
    MyButtonsComponent,
    FancyButtonComponent // <-- here
  ]
})
export class MyButtonsModule { }

/*
 * Public API Surface of my-buttons
 */

export * from './lib/my-buttons.service';
export * from './lib/my-buttons.component';
export * from './lib/my-buttons.module';
export * from './lib/fancy-button/fancy-button.component'; // <-- here

Nous avons déjà créé notre bibliothèque, il est donc temps pour l' application client. J'utiliserai Angular CLI pour le générer : ng new my-application, et j'ajouterai @my-company/my-buttonsen tant que dépendance dans l'application package.json(dernière dépendance, après zone.js)

{
  "name": "my-application",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^14.2.0",
    "@angular/common": "^14.2.0",
    "@angular/compiler": "^14.2.0",
    "@angular/core": "^14.2.0",
    "@angular/forms": "^14.2.0",
    "@angular/platform-browser": "^14.2.0",
    "@angular/platform-browser-dynamic": "^14.2.0",
    "@angular/router": "^14.2.0",
    "rxjs": "~7.5.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4",
    "@my-company/my-buttons": "0.0.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^14.2.6",
    "@angular/cli": "~14.2.6",
    "@angular/compiler-cli": "^14.2.0",
    "@types/jasmine": "~4.0.0",
    "jasmine-core": "~4.3.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~4.7.2"
  }
}

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyButtonsModule } from '@my-company/my-buttons';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    MyButtonsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

<lib-fancy-button></lib-fancy-button>

Error: src/app/app.component.html:1:1 - error NG8001: 'lib-fancy-button' is not a known element:
1. If 'lib-fancy-button' is an Angular component, then verify that it is part of this module.
2. If 'lib-fancy-button' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

1 <lib-fancy-button></lib-fancy-button>
  ~~~~~~~~~~~~~~~~~~

  src/app/app.component.ts:5:16
    5   templateUrl: './app.component.html',
                     ~~~~~~~~~~~~~~~~~~~~~~
    Error occurs in the template of component AppComponent.




** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

Vous verrez également d'autres erreurs telles que :

Error: src/app/app.module.ts:3:33 - error TS2307: Cannot find module '@my-company/my-buttons' or its corresponding type declarations.

3 import { MyButtonsModule } from '@my-company/my-buttons';

npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@my-company%2fmy-buttons - Not found
npm ERR! 404
npm ERR! 404  '@my-company/[email protected]' is not in this registry.
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
Npm link to the rescue

Vous trouverez ici la documentation complète de la commande npm link . Je vais vous montrer comment utiliser cette commande pour développer une bibliothèque partagée localement, avec un rechargement en direct.

Revenons au my-shared-workspacerépertoire, construisons my-buttonsla bibliothèque (le drapeau --watchest responsable du rechargement en direct) et allons dans le répertoire de construction

my-shared-workspace> ng build my-buttons --configuration development --watch
Building Angular Package

------------------------------------------------------------------------------
Building entry point '@my-company/my-buttons'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy full compilation mode.
✔ Writing FESM bundles
✔ Copying assets
✔ Writing package manifest
✔ Built @my-company/my-buttons

------------------------------------------------------------------------------
Built Angular Package
 - from: /Users/<user>/<path>/my-shared-workspace/projects/my-buttons
 - to:   /Users/<user>/<path>/my-shared-workspace/dist/my-buttons
------------------------------------------------------------------------------

my-shared-workspace> cd dist/my-buttons
my-shared-workspace/dist/my-buttons> npm link

added 1 package, and audited 3 packages in 1s

found 0 vulnerabilities

Dans le my-applicationrépertoire, angular.jsonajoutez une option supplémentaire - preserveSymlinks, qui nous permettra d'utiliser cette bibliothèque liée.

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "my-application": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/my-application",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "preserveSymlinks": true,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "my-application:build:production"
            },
            "development": {
              "browserTarget": "my-application:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "my-application:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          }
        }
      }
    }
  }
}

my-application> npm link @my-company/my-buttons

added 1 package, removed 1 package, and audited 918 packages in 2s

121 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Après cela, exécutez ng serveà nouveau:

my-application> ng serve
✔ Browser application bundle generation complete.

Initial Chunk Files   | Names         |  Raw Size
vendor.js             | vendor        |   3.30 MB |
polyfills.js          | polyfills     | 318.02 kB |
styles.css, styles.js | styles        | 210.56 kB |
main.js               | main          |  12.32 kB |
runtime.js            | runtime       |   6.53 kB |

                      | Initial Total |   3.84 MB

Build at: 2022-11-24T18:27:00.803Z - Hash: 7669daf65ca2f665 - Time: 4598ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


✔ Compiled successfully.

Voyons le résultat :

Super ! Essayez maintenant de changer quelque chose dans fancy-button-component.html

<button>fancy-button works - live reload!</button>

Vous pouvez maintenant travailler à la fois sur la bibliothèque et sur l'application sans aucun inconvénient :)

Vérification

Pour vérifier quelle version de la bibliothèque vous utilisez, vous pouvez exécuter la commande ci-dessous :

npm list -g
/usr/local/lib
├── @angular/[email protected]
├── @my-company/[email protected]+1669314745555 -> ./../../../Users/<user>/<path>/my-shared-workspace/dist/my-buttons

Après avoir fini de travailler sur vos modifications localement, vous pouvez arrêter d'utiliser la bibliothèque liée dans l'application cliente de la manière suivante

my-application> npm unlink @my-company/my-buttons --no-save

my-shared-workspace/dist/my-buttons> npm rm -g @my-company/my-buttons

Matériaux