Universal Windows Apps for Microsoft Surface Pro Hybrids / MS Edge with Ext JS 6

Posted on in Ext JS 6

Now that hybrid touch pc’s / tablets, like the Windows Surface Pro, got popular, I often hear people asking me, if it’s possible to create Ext JS apps for Windows tablets?
Ext JS 6 has support for Windows 10. The classic toolkit supports IE8 and up, and the modern toolkit supports the Edge browser. It even contains a Windows mobile theme!

sp4

Ext JS 6 is also the framework, with supports you by creating universal apps. With one code base you can create Windows desktop apps for mouse and keyboard usage, and tablet interface for touch usage.

Just generate your project like:

sencha generate app MyWindowsApp ../mywindowsapp

This will create a folder structure for you, with a classic and modern toolkit folder, to branche out the separate views. For more information about universal apps, please see my previous blog post: https://www.leeboonstra.com/developer/webinar-secrets-to-building-a-great-looking-universal-app/

The fun stuff comes into the app.json file. I’ve created these build profiles:

“builds”: {
 “desktop”: {
   “toolkit”: “classic”,
   “theme”: “theme-crisp”
  },
 “tablet”: {
   “toolkit”: “modern”, //classic
   “theme”: “theme-windows” //theme-crisp-touch
  }
}

You can set the tablet theme to windows mobile, or in case you prefer to make use of the classic toolkit, then switch the theme to “theme-crisp-touch” for more whitespace around buttons and links, and bigger icons and buttons. So you won’t miss tap.

My index.html file, has an Ext.beforeLoad method, that looks like this:

Ext.beforeLoad = function (tags) {
var s = location.search,  // the query string (ex "?foo=1&bar")
      profile;


      if (s.match(/\bdesktop\b/)) {
         profile = 'desktop';
      } else if (s.match(/\btablet\b/)) {
           profile = 'tablet';
      } else {
         profile = tags.desktop ? 'desktop' : 'tablet';
      }


      Ext.manifest = profile;
};

Ext.platformTags is a singleton http://docs.sencha.com/extjs/6.0.0/classic/Ext.html#property-platformTags within Ext JS which can contain information about what type of device I am running on. When I execute this command on my Windows Surfarce Pro tablet, I get the following information:

Ext.platformTags.tablet
> false
Ext.platformTags.touch
> 10
Ext.platformTags.edge
> 15

If I switch to touch / tablet mode, for example by removing my tablet from the dock:

Ext.platformTags.tablet
> false
Ext.platformTags.touch
> 10
Ext.platformTags.edge
> 15

Hmm. Not what I expected. Unfortunately, there is no out of the box way, on how you can detect the tablet mode, from your browser, or from the Ext.platformTags singleton.

Now obviously you can create a button in the top of your app, which contains an onclick handler that does something like this:

    onTablet: function(){
        var url = window.location.href;
        url = url.split('?')[0];
        window.location.href = url + '?tablet';
    },
    onDesktop: function(){
        var url = window.location.href;
        url = url.split('?')[0];
        window.location.href = url + '?desktop';
    }

It would be nicer, if my device can detect this mode switch programmatically. Unfortunately, by the time of writing there is no HTML5 / JavaScript API solution, which can detect mode changes. I tried to look into projects like Apache Cordova, to figure out if there are native API solutions, but I couldn’t find it either.

I can’t check on touch input either, because on a hybrid machine, like the surface pro, touch input works regardless switching the mode, since the screen is just a touch screen.
So navigator.pointerEnabled will always return true.

So for now, we are left with a trick. It’s a smart trick though. And it won’t work in IE11 or below.
Which is ok, since for our demo, we will make use of the modern toolkit for tablet mode, and modern toolkit is for modern browsers, like Microsoft Edge.

However, it won’t work in any other browser either. So not, in Google Chrome or Firefox. For those browsers, you will have to stick with the button switch approach.
This might be an ok solution for you, since we are talking here about developing universal windows apps.

In tablet mode; there is no browser scrollbar. The browser scrollbar will be 0, while on desktop mode, it will be a value in pixels. (like 12px).
Now, user: robocat created a working JavaScript example in a JSBin, where you can see a working demo: https://output.jsbin.com/puseco. The trick here, is to add an hidden div to your page body with overflow scroll, to start calculating the scrollWidth.
Now I was super amazed and surprised, but apparently Ext JS already has a built-in function like this! Yay. So you only need to call: Ext.getScrollbarSize(true) where you are forcing it to re-check it: http://docs.sencha.com/extjs/6.0.0/classic/Ext.html#method-getScrollbarSize

