Creating Frontend Libraries
Adding Libraries
When creating libraries parts and fragments, you can use different approaches according to the desired level of granularity:
- when you plan for reuse, creating a single library for each component (part/fragment editor) is the best choice, unless your parts/fragments can be considered so related among themselves that they can be implemented in the same library.
- when implementing project-specific editors that will probably never be reused outside of your project, or domain-specific sets of editors, you can go with a multiple-components approach and implement all of them in a single library. In this case the library will typically have a module with the sub-routes for each targeted component in it, as you design a sub-route for each target component.
Single-Component Libraries
In the single-component approach you create a single library for each editor, containing both the editor UI and its page wrapper.
Also, optionally a -pg
library will be created for all the single-components libraries which go together in a project.
▶️ (1) Add the library to the app workspace:
ng g library @myrmidon/cadmus-part-__PRJ__-__NAME__ --prefix cadmus
Example name:
cadmus-part-itinera-cod-loci
. Use-fr-
instead of-part-
for fragments.
In each single-component library you will have:
- the part/fragment model (e.g. a file
cod-loci-part
). - the part/fragment editor UI (e.g. a folder with the
cod-loci-part
editor component). - the part/fragment editor wrapper (e.g. a folder with the
cod-loci-part-feature
editor wrapper).
▶️ (2) Optionally, you might want to provide another library for all of your components in the project with sub-routes to the editor wrappers (e.g. cadmus-part-itinera-pg
). This will include only a module, which imports all the single component libraries, and exports routes like e.g.:
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
// cadmus
import { PendingChangesGuard } from '@myrmidon/cadmus-core';
// parts from your libraries
import {
CodBindingsPartFeatureComponent,
COD_BINDINGS_PART_TYPEID,
} from '@myrmidon/cadmus-part-codicology-bindings';
import {
CodContentsPartFeatureComponent,
COD_CONTENTS_PART_TYPEID,
} from '@myrmidon/cadmus-part-codicology-contents';
import {
CodDecorationsPartFeatureComponent,
COD_DECORATIONS_PART_TYPEID,
} from '@myrmidon/cadmus-part-codicology-decorations';
// ...etc.
// sub-routes
export const RouterModuleForChild = RouterModule.forChild([
{
path: `${COD_BINDINGS_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: CodBindingsPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: `${COD_CONTENTS_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: CodContentsPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: `${COD_DECORATIONS_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: CodDecorationsPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
// ... etc.
]);
@NgModule({
declarations: [],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
// Cadmus
RouterModuleForChild,
],
exports: [],
})
export class CadmusPartCodicologyPgModule {}
Legacy Approach
This approach for multiple-components libraries is more a legacy and is used in the core part/fragment shell libraries like general and philology.
▶️ (1) Create two libraries: one with the editors UI, and another for their page wrappers. Conventionally, these libraries are suffixed with -ui
and -pg
respectively, e.g.:
ng g library @myrmidon/cadmus-PRJ-part-ui --prefix cadmus
ng g library @myrmidon/cadmus-PRJ-part-pg --prefix cadmus
The UI library is just a standard Angular library with a bunch of components in it. Once you have added all of your imports, you just have to include your part/fragment editors there, and export their components.
The PG library is a standard Angular library with some added routes, one for each part included in the library. In its module add code like this:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatToolbarModule } from '@angular/material/toolbar';
import { PendingChangesGuard } from '@myrmidon/cadmus-core';
import {
BIBLIOGRAPHY_PART_TYPEID,
CATEGORIES_PART_TYPEID,
CHRONOTOPES_PART_TYPEID,
// ...
} from '@myrmidon/cadmus-part-general-ui';
import { BibliographyPartFeatureComponent } from './bibliography-part-feature/bibliography-part-feature.component';
import { CategoriesPartFeatureComponent } from './categories-part-feature/categories-part-feature.component';
import { ChronologyFragmentFeatureComponent } from './chronology-fragment-feature/chronology-fragment-feature.component';
// ...
// https://github.com/ng-packagr/ng-packagr/issues/778
export const RouterModuleForChild = RouterModule.forChild([
{
path: `${BIBLIOGRAPHY_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: BibliographyPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: `${CATEGORIES_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: CategoriesPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: `${COMMENT_PART_TYPEID}/:pid`,
pathMatch: 'full',
component: CommentPartFeatureComponent,
canDeactivate: [PendingChangesGuard],
},
// ...
// for fragments:
// {
// path: `fragment/:pid/${COMMENT_FRAGMENT_TYPEID}/:loc`,
// pathMatch: 'full',
// component: CommentFragmentFeatureComponent,
// canDeactivate: [PendingChangesGuard],
// },
]);
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
RouterModuleForChild,
// material
MatButtonModule,
MatCheckboxModule,
MatCardModule,
MatExpansionModule,
MatIconModule,
MatInputModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSelectModule,
MatTooltipModule,
MatToolbarModule,
// cadmus
BibliographyPartFeatureComponent,
CategoriesPartFeatureComponent,
ChronologyFragmentFeatureComponent,
// ...
],
exports: [
BibliographyPartFeatureComponent,
CategoriesPartFeatureComponent,
ChronologyFragmentFeatureComponent,
// ...
],
})
export class CadmusPartGeneralPgModule {}
Setup
Once you create a library, whatever its type:
(1) remove the stub code files added by Angular CLI: the sample component and its service. Also, take the time for adding more metadata to its package.json
file, e.g. (replace __PRJ__
with your project’s ID). In peerDependencies
you should add at least the Cadmus peer dependencies, like in this example:
{
"name": "@myrmidon/cadmus-__PRJ__-__NAME__",
"version": "0.0.1",
"description": "Cadmus - __PRJ__ ...",
"keywords": [
"Cadmus",
"__PRJ__"
],
"homepage": "https://github.com/vedph/cadmus-__PRJ__-app",
"repository": {
"type": "git",
"url": "https://github.com/vedph/cadmus-__PRJ__-app"
},
"author": {
"name": "Daniele Fusi"
},
"peerDependencies": {
"@angular/common": "^18.0.0",
"@angular/core": "^18.0.0",
"@myrmidon/ngx-tools": "^2.0.0",
"@myrmidon/cadmus-core": "^12.0.0",
"@myrmidon/cadmus-state": "^13.0.0",
"@myrmidon/cadmus-ui": "^13.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}
(2) in your app’s package.json
file, to speed up builds, for each added library you can add the corresponding build commands to package.json
scripts (to be run like npm run <SCRIPTNAME>
), e.g.:
{
"build-ass": "ng build @myrmidon/cadmus-refs-assertion",
"build-blk": "ng build @myrmidon/cadmus-text-block-view",
"build-lib": "npm run-script build-ass && npm run-script build-blk"
}
The build-lib
command is used to build all the libraries in the workspace. Be sure to enumerate them in their order of dependencies when writing the command.