Bước 1: Tạo một extension front-end (Micro Frontends) cho SuiteCRM8
Điều đầu tiên là chọn một tên cho tiện ích mở rộng dành cho SuiteCRM8 ( Micro Frontends Angular) Tên này không được có dấu cách, cũng không phải -, cũng không được có các ký tự đặc biệt. SuiteCRM khuyên bạn nên sử dụng camel case, ví dụ: myExt cho key (tên của front-end extension) và my-ext cho tên tệp và đường dẫn.
Lưu ý: khuyến nghị này về việc có một hệ thống chữ hoa chữ thường khác cho các tệp, chỉ là để tránh sự cố trong các hệ thống phân biệt chữ hoa chữ thường.
Bước 2: Setup cho extension của bạn vào hệ thống SuiteCRM8
2.1. Enable module federation
ng add @angular-architects/module-federation --project=myExt
2.2. Chỉnh sửa lại file extensions/my-ext/app/webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
output: {
publicPath: 'auto',
uniqueName: 'myExt'
},
optimization: {
runtimeChunk: false
},
plugins: [
new ModuleFederationPlugin({
name: 'myExt',
filename: 'remoteEntry.js',
library: {
type: 'window',
name: 'myExt',
},
exposes: {
'./Module': './extensions/my-ext/app/src/extension/extension.module.ts'
},
shared: {
'@angular/core': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/common': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/common/http': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/router': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/animations': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/cdk': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@angular/forms': {
singleton: true,
requiredVersion: '^12.0.0'
},
'@apollo/client': {
singleton: true,
requiredVersion: '^3.3.7'
},
'@apollo/link-error': {
singleton: true,
requiredVersion: '^2.0.0-beta.3'
},
'angular-svg-icon': {
singleton: true,
requiredVersion: '^12.0.0'
},
'apollo-angular': {
singleton: true,
requiredVersion: '^2.2.0'
},
graphql: {
singleton: true,
requiredVersion: '^14.7.0'
},
'graphql-tag': {
singleton: true,
requiredVersion: '^2.11.0'
},
'lodash-es': {
singleton: true,
requiredVersion: '^4.17.20'
},
luxon: {
singleton: true,
requiredVersion: '^1.25.0'
},
'ng-animate': {
singleton: true,
requiredVersion: '^1.0.0'
},
'ngx-chips': {
singleton: true,
requiredVersion: '^2.2.2'
},
'@swimlane/ngx-charts': {
singleton: true,
requiredVersion: '^17.0.0'
},
'@ng-bootstrap/ng-bootstrap': {
singleton: true,
requiredVersion: '^9.0.2'
},
'bn-ng-idle': {
singleton: true,
requiredVersion: '^1.0.1'
},
common: {
singleton: true,
import: 'dist/common',
requiredVersion: false
},
core: {
singleton: true,
import: 'dist/core',
requiredVersion: false
},
}
}),
],
};
2.3. Cấu hình share module
Thay đổi share module của file webpack.config.js ở trrên từ file core/app/shell/webpack.config.js vì file này sẽ là file cập nhập mới nhất và chính xác với version của bạn hơn
Bước 3 - Chỉnh sửa file angular.json
3.1. Cấu hình lại outputPath
Trong file angular.json tìm myExt > architect >
build >
options và thay đổi giá trị
outputPath là public/extensions/my-ext
3.2. Cấu hình dev build
Trong file angular.json tìm myExt > architect >
build >
options
thêm đoạn dưới
"namedChunks": true,
"sourceMap": true,
"aot": true,
Trong file angular.json tìm myExt > architect >
build >
configurations tìm và xoá
development
3.2. Cấu hình prod build
Trong file angular.json tìm myExt > architect >
build >
configurations >
production tìm và thay đổi
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
],
Sau các bước trên sẽ có sự thay đổi cấu hình myExt trong file angular.json như sau
"myExt": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "extensions/my-ext/app",
"sourceRoot": "extensions/my-ext/app/src",
"prefix": "app",
"architect": {
"build": {
"builder": "ngx-build-plus:browser",
"options": {
"namedChunks": true,
"commonChunk": false,
"sourceMap": true,
"aot": true,
"outputPath": "public/extensions/my-ext",
"index": "extensions/my-ext/app/src/index.html",
"main": "extensions/my-ext/app/src/main.ts",
"polyfills": "extensions/my-ext/app/src/polyfills.ts",
"tsConfig": "extensions/my-ext/app/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"extensions/my-ext/app/src/favicon.ico",
"extensions/my-ext/app/src/assets"
],
"styles": [
"extensions/my-ext/app/src/styles.scss"
],
"scripts": [],
"extraWebpackConfig": "extensions/my-ext/app/webpack.config.js"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "extensions/my-ext/app/src/environments/environment.ts",
"with": "extensions/my-ext/app/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
],
"extraWebpackConfig": "extensions/my-ext/app/webpack.prod.config.js"
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "ngx-build-plus:dev-server",
"configurations": {
"production": {
"browserTarget": "myExt:build:production",
"extraWebpackConfig": "extensions/my-ext/app/webpack.prod.config.js"
},
"development": {
"browserTarget": "myExt:build:development"
}
},
"defaultConfiguration": "development",
"options": {
"extraWebpackConfig": "extensions/my-ext/app/webpack.config.js",
"port": 3333
}
},
"extract-i18n": {
"builder": "ngx-build-plus:extract-i18n",
"options": {
"browserTarget": "myExt:build",
"extraWebpackConfig": "extensions/my-ext/app/webpack.config.js"
}
},
"test": {
"builder": "ngx-build-plus:karma",
"options": {
"main": "extensions/my-ext/app/src/test.ts",
"polyfills": "extensions/my-ext/app/src/polyfills.ts",
"tsConfig": "extensions/my-ext/app/tsconfig.spec.json",
"karmaConfig": "extensions/my-ext/app/karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"extensions/my-ext/app/src/favicon.ico",
"extensions/my-ext/app/src/assets"
],
"styles": [
"extensions/my-ext/app/src/styles.scss"
],
"scripts": [],
"extraWebpackConfig": "extensions/my-ext/app/webpack.config.js"
}
}
}
}
Bước 4: Thêm cấu hình lệnh build command
Thêm lần lượt lệnh dev build và prod build vào file package.json
build-dev:myExt": "ng build myExt
....
build:myExt": "ng build myExt --configuration production
Bước 5: Thêm extension của mình vào trong shell app
5.1. Thông qua entrypoint được chuẩn bị sẵn bởi SuiteCRM8, bạn có thể khai báo extension của mình vào file extensions/my-ext/app/webpack.config.js
exposes: {
'./Module': './extensions/my-ext/app/src/extension/extension.module.ts'
},
Như vậy extension của bạn đã được include vào main của ứng dụng shell của SuiteCRM8
5.2. Tạo file extensions/my-ext/app/src/extension/extension.module.ts
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule,
],
})
export class ExtensionModule {
constructor() {
console.log('Dynamic extension myExt!');
}
init(): void {
}
}
5.3. Thêm module extension vào trong extension app trong thư mục extension của minh
Tại file /extensions/my-ext/app/src/app/app.module.ts, thay đổi như sau
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app.component';
import {ExtensionModule} from '../extension/extension.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ExtensionModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
Bước 6: Enable Extension
Tạo file extensions/my-ext/config/extension.php
<?php
use Symfony\Component\DependencyInjection\Container;
if (!isset($container)) {
return;
}
/** @var Container $container */
$extensions = $container->getParameter('extensions') ?? [];
$extensions['myExt'] = [
'remoteEntry' => './extensions/my-ext/remoteEntry.js',
'remoteName' => 'myExt',
'enabled' => true
];
$container->setParameter('extensions', $extensions);
Bước 7 : Build lại core SuiteCRM8 và extension của minh
Lần lượt chạy các lệnh dev build sau
yarn run build-dev:common
yarn run build:core
yarn run build-dev:shell
yarn run build-dev:myExt --watch
refesh lại để check console 'Dynamic extension myExt!'
Nguồn: https://docs.suitecrm.com/8.x/developer/front-end-architecture/fe-extensions-setup