Emberjs.adaptTo(AEM)

Emberjs.adaptTo(AEM)

Emberjs and AEM

Emberjs is a popular framework for ambitious web applications. It can be used to create Single Page Applications or SPA. Emberjs can be used with AEM.

Here is a simple approach to getting Emberjs working inside AEM.

Getting Started

Follow the tutorial from RequireJS and AEM 6. KISS. If you don't want to go through the complete Requirejs tutorial is is okay to just do the maven archetype portion, but will make this tutorial slightly more difficult to follow. If you do not want to follow the tutorial, checkout out github. Note: the github version is a working project and may not work by the time you view it.

Getting Emberjs

Download Emberjs and Template Compiler (I used debug version 1.11.3)

Setup Emberjs

Under etc/designs/apps create folder structure and files here

I could have put the ember files in the same clientlib-vendor that contained the require js files but I made them independent, so they could be loaded at different times.

Inside .content.xml add/change

    categories="[cssId.ember]"
    dependencies="[jquery]"

Inside js.txt add/change

#base=js

vendor/ember.debug.js
#used for client side compilation
vendor/ember-template-compiler.js

Add Some Content

Upload two images into the dam. I used an image of the Enterprise and the Millennium Falcon. This is the paths created by the dam for each file: * /content/dam/content/MFalcon.jpg * /content/dam/content/Enterprise.jpg

vlt down these files and add the folder containing these images to ui.content/src/main/content/jcr_root/content/dam/content

These assets will now be available when we build the project.

Setup AEM

Component

Created new component named embercomponent (I copied testcomponent [from require js tutorial] and renamed) Created folder structure and files

Inside .content.xml add/change

categories="[cssId.test]

Inside js.txt add/change

#base=js
ember-test.js

app.js


App.js

This contains the Ember app.

Add the following code:


//shared variable between spaceships/spaceship routes
var spaceships;

App = Ember.Application.create({
    rootElement: '#ember-container'
});

App.Router.map(function() {
    // put your routes here
    this.resource('spaceships', function(){
        this.resource('spaceship', {path: ':spaceship_id'});
    });
});

App.IndexRoute = Ember.Route.extend({
    model: function() {
        return '';
    }
});
App.SpaceshipsRoute = Ember.Route.extend({
    model: function() {
        //get content from backend
        var path = CQ.HTTP.noCaching(CQ.HTTP.getPath()+'/jcr:content/par/embercomponent/jcr:content/json.json');
        var jcrContent = CQ.shared.HTTP.eval(path);
        spaceships = JSON.parse(jcrContent.json);

        return spaceships;
    }
});
App.SpaceshipRoute = Ember.Route.extend({
    model: function(params) {
        return spaceships.findBy('id', params.spaceship_id);
    }
});
App.SpaceshipController = Ember.Controller.extend({
    isEditing: false,
    isEditable: (CQ.WCM.getMode() == 'edit'),
    actions: {
        edit: function() {
            this.set('isEditing', true);
        },
        doneEditing: function(){
            this.set('isEditing',false);
            //update backend
            var path = CQ.HTTP.getPath()+'/jcr:content/par/embercomponent/jcr:content';
            var params = {'json':JSON.stringify(spaceships)};
            var serverResponse = CQ.utils.HTTP.post(path,null,params,this);
            if(CQ.utils.HTTP.isOk(serverResponse)){
                CQ.Notification.notify('Update','Ok',.5,true);
            }else{
                CQ.Notification.notify('Update','Failed',.5,true);
            }
        }
    }
});

embercomponent.html

This contains the templates. For now, will just be using client side templating. This method is the slowest but the easiest to get started.


