Creating A Module

Introduction

DCP stands out as a versatile solution that caters to diverse developer preferences and requirements. On the frontend, DCP holds the capability to seamlessly integrate with a wide array of commonly used frontend frameworks, empowering developers to leverage their preferred tools and technologies to build dynamic and engaging user interfaces. Meanwhile, in the backend, DCP provides developers with the freedom to select their preferred backend language for creating microservices and APIs. With DCP, developers have the flexibility to utilize the language best suited to their expertise and project requirements, ensuring seamless integration and efficient functionality across their backend infrastructure.

Backend

Frontend

VueJS

Here are some of the steps to create new VueJS application, to be used inside DCP ecosystem.

Create Vue application

For this step you can copy existing project or use Vue CLI to create a new one.

Create Environment files

Inside your .env file add

STANDALONE_SINGLE_SPA=true
VUE_APP_IS_APP_NAME=SAW
VUE_APP_IS_APP_VALIDATED=false
VUE_APP_API_ENV=dev
VUE_APP_API_URL=false
VUE_APP_I18N_LOCALE=en
VUE_APP_I18N_FALLBACK_LOCALE=en

Create environment files for all necessary environments by copying .env file, and changing VUE_APP_API_ENV.

.env.dev - VUE_APP_API_ENV=dev
.env.stage - VUE_APP_API_ENV=stage
.env.test - VUE_APP_API_ENV=test
.env.standalone - VUE_APP_API_ENV=dev
.env.prod - VUE_APP_API_ENV=prod

Later in the process, will use that VUE_APP_API_ENV to populate correct VUE_APP_API_URL.

Change application port

Add new line inside vue.config.js to give your application port that is not used by other Vue or Angular application. For Vue applications ports starting with 80 like 8081 are used by convention. Check other applications and add a port number that is not used yet.

module.exports = defineConfig({
    devServer: { port: 8082, https: true},  
})

Add Single Spa settings

If your application use Vue CLI you have one step, that should do all:

vue add single-spa

If your application does not use Vue CLI you need to do run these commands:

npm i --save single-spa-vue
npm i --save systemjs-webpack-interop

Create a file at the same level as your main.js/ts called set-public-path.js

import { setPublicPath } from 'systemjs-webpack-interop';
setPublicPath('applicationName');

In main.ts file add this lines

import { h, createApp } from 'vue';
import './set-public-path.js';
import singleSpaVue from 'single-spa-vue';

// Only for examples
import router from './router';
import i18n from './i18n';

const vueLifecycles = singleSpaVue({
    createApp,
    appOptions: {
        i18n,
        render() {
            return h(App, {
                // single-spa props are available on the "this" object. Forward them to your component as needed.
                // https://single-spa.js.org/docs/building-applications#lifecycle-props
                // if you uncomment these, remember to add matching prop definitions for them in your App.vue file.
                /*
                name: this.name,
                mountParcel: this.mountParcel,
                singleSpa: this.singleSpa,
                */
            });
        },
    },
    handleInstance(app) {
        app.use(i18n);
        app.use(router);
        // And all other library, that you will use
    },
});

export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;

More information here

Import Git Submodules

Inside Vue applications some of DCP Libraries. to be imported as a Git Submodules

DcShared

Run git submodule add ../../Modules/dc-shared.git src/libs/dc-shared.

From here we can use DcpSync Service Base class and model. Also, we have some other models and enums like MessagesTypeEnum.

DcAssets

Run git submodule add ../../Modules/dc-assets.git src/assets.

Inside we have vue.styles.scss reusable styles.

DcUser

Run git submodule add ../../Modules/dc-user.git src/libs/dc-user

From where we use models like UserDateFormatSettings, UserSettingsModel and UserTimeFormatSettings.

DcCore

Run git submodule add ../../Modules/dc-core.git src/libs/dc-core

Inside vue.config.js add this line process.env.VUE_APP_API_URL = require('./src/libs/dc-core/src/core/api.json').saw[process.env.VUE_APP_API_ENV];

DcSharedVue

TODO: This one needs to be extracted from SAW application, after we make some refactoring in existing shared folder.

