Getting GluJS to play with Sencha Cmd

GluJS is an MVVM framework and optionally a Specification-Model-View-ViewModel (SMVVM) framework (BDD driven by Jasmine) that works with ExtJS (3 and 4). This post is not about GlusJS itself, rather it  is about a solution to a problem that there didn’t seem to be a solution to on the forum.

This guide gives a detailed overview of GluJS and there is also this video.

For this blog post and discussion I am using ExtJS4.2, Sencha Cmd 5, GluJS 1.2 and Jasmine 2.3.4 (for the BDD specs that are an integral part of GluJS).

First thing to say is that I am new to GluJS and have found a number of hurdles getting it going. Some of the info on the main GitHub repo is out of date. I was given a lot of help by Ryan Smith and I have forked his fork and am adding to that. On the plus side, once the obstacles have been worked through or fixed it is an amazing framework and I look forward to building a useful piece of software with it. This post is about one of those hurdles – a very important one for many of us. That is, how to be able to use GluJS and then build with Sencha Cmd. GluJS can be used in two ways:

  1. what I will call “pure” mode, where GluJS takes over the whole app starting at the ViewPort layer
  2. what I will call “mixed” mode, where you can include GluJS “modules” within an existing normal ExtJS MVC app via xtype : 'glupanel'. In other words, to “embed” GluJS into ExtJS where you want to do that (eg as a migration path to convert to a full GluJS app)

One of the goals here is to is normalise those two modes into a single architecture where the only difference is the relative amounts of MVVM and MVC. Obviously they both actually use ExtJS – it is just the architecture that varies. I explain that below. Now, I have a large existing project that needs the mixed mode approach and I also wanted to write a new “Admin Control Panel” type app in pure mode. In both cases I needed to build for production using Sencha Cmd.

Ok, enough background. This is what I did. Taking pure mode first – an app built from scratch using the GluJS approach. The working example for this blog post is in the examples folder of the repo (see AdminControlPanel folder) and the rest of what I am going to say will be a lot easier to understand if you reference that example. I will summarise the solution first before going into more detail (there are some gotchas to take care of). The post by Sharon Kong was close (thanks Sharon!). What you need to do is to wrap the GluJS functions within ExtJS classes like this:

/**
 * Root viewmodel for the App
 */
Ext.define('App.gluwrap.viewmodels.vmMain', {
    constructor: function() { 

        glu.defModel('App.glu.Main', {

            vmVPMainMenu: {
                mtype: 'VPMainMenu'
            },

            vmVPCentreComponent: {
                mtype: 'VPCentreComponent'
            }

        })

    }
})

Notice that the glu.defModel() is contained within a constructor. That was the missing piece of the puzzle (and yes, there is a little more to it than that). Each ViewModel, View, Model, Locale, etc has it’s own file as you would for ExtJS MVC. See the demo. You then “require” the files as you would for any other ExtJS class in your Application.js file:

Ext.define('App.Application', {
    name: 'App',
    extend: 'Ext.app.Application',
    requires: [
      // These need to be required so they exist for GluJS
      // to modify on initialisation
      'Ext.selection.CheckboxModel',
      'Ext.selection.RowModel',
      'Ext.grid.Panel'

      // ExtJS wrapper files containing GluJS components

      // GluJS Library files
      , 'Glu.glu-extjs-4', 'Glu.glu'

      // Your GluJS app files
      , 'App.gluwrap.locale.locale_en'

      , 'App.gluwrap.models.mUsers'
      , 'App.gluwrap.models.mUsersActivity'

      , 'App.gluwrap.viewmodels.vmMain'

      // etc
    ]
    // etc
});

Note that the Glu Library files are also wrapped in an ExtJS constructor and required here. See the demo code.

Note: this is the high level overview. I will suggest some reorganisation of this in the detail below – I am just giving you the summary.

(A word about my naming conventions: I have found it beneficial to prefix the ExtJS MVC file names with ‘v’ for a view, ‘m’ for a model, ‘c’ for a controller, etc. That way, when I have the files open in my editor I know which file is the view and which is the controller and if there are lots of files open and the tabs are abbreviated the letter is first so I can still see it. I have continued that when using glujs (vm prefix) so I can tell my viewModel from my view in the editor. Additionally, and nothing to do with this demo, I name my internal variables with the same convention. So instead of var panel = btn.up('window'); I use var vStatusWin = btn.up('vStatusWin') since ‘vStatusWin’ is also the alias (and the filename). That way, everytime I see vStatusWin in my code I know exactly what it is. Also in event handlers eg onActivateOpsManualPanel: function(vOpsManualPanel){}  )

Ok, back to the topic. You no longer need to include your GluJS files in script tags in index.html. By wrapping the Glu library and your Glu modules within ExtJS wrappers, Ext.Loader is happy to dynamically load the files just like regular ExtJS class files.

The next step is to use Ext.create() on each file
eg Ext.create('App.gluwrap.viewmodels.vmMain')
What that does is to execute the constructor that wraps around the Glu function which in turn loads the Main viewModel App.glu.Main into the App.glu namespace. From that point on Glu is happy and you can also run your Jasmine specs (see below for how).

So that is the summary. Now to deal with the detail and the issues this raises!

1. Create your new app structure using Sencha generate as normal and test that it works (default ViewPort). In order to use my demo code easily I suggest you use the namespace of App ie Sencha generate app App {path to your new app folder}

2. Download the demo files and merge them into your app folder. I have only included the relevant changed folders and files in the demo. Now you can follow along.

3. To keep it simple we will keep the GluJS and Jasmine libraries within this new app. Of course, you can put these folders anywhere in your web root if you wish. After merging the example folders you will see a BDDlib folder in the root of your new app’s workspace that contains Glu and Jasmine.