So on my Windows Surface Pro, in desktop mode, it will return:

Ext.getScrollbarSize(true).height
> 12
Ext.getScrollbarSize(true).width
> 12

And in tablet mode:

Ext.getScrollbarSize(true).height
>0
Ext.getScrollbarSize(true).width
>0

Great! We are getting close. Now let’s check and see if we can detect it automatically!
Most of the times, when switching modes, it will also fire a window resize http://docs.sencha.com/extjs/6.0.0/classic/Ext.container.Viewport.html#event-resize or a childmove http://docs.sencha.com/extjs/6.0.0/classic/Ext.container.Viewport.html#event-childmove event.
We can attach a resize listener on an Ext.Viewport, like our main.js file, and run the mode switch. Note the extra check that I created to make sure you are switching profiles. (Otherwise your app would keep on refreshing.)
Main.js

   listeners: {
        resize: 'onMainResize',
        childmove: 'onMainResize'
    },

MainController.js

   onMainResize: function(){
        var url = window.location.href;
        url = url.split('?')[0];

        //console.log(Ext.getScrollbarSize(true).height == 0);
        //console.log(Ext.manifest.profile);

        if (
            Ext.platformTags.edge > 0 &&
            Ext.manifest.profile == 'desktop' && 
            Ext.getScrollbarSize(true).width == 0 && 
            Ext.getScrollbarSize(true).height == 0
        ) {
           window.location.href = url + '?tablet';
        } else if (
            Ext.platformTags.edge > 0 &&
            Ext.manifest.profile == 'tablet' && 
            Ext.getScrollbarSize(true).width != 0 && 
            Ext.getScrollbarSize(true).height != 0
        ) {
            window.location.href = url + '?desktop';
        }
    },

Let’s finish this article with a bonus topic. Sencha has responsiveConfig to change configuration on runtime, based on criteria. http://docs.sencha.com/extjs/6.0.0/classic/Ext.plugin.Responsive.htm. Responsive Design in Ext JS, means it’s JavaScript configuration, which means that you can write any type of criteria. It also means, that you can change anything you like on runtime. This doesn’t necessary means that you have to change the look and feel. You can change anything, so for example also load different models, redraw or refresh a page. (..because you can override a setter/update method).
Here’s a simple example. Any property can be configured as long as it has an setter under the hood. (if not, you can create your own setters of course.)

plugins: ['responsive']
responsiveConfig: {
   tall: {
      tabPosition: 'left'
    },
    wide: {
      tabPosition: 'bottom'
     }
},

Now look into this customized example, where I override the updateMethod

config: {
  customTabPanel: false
},
plugins: ['responsive']
responsiveConfig: {
    tall: {
      customTabPanel: true
    },
    wide: {
      customTabPanel: false
    }
},
updateCustomTabPanel: function(isCustomTabPanel){
   if(isCustomTabPanel){
    //manually build my tabpanel, and do a whole bunch of
      //other stuff, like creating models or something
      //or destroying a screen and rebuild it
   } else {
      //code for when customTabPanel is false
   }
}

The criteria to choose from are:

tall — Viewport width < height regardless of device type
wide — Viewport width > height regardless of device type.
landscape — Like wide, but always true on desktop devices
portrait — Like tall, but always false on desktop devices
width — An expression that tests for the specific width of the viewport
height— An expression that tests for the specific height of the viewport
platform — An object containing various booleans describing the platform

So how can you create a custom criteria? With responsiveFormulas! http://docs.sencha.com/extjs/6.0.0/classic/Ext.mixin.Responsive.html#cfg-responsiveFormulas

        responsiveFormulas: {
            touch: function(context) {
                return Ext.feature.has.Touch;
            },
            notTouch: function(context) {
                return !Ext.feature.has.Touch;
            }
        },

I have used this example, for example in a chart. On a touch device I use pinch to zoom, and on a mouse and keyboard device, I use the crosshair interaction.
Although this is a great example, it would make less sense for a windows universal app, since hybrid tablet PCs have both interfaces: touch and mouse/keyboard support.
Maybe you are creating an interface that needs to support mouse/keyboard or touch support.
Do you have a tablet/pc hybrid and want to play around with this? Here’s my project on Github to fork:

https://github.com/savelee/sencha-windows-universal-apps

studio

(PS: I own a Microsoft Surface Pro 4, and the best thing of it, is it’s keyboard. 😉 - As a small hybrid touch windows PC it’s great. As a tablet, it really sucks. No matter to what mode you switch it, it feels and breaths like a PC with touch support. Certain things in Windows are designed for mouse and keyboard usage. And also, the way how it boots, load times etc, it just shows you, it’s a PC. ..but a really good one.)