The idea is to have ready to use Vue components, services, composable, enums etc. that can be shared between VueJS applications like DcShared is shared between Angular applications.

Additional libraries setup

Because Dc Libraries contains a lot ot Angular specific code, this will break the VueJS build.

To not be affected by this code, during build, we need to make additional setup inside tsconfig.json.

{
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx",
    "src/libs/**/vue/*"
  ],
  "exclude": [
    "node_modules",
    "src/libs/**/*"
  ]
}

The new line in exclude section "src/libs/**/*" will exclude from build all code from Dc Libraries.

The new line in include section "src/libs/**/vue/*" will include all code, that we specifically set inside some Dc Libraries /vue folders if there is.

Use same styles and UI libraries

One of the main ideas in DCP is the UI to look similar in all applications.

To achieve that we use in all Angular applications Material Icons, Kendo UI for Angular, Angular Material and Bootstrap 4.6.0.

Fallowing the same path, for Vue applications we use Material Icons and Material Design Icons, Kendo UI for Vue, Vuetify (as an alternative to Angular Material) and Bootstrap 4.6.0.

Note: Version of Bootstrap is important, because styles are global and can affect existing applications.

Material Icons

Run npm i --save material-icons

@import "~material-icons/iconfont/material-icons.css"; is already inside DcShared vue.styles.scss

Material Design Icons

Run npm i --save @mdi/font

This one will be set as a default icon set to Vuetify later.

Boostrap

Run npm i --save [email protected]

@import "~bootstrap/scss/bootstrap"; is already inside DcShared vue.styles.scss

Vuetify

TODO

Kendo UI for Vue

More information here.

Inside App.vue add

<style lang="scss" scoped>  
    @import "~@progress/kendo-theme-material/dist/all.css";  
</style>

Next thing that we need to do is to be sure that the Kendo UI styles that we import will not affect other applications.

The problem is that Kendo use same component classes for both VueJS and Angular, but because of different HTML structure, classes with same names, have different implementations.

To achieve that we will add additional step to all build processes later. Now we will add npm packages that we need and create gulpfile.js file

  1. Run npm install --save-dev gulp
  2. Run npm install --save-dev gulp-replace
  3. Create gulpfile.js file to your root directory
const gulp = require('gulp');
const replace = require('gulp-replace');

gulp.task('replaceKendoCss', function () {
    return gulp.src(['./dist/js/app.js', './dist/js/app.js.map'])
        .pipe(replace(' k--', ' revert--'))
        .pipe(replace('k-icon', 'revert-icon'))
        .pipe(replace('.k-', '.dcp-vue-k-'))
        .pipe(replace(' k-', ' dcp-vue-k-'))
        .pipe(replace('\'k-', '\'dcp-vue-k-'))
        .pipe(replace('"k-', '"dcp-vue-k-'))
        .pipe(replace(' revert--', ' k--'))
        .pipe(replace('revert-icon', 'k-icon'))
        .pipe(gulp.dest('./dist/js/'));
});

This way we will change all Kendo UI for Vue class names from .k- to .dcp-vue-k-, and class names will be different from the one in Kendo UI for Angular. To not affect some code like .k-icon or for (let k; k < 12; k--) we make revert process for it.

Create build processes

During build, we need to be sure, that we get the latest code from current environment branches of all Dc Libraries.

Inside package.json add build processes for different environments.

{
  "scripts": {
    "build:dev": "npm run getLatestSubmodules:dev && vue-cli-service build --mode dev && npm run replaceKendoCss",
    "build:prod": "npm run getLatestSubmodules:main && vue-cli-service build --mode prod && npm run replaceKendoCss",
    "build:stage": "npm run getLatestSubmodules:stage && vue-cli-service build --mode stage && npm run replaceKendoCss",
    "build:test": "npm run getLatestSubmodules:test && vue-cli-service build --mode test && npm run replaceKendoCss",
    "getLatestSubmodules:dev": "git submodule foreach git pull origin develop --allow-unrelated-histories",
    "getLatestSubmodules:test": "git submodule foreach git pull origin test --allow-unrelated-histories",
    "getLatestSubmodules:stage": "git submodule foreach git pull origin stage --allow-unrelated-histories",
    "getLatestSubmodules:main": "git submodule foreach git pull origin main --allow-unrelated-histories",
    "replaceKendoCss": "gulp replaceKendoCss"
  }
}

