April 26, 2020
Estimated Post Reading Time ~

Requirejs and AEM 6. Kiss.

Following the KISS principle, let's get requirejs working inside AEM.

Getting Started with Requirejs and AEM
Setup
Create project using maven archetype

Instructions for doing this can be found at the docs.adobe.com

Simplified approach
Using Eclipse (Luna)
  • Click New -> Maven Project
  • Ensure Create a simple project (skip archetype selection) is not checked then click Next
  • Click Configure -> Add Remote Catalog. Enter http://repo.adobe.com/nexus/content/groups/public/ in the Catalog File Textbox. Put any name for Description (i.e. Adobe). Click OK
  • Select the newly created Catalog Item in the dropdown. In the main box you should now see a few options. If you do not see aem-project-archetype under Artifact Id, then an error has occurred.
  • Select aem-project-archetype (I'm using version 10) -> Click Next
  • Here we need to specify some properties, remember kiss...
Group Id                       mygroup
Artifact Id                      myartifact
artifactName                  artifact
packageGroup                package
appsFolderName             apps
contentFolderName         content
cssId                              cssId
componentGroupName    componentGroup
siteName                        site
  • Click Finish
Okay, so now we have made it through creating the maven archetype

Getting Requirejs
Download Requirejs here

Setup Requirejs
  1. Copy folder clientlib-all (located at: /ui.apps/target/classes/etc/designs/apps/clientlib-all)
  2. Paste to /ui.apps/target/classes/etc/designs/apps and Rename to clientlib-vendor
  3. Create folder "js" under clientlib-vendor
  4. Create folder "vendor" under js
  5. Modify .content.xml file
categories="[cssId.vendor]" dependencies="[jquery]"

Put the requirejs file into clientlib-vendor/js/vendor (mine is named require.js) I am using version 2.1.17

Setup jQuery
Create a file named require-configuration.js under clientlib-vendor/js
In AEM, jQuery ($) is defined globally, but requirejs tries to minimize things in the global namespace. We will need to make jQuery a module to be used inside of requirejs modules.
(function (){
            'use strict';
            define(
                'jquery',
                [],
                function() {
                    return jQuery;
            });
        }());
For more information about this go here

Wire Up Clientlibs
Create a js.txt file under clientlib-vendor
Add 
#base=js 
vendor/require.js 
require-configuration.js

At this point your vendor client lib structure should look like this:
Ignore the ember.js files, they are not needed for this tutorial

Setup AEM
In headlibs.html under /apps/apps/components/structure/page/partials
Add
<sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"
    data-sly-call="${clientLib.js @ categories='cssId.vendor'}" data-sly-unwrap/>

Simple Test
  • Create a component under /apps/components/content (I named it testcomponent)
  • Create a clientlib folder and a js.txt file inside the newly created component
Inside js.txt Add 
#base=js 
test.js
  • Create a test.js file under the clientlib folder
  • Create/Modify .context.xml file under the clientlib folder
Add
categories="[cssId.test]"
  • Create/Modify testcomponent.html
Add
<h3 id="test-me">test component</h3>
This is what your file structure should look like:

  • Modify .content.xml under /ui.content/src/main/content/jcr_root/content/content/en (This is under the ui.content project)
Add
<testcomponent
    jcr:primaryType="nt:unstructured"
    sling:resourceType="apps/components/content/testcomponent"/>

I put this in between servicecomponent and title_1 but it doesn't really matter where you put it.
  • Modify .content.xml file under /ui.apps/src/main/content/jcr_root/etc/designs/ (This is in the ui.apps project)
Add cssId.test like this:
embed="[cssId.site,cssId.page,cssId.topnav,cssId.colctrl,cssId.textimage,cssId.test]"
  • Modify the test.js file under /apps/components/content/testcomponent/clientlib/js
Add//test to make sure require and jquery are working together //test to make sure require and jquery are working together

require(['jquery'], function( $ ) {
    console.log( $('#test-me').html() ); // prints test component
});
  • This will ensure we have jquery working with requirejs.
Again, modify the test.js file under /apps/components/content/testcomponent/clientlib/js

    define('Spaceship', ['jquery', 'module'], function($, module) {
     var Spaceship = function() {
      //constructor
      var me = this;
      var equipment = {};
      equipment.weapon = (require.config.Spaceship != undefined) ? require.config.Spaceship.defaults.weapon : 'none';
      this.getEquipment = function() {
       return equipment;
      };
     };
     Spaceship.prototype.shoot = function() {
      var me = this;
      var equipment = this.getEquipment();
      if (equipment.weapon === 'none') {
       console.log('Retreat!');
      } else {
       console.log(equipment.weapon + ' fires');
      }
     };
     return Spaceship;
    });

    require(['Spaceship'], function(Spaceship) {
     require.config = {
      'Spaceship': {
       defaults: {
        weapon: 'Laser Cannon'
       }
      }
     };
     var warship = new Spaceship();
     warship.shoot();
     require.config = {
      'Spaceship': {
       defaults: {
        weapon: 'none'
       }
      }
     };
     var cargoship = new Spaceship();
     cargoship.shoot();
    });
  • Now wrap everything inside test.js with
(function() {
 //8. & 9. goes here
})();

Putting it all together

We created a project using a maven archetype. This created a sightly ready aem project.

When setting up Require js, we created a client lib to hold the require js files inside /etc... We defined jQuery as a Require js module in the require-configuration.js file which gets loaded immediately after the initial Require js file. This does not eliminate jQuery or $ from the global namespace, as would cause problems within AEM. We then "sightly included" theses files into the headlibs.html. Typically, js is loaded at the bottom but this is a simplified approach. It is possible to put the Require js files at the bottom but then any Require js modules would need deferred until the page is completely rendered or until Require js is loaded.

For the Simple Test, we created a test component and added the clientLibs folder and test.js file. Inside this file, we added the testing criteria. In this test, all of the Require modules are in the same file. Usually, each Require module has its own file. This is where AEM client libs show its usefulness: by adding all the Require js module files into the js.txt and by using embed we can add all the client libraries, that are inside the component, to the main clientlib (cssId.all in this case which is loaded at the bottom of the page). So if we have many components, and they all have the category of cssId.test, they all will be appended to the main client file. No build tool required.

Taking a closer look at the Simple Test, there are a few things visible: a module name Spaceship is defined; the Spaceship module is required; require.config is used to set attributes for the Spaceship. Require.config can be useful if dealing with only one javascript object. In this case, using it for multiple objects may not be a best practice, but it can be seen as how it acts as a global namespace for modules.

If you don't want to go through this whole process. I put my working copy on github at https://github.com/kuckmc01/aem-6-base. There is no guarantee that this working project will work by the time you see it.


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.