Native Sencha apps with Ext JS 6 and Cordova / PhoneGap

Posted on in Cmd Cordova Ext JS 6

Last time I played with Cordova, I developed Sencha Touch apps. Been there, done that. Sencha’s new mobile framework, is Ext JS 6. With Ext JS 6 you can create desktop applications or mobile (tablets or phone) apps. And.. you can create universal apps. A universal app, means one Ext JS 6 code base, for any type of device. Whether it’s a phone, a tablet or a desktop app, we let Sencha detect the type of device, browser or OS, and download and serve the right kind of experience for you.
That works great for web applications, the so called PWAs, progressive web apps. But what if you want to create a native app with Ext JS? For example, because you want to sell your app in an app store, or because you want to make use of native device API features. The solution would be Adobe PhoneGap or Apache Cordova.

Just in case you are completely new to this technology; PhoneGap and Cordova are making use of the same technology, but PhoneGap is from Adobe and is a commercial solution, Cordova is Apache and open source. The commercial version has a cloud build tool, which can build your applications to native online, (and therefore you don’t need to install dev tools (like for example XCode) on your own machine. Just like how Cordova works, with PhoneGap it’s also possible to build yourself, from your own machine. This requires you to have the SDKs for Android Dev Tools, Windows Tools or XCode installed on your pc or Mac. The way how Cordova/PhoneGap works, is they will take your web build (the Sencha concatenated / minified JavaScript build, index.html and CSS theme), and copy this in a native project file. (like an XCode project). These SDK project files contain a webview (a native shell. It’s like a browser, it can display webpages/apps, you just can’t enter URLs, it defaults to your web build).

Alright, so in the past I’ve wrote tutorials about Cordova and PhoneGap before, you can find those here and here. However, that has been a while ago, and I’m curious to see if it all works the same way, since technologies change, and so do Android and iOS versions.
In particular I’m curious how it will work with Ext JS 6 universal apps. I do want to have one code base for maintainability, however I do not want to have my desktop application deployed with my mobile app, since it’s only for mobiles. Further more, I don’t want to have my Android interface in an iOS build etc.

So let’s dive into this! We will make use of Cordova, since it’s open source there will be many plugins available.

In case you haven’t already. You will need to install Cordova with the NPM package manager, from your command-line. It requires Node. I’m currently running Node JS version: 5.8.0


$ npm install -g cordova

(Mac OSX users might need to prefix the CLI commands with sudo, for the permissions.)

Afterwards, review if Cordova has been correctly installed:


$ cordova -v

Generate a new Ext JS universal app. (Or use an existing one.)
(sencha generate app MyApp ../mypath), you run this command from the downloaded Sencha SDK folder.

Next open from your Ext JS app, the app.json file. We will add some code here.
Scroll to the “builds” block. Add the following build profile:

    "cordovaios": {
        "packager": "cordova",
        "cordova": {
            "config": {
                "platforms": "ios",
                "remote": false,
                "id": "com.ladysign.Spotifinder",
                "name": "MyApp"
            }
        }.
        "toolkit": "modern",
        "theme": "theme-ios"
    },

The “packager” field, will trigger Sencha Cmd, to make use of your Cordova installation.
Note, you can also enter “phonegap” here, in case you have PhoneGap installed on your machine. Once you have the packager defined, you can use the “cordova” object.
In the “platforms” field, you define the platforms you want to build for (again, it requires the SDK tools for every platform). Unless you are creating an application with one universal look and feel (a theme derived from Neptune or Triton), I would put only one platform here. You can always create multiple build profiles in the app.json. I only wrote “ios” here, because my app is making use of the new Ext JS 6.2 “theme-ios” theme. (Remember, that I don’t want to put more than one theme in my build?). The field “remote” is set to false, unless you want to make use of the PhoneGap cloud builder, than it would be “true”. The “id” is the id (reverse domain notation), you will use in your app store, and “name” that’s the name of your app, as you use in Ext.Application.
The “toolkit” property is important for an Ext JS 6 app. (Remember, that I don’t want to include the desktop version of my app in this build?). All phone applications should make use of the toolkit, since that’s a faster performing toolkit, with more components that were designed for mobile touch user experience.

Next. verify, that you didn’t make typos in the app.json file, by running from your project folder the following command:
$ sencha app refresh

Now, we need to modify the index.html file, to make sure the application can load within Cordova. The microloader will need to detect if it’s an Cordova app, to load the correct JavaScript code and theme:

    var Ext = Ext || {}; // Ext namespace won't be defined yet...
    // This function is called by the Microloader after it has performed basic
    // device detection. The results are provided in the "tags" object. You can
    // use these tags here or even add custom tags. These can be used by platform
    // filters in your manifest or by platformConfig expressions in your app.
    //
    Ext.beforeLoad = function (tags) {
        var s = location.search,  // the query string (ex "?foo=1&amp;bar")
            profile;
        if (
            location.href.match(/\bcordova\b/) ||
            Ext.platformTags.cordova ||
            Ext.platformTags.webview
        ) {
            profile = 'cordovaios';
        }
        else if (s.match(/\bmodern\b/) || tags.ios) {
            profile = 'modern';
        }
        else if (s.match(/\bandroid\b/) || Ext.platformTags.android &gt; 0) {
            profile = 'android';
        }
        else {
            profile = 'classic';
        }
        Ext.manifest = profile;
    };

Note the:
if (location.href.match(/\bcordova\b/) || Ext.platformTags.cordova || Ext.platformTags.webview)

This tells the Sencha microloader, to load the “cordovaios” build, if: “cordova” is in the url (to test on your development machine) or if the app runs in a cordova or webview shell.
Sure, instead of all this if else checking, you could force Ext.manifest = ‘cordovaios’ but the magic here, is that we will reuse this index.html for every type of app, whether it’s an desktop app, iOS PWA or Android PWA. So this microloader is actually great.

Ready? Let’s go and build:

$ sencha app build cordovaios

We tell Sencha Cmd to build the “cordovaios” profile, like we wrote in app.json.

Woopie. It seems like it worked. Let’s take a close look and see where the build is located.
According to my build log, it looks like there’s a cordova folder created in my Ext JS application folder:

MyApp/cordova:

MyApp
- cordova
- - config.xml
- - hooks
- - platforms
- - plugins
- - www
- - - index.html
- - - cordovaios
- - - - app.js
- - - - resources
- - - - - MyApp.css

The www folder, will be the place, where a copy of your Sencha Ext JS 6 app will be located. Let’s look into this folder, and verify what’s inside of it.

So what’s in the cordovaios folder, is the JavaScript code, for the modern toolkit only. Nice!
So. you won’t bundle the desktop version inside of it. And also, it only includes one theme, the right one. This is all because of the “toolkit” and “theme” settings we made in the app.json file.
How awesome.

Let’s cover some Cordova / XCode stuff, since you are here anyway. If you have done this before, this is probably not so interesting for you…

Oh cool. So you are still here.
Open config.xml and modify the name and description fields to describe your app.

After your first build, you can copy the generated cordova.js and cordova-plugins.js files, and put it in your cordova www folder. Copy it from MyApp/cordova/platforms/ios/www. That way the other cordova/www folder won’t have 404 errors.

You could make another build:
$ sencha app build cordovaios

Open your application from the cordova www folder in your browser, and verify that everything works without issues. For example:
http://localhost/MyApp/cordova/www/

We don’t need the auto generated css and js folders. So remove those.
No worries, the next time you make a cordovaios build, those folders won’t come back.

Ok, so basically, we could now open XCode, open the MyApp/cordova/platforms/ios/MyApp.xcodeproj, and build with XCode tools, the applications.

In XCode, press the “play” button. It creates a build, and it should run it in an simulator. I run into a common issue:

“The status bar is mixed into my layout.” We can fix this with the cordova-plugin-statusbar plugin. So install it.

Run on the CLI from he cordova folder:
$ cordova plugin add cordova-plugin-statusbar

Add to config.xml the following configuration:


In XCode rightclick your project to view the resources/MyApp.pList configuration. And add the following settings:

Status bar is initially hidden = YES
View controller-based status bar appearance = NO
UIRequiresFullScreen = YES

This should be fixed. Next replace all the Cordova logos and splash screens. You can find and replace those assets from here:
MyApp/Cordova/platforms/ios/MyApp/Images.xcassets

And that’s it. Project > Clean your build, Rebuild, “Project > Build”. And test and run your application in the simulator.

For iOS developers, the next steps would be to setup an paid Apple Developer account. It cost about a 100 bucks a year, and you can sell unlimited applications from the Apple App Store.
With the developer account, you can get access to iOS beta releases, and you can test your applications on real devices.

https://developer.apple.com/

Success! Feel free to share your deployed Sencha hybrid apps for Android or iOS with me in the comments!