4. Edit the app.classpath property in .sencha/app/sencha.cfg to point to the location of the BDDlib folder. ie add ,${app.dir}/BDDlib to the classpath.

5. A Glu namespace and path has also been added to the Ext.Loader.setConfig() in your app.js file.

4. There is a new folder at app/gluwrap. This is where you put your Glu viewmodels and models etc as described in the summary above.

5. Have a look at the BDDlib/glu files. Notice that they are the GluJS library files wrapped in an ExtJS class and constructor. This is the same process as for your app glujs files, just with a separate namespace. The file glu-test-custommatchers.js is not wrapped since it is optional and only used in the specRunner.html file.

I wanted a consistent way to implement GluJS in both pure and mixed mode so I did the following:

1. See the files app/Application.js, app/applicationGlu.js and app.js. I wanted to keep the GluJS bits separate so they all live in applicationGlu.js. The only change to your Application.js from the Sencha generate is to change the extend:
extend: 'App.applicationGlu',
so that it includes applicationGlu.js

2. Look at app.js. Here we add a launch() function that calls this.callParent() which refers to launch() in applicationGlu.js. And, of course, the call to glu.viewport() to kick off the app.

3. Now turn to applicationGlu.js. The requires array lists all the ExtJS “native” files we need plus the ExtJS “wrapper” files containing the GluJS objects. The launch() function creates the Glu Library files then calls this.createAppGluClasses('gluwrap'); which iterates the App.gluwrap namespace (already populated by Ext.Loader via the requires array) and runs Ext.create() for each class it finds there. That gets your Glu app files into the App.glu namespace as glu objects. Finally we create some mock data via the createMockBackend() function before returning to app.js to launch the viewport.

That’s basically it for the app itself. At this point you can run index.html in your browser and you should see a working app (as simple as it currently is!). I have tested it in Chrome, FF and IE, just to be sure. All good. Activity list tab has no data – it isn’t broken, I just haven’t done that yet.

If you run Sencha app build testing (or production) it should build ok. There was a gotcha here that you should be aware of. The glu-extjs-4.js code choked Sencha Cmd because Cmd added a random semi colon to the minified glu code when it packaged it. I added a do-nothing variable to the src/providers/sencha/binder_adapter.js file which fixed that misunderstanding. That fix is in the repo and is included in the build you are using.

SpecRunner

1. See SpecRunner.html. That should be self explanatory.

2. The difference is that the specs are in appSpec.js. Open that up. You will see that it is a variant of app.js. Some differences:

  1. We extend ‘App.applicationGlu’
  2. autoCreateViewport = false so the UI doesnt start.
  3. In launch() the this.callParent() initialises the glujs in applicationGlu.js
  4. Then you include the specs directly.

Try SpecRunner.html in your browser. All should be green.

Mixed mode MVVM and MVC

I haven’t provided a complete demo of that but I have included some files in /sampleMixedMode. I used the same architecture as above so I have consistency across the projects. I am assuming you have an existing app that was generated by Sencha Cmd. We will add a simple glupanel called ‘PanelA’ in an ExtJS window.

1. Copy the BDDlib folder from the example above to an appropriate place. I have the BDDlib folder in my web root so I can share it across multiple projects.

2. Edit the app.classpath property in .sencha/app/sencha.cfg to point to the location of the BDDlib folder. eg add ,${app.dir}/../../BDDlib or similar to the classpath. Adjust your path according to where the BDDlib folder is.

3. Add the Glu namespace and path to the Ext.Loader.setConfig() in your app.js file.

4. Save a copy of the applicationGlu.js file from /sampleMixedMode/app next to your Application.js file. If you are not using namespace ‘App’ amend the root namespace in the requires array and the createAppGluClasses() function.

5. Open your existing Application.js file and:

  1. change the extend to: extend: 'App.applicationGlu',
  2. leave all your existing entries in the requires array

6. Copy the folder gluwrap from /sampleMixedMode/app to /app (ie /app/gluwrap ). This contains our sample PanelA glu files wrapped in ExtJS classes.

7. Finally, copy the file vTestingWin.js from  /sampleMixedMode/app/view to your ExtJS app/view folder (where all your ExtJS view are). Adjust the Ext.define classname to suit your app structure. Notice the standard this.items[] array contains an xtype of ‘glupanel’ with an mtype of ‘App.glu.PanelA’ which loads that viewmodel and renders the glu view to the window.

8. Wire up the test window to a button handler in your app as you normally would for any other ExtJS window (because that is exactly what it is).

That’s it. Test your app and when you click the button you should see the glu view render the html. Test build using Sencha build testing (or production). All should still work.

SpecRunner

Again, basically the same as the Admin Control Panel example above. Remember that we are only testing the GluJS components in your app, not the whole app. One of the reasons I started using GluJS was that I needed to start unit testing my ExtJS app and hadn’t done that before. My search for how to do that lead me to GluJS and since I had already decided that I liked the BDD “red to green” approach to spec driven development this was very attractive. I am thinking that it might be possible to build Jasmine tests for the non-GluJS objects in appSpec.js too but I am yet to look at that.

1. Copy the SpecRunner.hmtl file from /sampleMixedMode and adjust the paths in the script tags to your BDDlib folder

2. Copy the appSpec.js file from /sampleMixedMode and adjust the loader paths and the namespace in the spec to suit your app.

Test SpecRunner.html in your browser.

Conclusion So, I am looking forward to using GluJS more now that I have worked out how to integrate it into my normal workflow that relies upon Sencha Cmd. As an aside, the existing ExtJS MVC app that now has my test glupanel in it is packaged for desktop using nwjs. That packaging still worked without a hitch. I thought it would but one can never tell. 😉

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s