TL;DR: Discover the top practices for how to share Angular code between projects by exploring reusable modules, leveraging Nx for efficient code sharing, and ensuring maintainable, scalable architecture in your Angular applications.
Sharing code across multiple Angular projects has numerous benefits, including increased productivity, enhanced consistency, cost efficiency, and improved maintainability. It allows developers to reuse well-tested components, services, and utilities, reducing duplication.
Let’s look at the best practices for sharing Angular code between projects, including using module libraries, code organization, versioning, and maintenance of shared code.
Modularize code using Angular libraries
Angular libraries are a powerful way to share code. These reusable packages can’t run independently and need to be imported into other apps.
To create an Angular library, use the following command.
ng generate library angular-lib-1
Once created, the library can be used in other apps. For example, reactive-forms is a famous Angular library. To add it to the project, use the following command.
ng add @angular/forms
Then, the ReactiveFormsModule from @angular/forms can be imported into the project.
Use Nx for Monorepos
A monorepo is a single repository that contains code for different projects. Nx is a scalable monorepo building tool. It is fast and efficient and has many famous clients like Walmart, FedEx, and Shopify.
To create an Angular monorepo, use the following command.
npx create-nx-workspace@latest angular-monorepo --preset=angular-monorepo
Furthermore, Nx has the ability to create local libraries, which will result in better reusability and better separation.
For example, let’s consider a system where the domain areas are student, teacher, and staff. A new library can be created for each and every domain area.
nx g @nx/angular:library student --directory=libs/student --standalone
nx g @nx/angular:library teacher --directory=libs/teacher --standalone
nx g @nx/angular:library staff --directory=libs/staff --standalone
These libraries can be shared in other projects, as follows.
{
"compilerOptions": {
...
"paths": {
"@angular-monorepo/student": ["libs/student/src/index.ts"],
"@angular-monorepo/teacher": ["libs/teacher/src/index.ts"],
"@angular-monorepo/staff": ["libs/staff/src/index.ts"]
},
...
}
}
Therefore, using Nx for maintaining monorepos is extremely useful.
Use feature modules
Another best practice for sharing Angular code between projects is to create feature modules. As the system grows, it is recommended to separate each functionality into separate modules. For example, let’s consider the previous example of the system containing students, teachers, and staff. Separate modules can be created for all three types.
ng generate module student
ng generate module teacher
ng generate module staff
These feature modules can be easily imported into the app.module.ts file as well as into other projects. Refer to the following code example.
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { StudentModule } from './student/student.module';
import { TeacherModule } from './teacher/teacher.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
StudentModule,
TeacherModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
This structure allows for easier maintenance and scalability as the app evolves.
Create shared modules
It is recommended to create shared modules that contain similar functions. For example, date functions and math functions can be added into one module and can be shared.
Refer to the following code example.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ButtonComponent } from './components/button/button.component';
import { HighlightDirective } from './directives/highlight.directive';
import { DateFormatPipe } from './pipes/date-format.pipe';
@NgModule({
declarations: [
ButtonComponent,
HighlightDirective,
DateFormatPipe
],
imports: [
CommonModule
],
exports: [
ButtonComponent,
HighlightDirective,
DateFormatPipe
]
})
export class SharedModule { }
This approach helps in maintaining consistency and reusability across different parts of the app.
Versioning and dependency management
Semantic versioning
When sharing code through libraries, it’s crucial to follow semantic versioning (semver). It provides a clear and predictable way to manage versions of your shared libraries, helping to communicate the impact of changes to the consumers of the library. The semantic versioning types include:
Major version: Incompatible API changes.
Minor version: Backward-compatible functionality.
Patch version: Backward-compatible bug fixes.
Dependency management
As a best practice, it is essential to manage the dependencies of the shared libraries. It is recommended to use a package manager like yarn or npm to manage the dependencies properly.
Integration tests
To ensure the shared libraries are working as expected, it is essential to conduct integration tests. Unlike unit tests, which focus on individual components, integration tests verify the interactions among components, services, and modules.
In the context of shared Angular libraries, it is essential to write integration tests to validate that the library functions correctly when used within various projects. Integration tests will help identify issues early, ensuring that the shared code works seamlessly across different environments and scenarios.
To conduct integration tests, tools like Jasmine, Karma, or Jest can be used.
Continuous Integration/Continuous Deployment (CI/CD)
CI/CD pipelines must be configured for shared Angular libraries. These pipelines automate the process of building, testing, and deploying code, ensuring that every change is validated before it is merged.
This will reduce the risk of introducing bugs. Tools like GitHub Actions, Travis CI, and Jenkins can be configured to run tests, linting, and build processes automatically.
Refer to the following code example of a simple GitHub Actions workflow for an Angular library.
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- run: npm run build
- run: npm test
Additionally, these pipelines can be set up to publish new versions of the shared libraries to a private NPM registry or other package management system, streamlining the deployment process.
Proper documentation
Shared Angular libraries should contain proper documentation for successful adoption and usage. It should include detailed usage examples, API references, and setup instructions to help developers understand how to integrate and use the library effectively. It should also cover common issues, troubleshooting tips, and best practices.
One of the most famous Angular libraries is Angular Material. It has a separate website that contains demos, setup instructions, and a guide.
Well-documented libraries reduce the learning curve for new users and provide a single source of truth for all information related to the library. Tools like TypeDoc can be used to generate API documentation directly from the TypeScript code, ensuring that the documentation stays up to date with the codebase.
Consistent coding standards
Maintaining consistent coding standards across all projects and shared libraries ensures code quality and readability. Establishing and enforcing these standards helps developers write clean, maintainable, and bug-free code.
Tools like Prettier and ESLint can be used to maintain coding standards and quality guidelines, as follows.
{
"scripts": {
"lint": "eslint . --ext .ts",
"format": "prettier --write 'src/**/*.ts'"
}
}
Consistent coding standards also enable easier code reviews and collaboration.
Backward compatibility
When developing and updating shared libraries, it is important to ensure backward compatibility to avoid breaking existing implementations. Maintaining backward compatibility ensures that projects depending on the library do not face unexpected issues or require immediate changes when the library is updated. If breaking changes are necessary, they should be clearly documented, and a migration path should be provided to help users transition smoothly.
Following semantic versioning helps communicate the nature of changes (e.g., major, minor, patch) to the users, allowing them to manage their dependencies accordingly.
Security
Security is a critical consideration when sharing code between projects. Following best practices for security helps protect the shared libraries from vulnerabilities that could be exploited by malicious actors.
It is essential to regularly audit the code for security vulnerabilities, use automated tools like Snyk or npm audit, and address any issues promptly. Additionally, implementing secure coding practices, such as validating inputs, sanitizing outputs, and using secure dependencies, helps minimize the risk of security breaches.
Properly managing access controls and permissions for the repository and CI/CD pipelines also ensures that only authorized personnel can make changes to the shared libraries.
Conclusion
Thanks for reading this article! We have explored how sharing Angular code between projects can enhance productivity, maintain consistency, and ensure quality.
Syncfusion Angular UI components library is the only suite that you will ever need to build an app since it contains over 85 high-performance, lightweight, modular, and responsive UI components in a single package.
The new version of Essential Studio for Angular is available for existing customers on the License and Downloads page. If you’re not yet a Syncfusion customer, sign up for our 30-day free trial to explore our features.
If you have any questions, you can reach us through our support forum, support portal, or feedback portal.