Nhảy đến nội dung

Hướng dẫn tạo Front-End Extension Module trong SuiteCRM8

Nội Dung Bài Viết

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

Check Link Git tại đây