<div class="container-fluid" id="ember-container"></div>

    <script type="text/x-handlebars">
        <div style="margin-bottom:15px;">
            <ul class="nav nav-tabs">
                <li role="presentation">Home</li>
                <li role="presentation">Spaceships</li>
            </ul>
        </div>


    </script>

    <script type="text/x-handlebars" data-template-name="index">
        <div class="jumbotron">
            <h1>Spaceship Collection</h1>
            <p>A collection of spaceships.</p>
            <p>See Collection</p>
        </div>
    </script>

    <script type="text/x-handlebars" data-template-name="spaceships">
        <div class="row">
            <div class="col-xs-6 col-md-6">
                <ul class="list-group">

                    <li class="list-group-item">
                        <p>

                                <h3> : </h3>

                        </p>
                    </li>

                </ul>
            </div>
            <div class="col-xs-6 col-md-6">

            </div>
        </div>

    </script>

    <script type="text/x-handlebars" data-template-name="spaceship">



            <button >Done</button>

            <button >Edit</button>



        <div class="thumbnail">
            <img src="" alt="">
            <div class="caption">
                <h3></h3>
                <h5></h5>
                <p></p>
                <p>Weapon systems</p>
                <ul>

                    <li><p></p></li>

                </ul>
            </div>
        </div>
    </script>

    <script type="text/x-handlebars" data-template-name="spaceship/_edit">
        <p></p>
        <p></p>
        <p></p>
    </script>

Wire it up

Add this below the line that contains cssId.vendor (from requirejs tutorial) in headlibs.html

<sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html" data-sly-call="${clientLib.js @ categories='cssId.ember'}" data-sly-unwrap/>

Under /ui.content/src/main/content/jcr_root/content/content/en/.content.xml

Add the following after </testcomponent> (if you didn't follow the requirejs tutorial this will not be there)

<embercomponent
    jcr:primaryType="nt:unstructured"
    sling:resourceType="apps/components/content/embercomponent">
        <jcr:content
            jcr:primaryType="nt:unstructured"
            jcr:title="ember-component"
            json="\[{&#xa;        &quot;id&quot;: &quot;1&quot;,&#xa;        &quot;title&quot;: &quot;Enterprise&quot;,&#xa;        &quot;series&quot;: &quot;Star Trek&quot;,&#xa;        &quot;description&quot;: &quot;The Starship Enterprise is a spaceship from the Star Trek Series&quot;,&#xa;        &quot;weapons&quot;: [&quot;photon torpedos&quot;,&quot;lasers&quot;],&#xa;        &quot;image&quot;: &quot;/content/dam/content/Enterprise.jpg&quot;&#xa;        },&#xa;        {&#xa;        &quot;id&quot;:&quot;2&quot;,&#xa;        &quot;title&quot;: &quot;Millenium Falcon&quot;,&#xa;        &quot;series&quot;: &quot;Star Wars&quot;,&#xa;        &quot;description&quot;: &quot;The Millenium Falcon is a spaceshp from the Star Wars series&quot;,&#xa;        &quot;weapons&quot;: [&quot;quad laser cannon&quot;],&#xa;        &quot;image&quot;: &quot;/content/dam/content/MFalcon.jpg&quot;&#xa;}
            ]">
        </jcr:content>
</embercomponent>

The json section might look a little bit odd, but AEM xml requires json to be HTML delimited. The way I got this was to add everything from above (except the json property), build the project. I then went to CRXDE lite, added the json property with the json content (under /content/content/en/jcr:content/par/embercomponent/jcr:content) and saved it. I then vlt (vaulted) down the result and put it into the xml file.

Build and open http://localhost:4502/content/content/en.html

Putting it all together

We followed the Requirejs and AEM. KISS. Tutorial, created a component to contain the Ember app, created the clientlibs for Ember, created uploaded some images into the DAM to be used in the Ember app, and added a starting point for the content to be added to the en page.

In the embercomponent.html, we had our templates. Notice, that the id in the first div is 'ember-container' and it how we attached the app.js file to the HTML.

In the app.js, we have the typical ember app (combined into one file), but we added some AEM specifics into this.

In App.SpaceshipsRoute, we added json to be fetched when the page loads. In App.SpaceshipController, the done editing section is how we save items directly into the backend. Here we also added a way for editing to only be available if AEM is in edit mode.

Conclusion

It is possible to adapt Emberjs to AEM for Single Page Applications or other applications. It could be used to edit the pages similar to what already exists within AEM, but without using dialogs or inline editing. The really awesome part is the fact that these updates are displayed on the page automatically without a page refresh and are saved automatically.

Share this post

0 Comments

comments powered by Disqus