Local development

For easy change between branches during development we will add npm run checkout:BRANCH commands, that will help us to not change all libraries one by one.

Also, we will add command npm run start:spa that will be used to build the application for Import map overrides.

{
  "scripts": {
    "checkout:dev": "git checkout develop && git submodule foreach git checkout develop",
    "checkout:main": "git checkout main && git submodule foreach git checkout main",
    "checkout:stage": "git checkout stage && git submodule foreach git checkout stage",
    "checkout:test": "git checkout test && git submodule foreach git checkout test",
    "start": "vue-cli-service serve --mode standalone",
    "start:spa": "vue-cli-service serve"
  }
}

Register a new App

To register new application in Base App three steps are required

Add application to SystemJS Importmap

<script type="systemjs-importmap">
  {
    "imports": {
      <!--...-->
      "@dcp/[applicationName]": "https://dcp-[gxpOrNonGxp].domain.com/[applicationBuildFolder]/[applicationMainFile]?version=1"
      <!--...-->
    }
  }
</script>
  • [applicationName] - name of the application with camelCase that will be easy to understand when is in use with import map override for local development.
  • [gxpOrNonGxp] - this part of the path is based on is app validated or not, when it's new one should be nongxp. Once the app is validated deploy path will be changed to gxp.
  • [applicationBuildFolder] - path to a deployed application like /app-mvda/ or /app-login/. This is setup inside .gitlab-ci.yml file for the application.
  • [applicationMainFile] - for Angular applications this will main.js, and for VueJS applications app.js.

Add application loader

<!--index.html-->
<script>
    /*...*/
    const routes = constructRoutes(
        document.querySelector("#single-spa-layout"),
        {
            loaders: {
                /*...*/
                "[applicationLoader]": createLoaderHtml('[ApplicationName]'),
            }
        }
    );
    /*...*/
</script>
  • [applicationLoader] - key to be used inside Single Spa Template
  • [ApplicationName] - human-readable application name (e.g. Product and Process Monitoring, Administration), that will be shown to users during loading.

Add application to Single Spa Template

Here we have three cases

  1. When application contains top navigation and side menu inside. This is the case with existed Angular applications.
  2. When application will use Top Navigation, and Side Menu Applications, and will load only pages, similar to SAW Applications.
  3. When application need to show shareable content outside of login scope.
<!--index.html-->
<head>
    
<template id="single-spa-layout">
    <single-spa-router>
        <route path="/app/:siteId">
            
            <route path="/[applicationPath]">
                <!-- We load here applications that contains top navigation and side menu inside -->
                <application name="@dcp/[applicationName]" loader="[applicationLoader]"></application>
            </route>
            
            <route path="/menu">
                <div class="wrapper">
                    <nav class="side-menu">
                        <application name="@dcp/side-menu"></application>
                    </nav>
                    <header class="top-navigation">
                        <application name="@dcp/top-navigation"></application>
                    </header>
                    <main class="main-content dcp-app-wrapper">
                        <route path="/[applicationPath]">
                            <!-- We load here applications that will use  Top Navigation, and Side Menu Applications -->
                            <application name="@dcp/[applicationName]" loader="[applicationLoader]"></application>
                        </route>
                    </main>
                </div>
            </route>
        </route>

        <route path="/[applicationShareablePath]">
            <!-- We load here application content that need to be visible for users that are not logged in -->
            <application name="@dcp/[applicationName]" loader="[applicationLoader]"></application>
        </route>

    </single-spa-router>
</template>
    
</head>
  • [applicationPath] - URL path that should load application. E.g. MVDA application use mvda, Administration use administration. This is setup inside application router.
  • [applicationShareablePath] - URL path that should load application for non logged users. This is setup inside application router.
  • [applicationLoader] - loader key created during previous step.
This page was last edited on 03 May 2024, 08:19 (UTC).