We have created a JavaScript micro-frontend in VueJS to handle a new feature for our website. The JavaScript micro-frontend is set up nicely inside of a maven module. Along with it’s own Node Package Manager configuration, we used the aem-clientlib-generator to generate a client library under /apps/sourcedcode/clientlibs/micro-frontend.
Within our VueJS application, we have a utility named i18n.js, the file looks like this (code below). Throughout application, we will call this util i18n(function) to get values from our i18n AEM dictionary.
1 2 3 4 5 6 7 8 | export default { i18n(id, params, notes) { if (window.Granite?.I18n) { return window.Granite.I18n.get(id, params, notes); } return id; } }; |
When we test out the micro-frontend, we noticed that sometimes the window.Granite, AEM global object, is intermittently working; sometimes the window object is valid/not, so dictionary values are loaded, and sometimes they are not. Why window.Granite is sometimes undefined? Well, this looks like a race condition issue.
In short, a race condition occurs when a device or system attempts to do two or more actions at the same time, but the actions must be performed in the proper sequence to be performed correctly.
How can we ensure window.Granite is loaded first before our application is loaded next? In the next section let’s look at the different ways to see how we can do this.
Solutions
- Manually Invoking the i18n.js from /libs/clientlibs/granite/utils/source/I18n.js
- Include the granite.utils as a dependency
Solutions
In this section of the blog, you will find two solutions to this problem.
1. Manually Invoking the i18n.js from /libs/clientlibs/granite/utils/source/I18n.js
From the page template that your micro-frontend is embedded, you can just call the Adobe core Granite utils > i18n.js before your custom code, like the code provided below. Viewing the source code of your HTML page should look something like this; where the i18n.js is loaded first, and then your application is loaded secondly. Using the proxy method for accessing client libraries, you can serve /libs/clientlibs/granite/utils/source/I18n.js with /etc.clientlibs/clientlibs/granite/utils.js.
1 2 | <script type="text/javascript" src="/etc.clientlibs/clientlibs/granite/utils.js"></script> <script type="text/javascript" src="/etc.clientlibs/sourcedcode/clientlibs/micro-frontend.min.js"></script> |
2. Include the granite.utils as a dependency
.
Simply ensure that your client library has a dependency for the granite.utils.
In line:6, notice how the a dependency have been added.
1 2 3 4 5 6 | <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:ClientLibraryFolder" allowProxy="{Boolean}true" categories="[sourcedcode.micro-frontend]" dependencies="[granite.utils]"/> |
While in this article, we have mentioned that we are using the aem-clientlib-generator for our micro-frontend, so to install it, it would be as simple as adding a new property in the clientlib.config.js, and would be something that looks like this: line:33
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | const path = require('path'); const BUILD_DIR = path.join(__dirname, 'dist'); const CLIENTLIB_DIR = path.join( __dirname, '..', 'ui.apps', 'src', 'main', 'content', 'jcr_root', 'apps', 'sourcedcode', 'clientlibs' ); const libsBaseConfig = { allowProxy: true, serializationFormat: 'xml', cssProcessor: ['default:none', 'min:none'], jsProcessor: ['default:none', 'min:none'] }; // Config for `aem-clientlib-generator` module.exports = { context: BUILD_DIR, clientLibRoot: CLIENTLIB_DIR, libs: [ { ...libsBaseConfig, name: 'micro-frontend', categories: ['sourcedcode.micro-frontend'], dependencies: ['granite.utils'], assets: { js: { cwd: 'clientlib-site', files: ['**/*.js'], flatten: false }, css: { cwd: 'clientlib-site', files: ['**/*.css'], flatten: false }, // Copy all other files into the `resources` ClientLib directory resources: { cwd: 'clientlib-site', files: ['**/*.*'], flatten: false, ignore: ['**/*.js', '**/*.css'] } } } ] }; |
Sometimes after deploying new dictionary keys and values in your AEM platform, you will realize, in rare occasions, you’re dictionary values are not loading as expected. Have no fear, I have created an article here for how to solve this problem. Click here to learn how to refresh the Apache Sling I18N translation engine.