Skip navigation

RhoMobile Blogs

5 Posts authored by: Jake Bernstein

If you've been keeping up to date on the RhoMoible blogs, then you've seen the recent post about all the benefits of using Ionic in your RhoMobile application. A big part of what makes Ionic so extraordinary is that it is built on top of AngularJS.


While many developers know about the power of AngularJS and its data-binding abilities, few might know about Angular's impressive finer points: directives and services. Directives are one of Angular’s most overlooked, yet most powerful features.

 

What is a Directive?

At its most basic level, a directive is a marker on a DOM element (an attribute, element name, comment, or CSS class) that tells the Angular compiler in your HTML to attach a specified behavior to the marked DOM element. This allows you to take a lot of repetitive code out of your HTML files. You can name your directives based on their purpose, such as “displayImages” or “showWeeklyWeather.” This gives the added bonus of making your code clearer and easier to read.


On top of this, Angular lets you have custom directives and create your own reusable functionality. This is where Angular directives really shine.


Below is a custom Directive that changes the background color of “Hello World” to match the color in the input box.



HTML Code:


<html>

  <head>
    <meta charset="utf-8" />
    <script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.7/angular.js" data-semver="1.2.7"></script>
    <script src="js/app.js"></script>
  </head>


  <body ng-controller="MainCtrl" ng-app="myapp">
  <input type="text" ng-model="color" placeholder="Enter a color" />
  <hello-world/>
</body>
</html>










JavaScript Code:


var app = angular.module('myapp', []);


app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});


app.directive('helloWorld', function() {
  return {
    restrict: 'AE',
    replace: true,
    template: '<p style="background-color:{{color}}" >Hello World </p>',
    link: function(scope, elem, attrs) {
      elem.bind('click', function() {
        elem.css('background-color', 'white');
        scope.$apply(function() {
          scope.color = "white";
        });
      });
      elem.bind('mouseover', function() {
        elem.css('cursor', 'pointer');
      });
    }
  };
});









This might look light a bit of code, but if you want this functionality in multiple places on a web page, all you have to do is drop the hello world directive into the HTML “<hello-world/>” and watch the magic happen.


The “restrict” and “replace” properties from the code above are optional.


Restrict lets your HTML know how you are going to reference your directives and can be set to:

'A' - only matches attribute name

'E' - only matches element name

'C' - only matches class name

or any combination of the above:

'AEC' - matches either attribute or element or class name

If you don’t set restrict to anything, its default status will be 'AE'.


“Replace: true” means that the content of the directive template will replace the element that the directive is declared on, in this case, the </hello-world> tag.


Another important thing to notice in the code above is that the directive is named “helloWorld” in the JavaScript file, but is referenced as “hello-world” in the HTML code. This is not a typo; your HTML file knows that hello-world = helloWorld. It is important to follow this pattern as HTML files expect “snake-case” variables and JavaScript files expect “camelCase” variables.


There’s a lot of potential in custom directives and they are becoming increasingly popular.


Angular Google Maps is a great example of how to take advantage of custom-made directives. Angular Google Maps is a set of directives (part of angular-ui) written in CoffeeScript and JavaScript which integrate Google Maps in an AngularJS application. There are directives for most of the widely-used Google Maps objects, including markers, windows, lines and shapes, and coming up is integration with more layer objects, including the HeatMap.


Screen Shot 2015-07-30 at 3.52.27 PM.png


UI-Bootstrap is a useful directive that comes with many of the great Bootstrap features that you’ve grown to love, but without any dependency on jQuery or Bootstrap’s JavaScript. Bootstrap offers a powerful set tools that can take you far and wide. (The website below was created with Bootstrap)


Screen Shot 2015-07-30 at 3.57.04 PM.png


Check out more of the gorgeous designs people have created.


If you are in the market for more basic features, UI-Calendar lets you implement a fully functioning Arshaw FullCalendar or UI-Router, which provides many routing options that don’t inherently come with Angular.


Screen Shot 2015-07-30 at 4.06.53 PM.png


Whatever you need, there’s probably a directive for that


Directive Deep Dive

The Ionic blog post touched on earlier had a great feature where a user's profile would load with a generic profile picture and then clicking on the picture would let you use RhoMobile's Camera API to open your device's camera and take a new profile picture.


Screen Shot 2015-07-30 at 4.29.59 PM.png


This feature will come in handy, so let's turn it into a directive for future use.


.directive("profileDirective", function() {
    return {
        restrict: "AEC",
        template: '<div class="item item-image" ng-click="takePicture()"> <img ng-src={{dataUriModel}}> <div/>',
        link: function(scope, element) {
          scope.dataUriModel = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjAwLjAwMDA2IgogICBoZWlnaHQ9IjYwMCIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC40IHI5OTM5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJQcm9maWxlUGxhY2Vob2xkZXJTdWl0LnN2ZyIKICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS9uYXVnaHQxMDEvRG9jdW1lbnRzL2FydC9Qcm9maWxlUGxhY2Vob2xkZXJTdWl0LnBuZyIKICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjMwIgogICBpbmtzY2FwZTpleHBvcnQteWRwaT0iMzAiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjciCiAgICAgaW5rc2NhcGU6Y3g9IjYxNy4yMTEyNSIKICAgICBpbmtzY2FwZTpjeT0iMzI3LjQ1ODQ4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTYwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NTQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii0yIgogICAgIGlua3NjYXBlOndpbmRvdy15PSItMyIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGZpdC1tYXJnaW4tdG9wPSIwIgogICAgIGZpdC1tYXJnaW4tbGVmdD0iMCIKICAgICBmaXQtbWFyZ2luLXJpZ2h0PSIwIgogICAgIGZpdC1tYXJnaW4tYm90dG9tPSIwIiAvPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03NDMuMjAxNTUsOTcuNTY2MzU0KSI+CiAgICA8cGF0aAogICAgICAgc3R5bGU9Im9wYWNpdHk6MC41O2ZpbGw6Izk5OTk5OTtzdHJva2U6bm9uZSIKICAgICAgIGQ9Im0gMTAyNi41MTg0LC0zNC4wNDM4NTQgYyAtMTQuOTExMywxLjI3MTk5IC0zMC40NTU2NCw0Ljc1NjYxNSAtNDIuOTk1OTEsMTIuOTI0MTE1IC0xMC45NzIxNiw3LjE0NjI0OSAtMTkuMjMzMzcsMTguMzY3NjM5MSAtMjUuNTUyMjksMjkuODM2Mjc5MSAtNC42OTAxMiw4LjUxMjQ2ODkgLTYuOTkyNDIsMTguMjc2NDYyOSAtOC41ODc4MywyNy44NjM2NDk5IC0yLjUyNDU0LDE1LjE3MDQ1OSAtMS40MjIyOCw0Ni4xMTUzNTMgLTEuNDIyMjgsNDYuMTE1MzUzIGwgLTUuNDA2MTcsOC44Njk0OTEgYyAtMS4zNjMzOCwxMC43Mjg1NjYgMC4zNzk1MSwyMS4xMTU3ODYgMC43NzMwMSwzMS44NDQzNDYgMC4zMDY2Niw4LjM2MTk2IDEuMjQ0OTgsMTYuNzIzOTQgMi45Njc3MywyNS4wODU5MSBsIDguNTA3NjEsOS45NzEwNSBjIDAsMCAxLjk3ODk3LDExLjg1MTY2IDUuNDYxMDYsMjAuMDU1MzEgMy40ODIwNiw4LjIwMzYzIDE3LjA5Mjc5LDI2Ljk5NTIyIDE3LjA5Mjc5LDI2Ljk5NTIyIC0xMi41MzcwNCw0LjMzNTE4IC0zMS4xNTQ2LDguMDQ4OTcgLTM3LjU4OTc2LDEzLjAwODU5IC00LjY3Mjg0LDMuNjAxNCAtNS43NjIyOSwxMC42MjUxNSAtOC4xOTM3MiwxNi4zODc0NSBsIC0xMTUuMzAzMzgsMjEuMTE3ODMgLTE0LjM2MDEyLDIxLjIwMjMxIDAsMzAuNjYzMSAtNTguNzA3NTksMTk0LjUzNzQ4IDYwMC4wMDAwNSwyZS01IC0xOC40MTQ5LC02Mi4wODY0NCAtMjIuNTUzOCwtMTI2LjI4NDY1IC0xLjM1MTUsLTI1LjI1Njk0IC04Ljg2OTYsLTE1LjAzNTkgLTQ2LjM3NDcsLTE3LjA2MzIxIC02Ni4yMjU1LC0xMS41NzI1OCBjIC03LjA2NzUsLTcuMjkyNyAtMTUuOTk5OSwtMTcuNDMwMiAtMjEuMjAyMywtMjEuODc4MDcgLTUuMTg5MSwtNC40MzY0MyAtMjguNjY0LC0xMC45MjQ5NyAtNDIuOTk1OSwtMTYuMzg3NDQgbCAtMi43MDMxLC05LjU0NTI3IGMgMCwwIDcuNjY4MiwtOS4yNTI4MSAxMC4yMjEsLTE2LjM4NzQzIDIuNTUyOCwtNy4xMzQ2MyAzLjM3ODgsLTI1LjkzMjcxIDMuMzc4OCwtMjUuOTMyNzEgbCA0LjgxNDksMS4zNTE1NSA0LjgxNDksLTcuNTE3OTYgMi4wMjczLC0yNy4yODQyNCBjIDAsMCAwLjM5NjksLTMyLjM0MDMzMiAtMS4zNTE1LC0zNi4xNTM3MzQgLTEuNzQ4NSwtMy44MTM0MDEgLTUuNDkwNywtNC44MTQ4NjcgLTUuNDkwNywtNC44MTQ4NjcgMCwwIDAuOTk3MiwtMjkuNTYzODA4IC0yLjcwMzEsLTQzLjY3MTY4NCAtMy44NDk5LC0xNC42NzgzMzYgLTEwLjQyNDksLTI5LjA3NjE3MjEgLTE5Ljg1MDcsLTQwLjk2ODYwMjEgLTcuNzI4MywtOS43NTA2MTQ5IC0xNy41MzQ2LC0xOC42NTE1NDI5IC0yOC45ODYyLC0yMy41MTYwMzc5IC0xNi4zNDA1LC02Ljk0MTMyOCAtMzUuMTc3MSwtNy45ODAyNTggLTUyLjg2NjYsLTYuNDcxMjg5IHogbSAtNDcuMTM1LDI1MS44OTM1NDQgMTAuODk2ODEsMTkuNzY2MyAzOC45NDEyOSwzMi43NzQ4OCAxOS4wOTA1LDEuMzUxNTQgNDQuNDMxOSwtMjMuOTA1MzkgMTkuNzY2NCwtMjcuMjg0MjQgLTIuMDI3NCwxMC4yMjEwMyAtMjMuOTA1Myw2OC45Mjg2MiAtMTkuODUwOCw2NC44NzM5OCAtMTIuOTI0MSw1OS4zODMzNiAtMTMuMDA4Niw3Mi4zOTE5NCAtMzIuMDE0NiwtMTkzLjE4NTk2IC0xOS4xNzUwMSwtNDAuMjkyODMgeiBtIDg0LjY0OTMsMTQ1LjM2NTgzIC0xNi4zODMyLDgxLjkxNTc3IC02LjgyNjMsNDkuNTI0NDQgLTIyLjgzNDksLTE1NC4xMTY4MyA3Ljg1NDMsLTIwLjczNzM1IDIwLjE4MywtMi4wMDEwMSAtMTkuMTk2NCwtMC4zMjEzNCAtMTIuMzE3LC0yNS42MDY1MiAtNi42MjYsNS4yOTgyMyAxNy45MTkxLC0yMy4zODAxMyAyNC41NzQ3LDEuMzY1MjcgMjYuOTYzOSwzMS43NDIzNiAtMTUuNjQ2NiwtMTMuMDUwODQgLTE0LjczMDUsMjMuNjMxNjMgMTYuMzgzMiwzNC44MTQyMiB6IgogICAgICAgaWQ9InBhdGgyOTk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0ic3Nzc2Njc2NjemNzY2NjY2NjY2NjY2Njc2NjemNjY2N6Y3Nzc3NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2MiIC8+CiAgPC9nPgo8L3N2Zz4K";
          scope.takePicture = function(){
          Rho.Camera.takePicture({
            outputFormat: Rho.Camera.OUTPUT_FORMAT_DATAURI
            },
            function(resp){
                scope.dataUriModel = resp.imageUri;
                scope.$apply();
          });
        }
        }
    };
});







We pass the original image (in dataUri format) into the scope of our directive, and then simply make the necessary API calls and store the new photo as our profile picture.


Any time you want to use this feature, you could drop "<profile-directive/>" into your HTML and reap all the benefits without any additional code.


Clean, simple, and easy to implement, directives are the way to go.


The Best in Service

Services are singletons, which are objects that are instantiated only once per app (by the $injector). They provide an interface to keep together methods that relate to a specific function. The $http service is an example of an AngularJS service that provides low-level access to the browser’s XMLHttpRequest object. Rather than needing to dirty the application with low-level calls to the XMLHttpRequest object, we can simply interact with the $http API.

Angular offers several useful services (like $http), but for most applications you'll also want to create your own. You can define your own service by registering the service's name and service factory function, with an Angular module. This function generates the single object or function that represents the service to the rest of the application. The object or function returned by the service is injected into any component (controller, service, filter or directive) that specifies a dependency on the service.

Services are registered to modules via the Module API. Typically you use the Module factory API to register a service:

var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
  var shinyNewServiceInstance;
  // factory function body that constructs shinyNewServiceInstance
  return shinyNewServiceInstance;
});





Note that you are not registering a service instance, but rather a factory function that will create this instance when called.

To use an Angular service, you add it as a dependency for the component (controller, service, filter or directive) that depends on the service. Angular's dependency injection subsystem takes care of the rest.


Have it Your Way

Just like with directives, we can write custom services. Let's take the code from the Ionic blog post, and add a custom service that uses RhoMobile's barcode API


Go into build.yml and add the  "- barcode" extension.


extensions:
  - rhoconnect-client
  - mediacapture
  - barcode





Next, add the factory method for the service into Controller.js and add the necessary dependencies to the Profile Controller:


.controller('ProfileCtrl', ['$scope', '$stateParams', '$zebraBarcodeScanner', function($scope, $stateParams,$zebraBarcodeScanner) {
  $scope.dataUriModel = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNjAwLjAwMDA2IgogICBoZWlnaHQ9IjYwMCIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC40IHI5OTM5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJQcm9maWxlUGxhY2Vob2xkZXJTdWl0LnN2ZyIKICAgaW5rc2NhcGU6ZXhwb3J0LWZpbGVuYW1lPSIvaG9tZS9uYXVnaHQxMDEvRG9jdW1lbnRzL2FydC9Qcm9maWxlUGxhY2Vob2xkZXJTdWl0LnBuZyIKICAgaW5rc2NhcGU6ZXhwb3J0LXhkcGk9IjMwIgogICBpbmtzY2FwZTpleHBvcnQteWRwaT0iMzAiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjciCiAgICAgaW5rc2NhcGU6Y3g9IjYxNy4yMTEyNSIKICAgICBpbmtzY2FwZTpjeT0iMzI3LjQ1ODQ4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTYwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NTQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii0yIgogICAgIGlua3NjYXBlOndpbmRvdy15PSItMyIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGZpdC1tYXJnaW4tdG9wPSIwIgogICAgIGZpdC1tYXJnaW4tbGVmdD0iMCIKICAgICBmaXQtbWFyZ2luLXJpZ2h0PSIwIgogICAgIGZpdC1tYXJnaW4tYm90dG9tPSIwIiAvPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03NDMuMjAxNTUsOTcuNTY2MzU0KSI+CiAgICA8cGF0aAogICAgICAgc3R5bGU9Im9wYWNpdHk6MC41O2ZpbGw6Izk5OTk5OTtzdHJva2U6bm9uZSIKICAgICAgIGQ9Im0gMTAyNi41MTg0LC0zNC4wNDM4NTQgYyAtMTQuOTExMywxLjI3MTk5IC0zMC40NTU2NCw0Ljc1NjYxNSAtNDIuOTk1OTEsMTIuOTI0MTE1IC0xMC45NzIxNiw3LjE0NjI0OSAtMTkuMjMzMzcsMTguMzY3NjM5MSAtMjUuNTUyMjksMjkuODM2Mjc5MSAtNC42OTAxMiw4LjUxMjQ2ODkgLTYuOTkyNDIsMTguMjc2NDYyOSAtOC41ODc4MywyNy44NjM2NDk5IC0yLjUyNDU0LDE1LjE3MDQ1OSAtMS40MjIyOCw0Ni4xMTUzNTMgLTEuNDIyMjgsNDYuMTE1MzUzIGwgLTUuNDA2MTcsOC44Njk0OTEgYyAtMS4zNjMzOCwxMC43Mjg1NjYgMC4zNzk1MSwyMS4xMTU3ODYgMC43NzMwMSwzMS44NDQzNDYgMC4zMDY2Niw4LjM2MTk2IDEuMjQ0OTgsMTYuNzIzOTQgMi45Njc3MywyNS4wODU5MSBsIDguNTA3NjEsOS45NzEwNSBjIDAsMCAxLjk3ODk3LDExLjg1MTY2IDUuNDYxMDYsMjAuMDU1MzEgMy40ODIwNiw4LjIwMzYzIDE3LjA5Mjc5LDI2Ljk5NTIyIDE3LjA5Mjc5LDI2Ljk5NTIyIC0xMi41MzcwNCw0LjMzNTE4IC0zMS4xNTQ2LDguMDQ4OTcgLTM3LjU4OTc2LDEzLjAwODU5IC00LjY3Mjg0LDMuNjAxNCAtNS43NjIyOSwxMC42MjUxNSAtOC4xOTM3MiwxNi4zODc0NSBsIC0xMTUuMzAzMzgsMjEuMTE3ODMgLTE0LjM2MDEyLDIxLjIwMjMxIDAsMzAuNjYzMSAtNTguNzA3NTksMTk0LjUzNzQ4IDYwMC4wMDAwNSwyZS01IC0xOC40MTQ5LC02Mi4wODY0NCAtMjIuNTUzOCwtMTI2LjI4NDY1IC0xLjM1MTUsLTI1LjI1Njk0IC04Ljg2OTYsLTE1LjAzNTkgLTQ2LjM3NDcsLTE3LjA2MzIxIC02Ni4yMjU1LC0xMS41NzI1OCBjIC03LjA2NzUsLTcuMjkyNyAtMTUuOTk5OSwtMTcuNDMwMiAtMjEuMjAyMywtMjEuODc4MDcgLTUuMTg5MSwtNC40MzY0MyAtMjguNjY0LC0xMC45MjQ5NyAtNDIuOTk1OSwtMTYuMzg3NDQgbCAtMi43MDMxLC05LjU0NTI3IGMgMCwwIDcuNjY4MiwtOS4yNTI4MSAxMC4yMjEsLTE2LjM4NzQzIDIuNTUyOCwtNy4xMzQ2MyAzLjM3ODgsLTI1LjkzMjcxIDMuMzc4OCwtMjUuOTMyNzEgbCA0LjgxNDksMS4zNTE1NSA0LjgxNDksLTcuNTE3OTYgMi4wMjczLC0yNy4yODQyNCBjIDAsMCAwLjM5NjksLTMyLjM0MDMzMiAtMS4zNTE1LC0zNi4xNTM3MzQgLTEuNzQ4NSwtMy44MTM0MDEgLTUuNDkwNywtNC44MTQ4NjcgLTUuNDkwNywtNC44MTQ4NjcgMCwwIDAuOTk3MiwtMjkuNTYzODA4IC0yLjcwMzEsLTQzLjY3MTY4NCAtMy44NDk5LC0xNC42NzgzMzYgLTEwLjQyNDksLTI5LjA3NjE3MjEgLTE5Ljg1MDcsLTQwLjk2ODYwMjEgLTcuNzI4MywtOS43NTA2MTQ5IC0xNy41MzQ2LC0xOC42NTE1NDI5IC0yOC45ODYyLC0yMy41MTYwMzc5IC0xNi4zNDA1LC02Ljk0MTMyOCAtMzUuMTc3MSwtNy45ODAyNTggLTUyLjg2NjYsLTYuNDcxMjg5IHogbSAtNDcuMTM1LDI1MS44OTM1NDQgMTAuODk2ODEsMTkuNzY2MyAzOC45NDEyOSwzMi43NzQ4OCAxOS4wOTA1LDEuMzUxNTQgNDQuNDMxOSwtMjMuOTA1MzkgMTkuNzY2NCwtMjcuMjg0MjQgLTIuMDI3NCwxMC4yMjEwMyAtMjMuOTA1Myw2OC45Mjg2MiAtMTkuODUwOCw2NC44NzM5OCAtMTIuOTI0MSw1OS4zODMzNiAtMTMuMDA4Niw3Mi4zOTE5NCAtMzIuMDE0NiwtMTkzLjE4NTk2IC0xOS4xNzUwMSwtNDAuMjkyODMgeiBtIDg0LjY0OTMsMTQ1LjM2NTgzIC0xNi4zODMyLDgxLjkxNTc3IC02LjgyNjMsNDkuNTI0NDQgLTIyLjgzNDksLTE1NC4xMTY4MyA3Ljg1NDMsLTIwLjczNzM1IDIwLjE4MywtMi4wMDEwMSAtMTkuMTk2NCwtMC4zMjEzNCAtMTIuMzE3LC0yNS42MDY1MiAtNi42MjYsNS4yOTgyMyAxNy45MTkxLC0yMy4zODAxMyAyNC41NzQ3LDEuMzY1MjcgMjYuOTYzOSwzMS43NDIzNiAtMTUuNjQ2NiwtMTMuMDUwODQgLTE0LjczMDUsMjMuNjMxNjMgMTYuMzgzMiwzNC44MTQyMiB6IgogICAgICAgaWQ9InBhdGgyOTk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0ic3Nzc2Njc2NjemNzY2NjY2NjY2NjY2Njc2NjemNjY2N6Y3Nzc3NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2MiIC8+CiAgPC9nPgo8L3N2Zz4K";
  $scope.barcode = "Scan";
  $scope.takePicture = function(){
    Rho.Camera.takePicture({
      outputFormat: Rho.Camera.OUTPUT_FORMAT_DATAURI
      },
      function(resp){
          $scope.dataUriModel = resp.imageUri;
          $scope.$apply();
    });
  }


  $scope.scanBarcode = function(){
    $zebraBarcodeScanner
      .scan()
      .then(function(barcodeData) {
        // Success! Barcode data is here
        $scope.barcode = barcodeData;
      }, function(error) {
        // An error occurred
        alert(error)
      });
  }


}])


.factory('$zebraBarcodeScanner', ['$q', function ($q) {


    return {
      scan: function (config) {
        var q = $q.defer();


        if(Rho.Barcode){
          Rho.Barcode.take({},
            function(resp){
              if(resp.status=='ok'){
                q.resolve(resp.barcode);
              }else{
                q.reject('user cancelled or timeout occured');
              }
            });
        }
        else{
          q.reject('API not found - Make sure to add barcode extension and perform clean build.')
        }


        return q.promise;
      }
    };
  }]);





Now if you go into profile.html inside of Public/templates and remove the ng-if statement from the page and compile your code, your profile page should have a spiffy new scanner!


</div>
    <div class="list">
    <div class="item">
    <button class="button button-full button-positive icon-left ion-ios-barcode-outline" ng-click="scanBarcode()">{{barcode}}</button>
    </div>
    </div>
  </ion-content>
</ion-view>





Screen Shot 2015-07-31 at 1.05.54 PM.png

 

As the code above suggests, services are used more in the controller layer then in presentation, while directives are used mostly to influence what your users see. Directives and Services are both great ways to tidy up your HTML and JavaScript files and to keep everything organized and effective. Start looking into these options as a way to bring your RhoMobile apps to the next level.



We developers spend the majority of our time interacting with the world through screens and pixels, which means it is more important than ever to have a stunning and immersive interface. Google takes care of this with its beautiful and clean Material Design, which offers simple yet delightful visual details and UI components that lead to a great user experience.


Material Design is a visual language designed with a mobile-first approach and an increased use of grid-based layouts, responsive animations and transitions, padding, and depth effects. Material Design has been growing in popularity and has plenty of frameworks like Materialize, Angular Material, Material Design Lite, Material UI, and many more.


Frameworks:


Materialize

A modern responsive front-end framework. Materialize was designed as an easy to work with framework that speeds up development while still focusing on user experience. Materialize has many UI components that were created with mobile in mind.


Take Materialze’s Toast, for example. Toast, albeit a funny name, is an easy way to send users unobtrusive alerts and is placed and sized responsively. This, along with sleek drag out menus, makes Materialize a great choice for any developer.


Angular Material

Makes using Material Design in your Angular application effortless. Angular Material provides a set of reusable, well-tested, and accessible UI components based on the Material Design System.


Polymer

Polymer’s paper elements collection implements Material Design for the web in a fast and efficient way. The Polymer core elements collection provides a number of unthemed elements that you can use to achieve material design app layouts, transitions, and scrolling effects.


Material Design Lite

Lets you add a Material Design look and feel to your static content websites without relying on any JavaScript frameworks or libraries. Optimized for cross-device use, gracefully degrades in older browsers, and offers an experience that is accessible from the get-go.


A Deeper Leap into Lite Design:

Material Design Lite, is extremely light (27kB gzipped), and a great place to start if you want to try out Material Design in your project.


You can include Material Design Lite in your application by including the following <link> and <script> elements in your HTML pages:

   

  <link rel="stylesheet" href="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.indigo-pink.min.css">
<script src="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

 

There’s a large number of color combinations for you to choose from for you color scheme. You can specify a color scheme in the CSS file name by following the pattern:

 

material.{primary}-{accent}.min.css

(e.g. material.indigo-pink.min.css)

 

Some Cool Components

The Fab Buttons Material offers are simple, yet get the point across, and comes in multiple sizes and colors.

 

Screen Shot 2015-07-14 at 12.08.16 PM.png


You can include a Fab button in your project with the following code:


<!-- Colored FAB button -->
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
  <i class="material-icons">add</i>
</button>













You can add a ripple effect to your Fab by simply adding mdl-js-ripple-effect

to the button class


MDL’s Badge icons follow this “less is more” pattern, letting you display different numbers and designs above badges to get your point across


Screen Shot 2015-07-14 at 12.10.10 PM.png


And the code for these badges:


<!-- Number badge on icon -->
<div class="icon material-icons mdl-badge" data-badge="1">account_box</div>
<!-- Icon badge on icon -->
<div class="icon material-icons mdl-badge" data-badge="♥">account_box</div>













Google’s self-contained cards give developers a way to add smooth aesthetics to various forms of information, ranging from pictures to event invites.


Screen Shot 2015-07-14 at 12.09.49 PM.png

 

 

<!-- Image card -->
<style>
  .demo-card-image.mdl-card {
    width: 256px;
    height: 256px;
    background: url('../assets/demos/image_card.jpg') center / cover;
  }
  .demo-card-image > .mdl-card__actions {
    height: 52px;
    padding: 16px;
    background: rgba(0, 0, 0, 0.2);
  }
  .demo-card-image__filename {
    color: #fff;
  }
</style>

<div class="mdl-card mdl-shadow--2dp demo-card-image">
  <div class="mdl-card__title mdl-card--expand"></div>
  <div class="mdl-card__actions">
    <span class="demo-card-image__filename">Image.jpg</span>
  </div>
</div>
<!-- Event card -->
<style>
  .demo-card-event.mdl-card {
    width: 256px;
    height: 256px;
    background: #3E4EB8;
  }
  .demo-card-event > .mdl-card__actions {
    border-color: rgba(255, 255, 255, 0.2);
  }
  .demo-card-event > .mdl-card__title,
  .demo-card-event > .mdl-card__actions > .mdl-button {
    color: #fff;
  }
</style>

<div class="mdl-card mdl-shadow--2dp demo-card-event">
  <div class="mdl-card__title mdl-card--expand">
    <h4>
      Featured event:<br>
      May 24, 2016<br>
      7-11pm
    </h4>
  </div>
  <div class="mdl-card__actions mdl-card--border">
    <a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">
      Add to Calendar
    </a>
  </div>
</div>













More Fun with Mobile:

MDL's layout principles simplify the creation of scalable pages by providing reusable components and encourage consistency across environments by establishing recognizable visual elements, adhering to logical structural grids. The Layout components of MDL comprises navigation, tabs, and footers–which have each been optimized for varying viewport sizes and are a key set of features for all mobile developers to add to their arsenals.


Responsive Grid

The responsive grid in MDL is perfect for maintaining appropriate spacing across multiple platforms and screen sizes. MDL's grid is extremely powerful and dynamic, allowing for great consistency in outward appearance and behavior while maintaining development flexibility and ease of use.


Screen Shot 2015-07-14 at 12.46.09 PM.png


Tabs

The Tab component is a user interface element that allows different content blocks to share the same screen space in a mutually exclusive manner. MDL Tabs can shrink and stretch to fit any screen size, whether it be a huge monitor, an iPad, or a smart-phone, so you can always share your latest Game of Thrones conspiracies with friends. (Is Ned Stark really dead?)

Screen Shot 2015-07-14 at 12.48.34 PM.png


Footers

The Material Design Lite (MDL) footer component is a comprehensive container intended to present a substantial amount of related content in a visually attractive and logically intuitive area. Although it is called "footer", it may be placed at any appropriate location on a device screen, either before or after other content.

 

Screen Shot 2015-07-14 at 1.01.57 PM.png

 

There are different footer options, ranging from the "Mega Footer" above to a small and to the point "Mini Footer" (code below).

 

<footer class="mdl-mini-footer">
 <div class="mdl-mini-footer--left-section">
  <div class="mdl-logo"> More Information </div>
   <ul class="mdl-mini-footer--link-list">
    <li><a href="#">Help</a></li> <li><a href="#">Privacy and Terms</a></li>
    <li><a href="#">User Agreement</a></li> </ul> </div>
    <div class="mdl-mini-footer--right-section">
    <button class="mdl-mini-footer--social-btn"></button>
   <button class="mdl-mini-footer--social-btn"></button>
  <button class="mdl-mini-footer--social-btn"></button>
 </div>
 </footer>




 


Using MDL on Dynamic Websites

Material Design Lite will automatically register and render all elements marked with MDL classes upon page load. But in the case where you are creating DOM elements dynamically you need to register new elements using the upgradeElement function. Here is how you can dynamically create a raised button with ripples:

<div id="container"/>
<script>
  var button = document.createElement('button');
  var textNode = document.createTextNode('Click Me!');
  button.appendChild(textNode);
  button.className = 'mdl-button mdl-js-button mdl-js-ripple-effect';
  componentHandler.upgradeElement(button, 'MaterialButton');
  document.getElementById('container').appendChild(button);
</script>














Make the Most of Your Material

Material Design helps make otherwise complicated designs straightforward. Stop pulling your hair out over interface issues, and reconsider what you’ve been using for your web apps and websites. Material design may be just the solution to your development problems, and relieve a lot of the headache that comes along.

In our fast-paced world, it’s more important than ever to provide the content people need, right when they need it. Realtime Applications give you the opportunity to do just that for your users. It’s great to focus on keeping your users happy but have you ever stopped to think about if there was another way to make these applications?  Most programmers create their Realtime Applications with either “polling” or “push notifications”. Both methods are great, but they also have their drawbacks.

 

Polling:

Polling is when your application continually checks the back end for updates on some timed interval. This is very effective but isn’t the most efficient. The main concern with polling is that it can impact on rate limits and increases strain on the host server. In a real world example, which of the following sounds better?

Option 1: Repeatedly walk back and forth to the Post Office until new mail arrives

Option 2: Go about your business and have the mailman bring the mail directly to your house


Most people would go with option 2.


Push Notifications:

Push notifications are simple messages from apps installed on a device that wake up the handset and alert the user with a message displayed on the home or lock screen. Pushing works well for a limited number of users, and is a go-to option if you need low latency. Push notifications are also better means to maintain battery life than background processes. But sometimes you want your application to receive 'background push' notifications without any user intervention or disruption.


Another approach:

To avoid the pitfalls of Polling and Push Notifications, you can make use of WebSockets and a responsive backend. Below is a Realtime shopping list that uses PubNub’s WebSockets and Parse’s responsive backend, but you could use whatever frameworks and libraries you like. You can swap in any backend in place of Parse, such as MySQL or MongoDB. And there are plenty of alternatives for PubNub such as Socket.IO, Firebase, Pusher.com, and much more.


Download the code for the Shopping list and play around with the application.



Data flow:

Let’s use a real-world example. We have two roommates, John and Jack who need to go shopping for Jack’s birthday party

 

  1. Subscribe - John opens up the shopping list on Device A and Jack opens up the shopping list on Device B. On load, Device A and Device B are both subscribed to the same PubNub channel and both devices display a shopping list based off of the grocery list on Parse.com.
  2. REST - While Jack is shopping for groceries, John realizes that they need beer for the birthday party. John is stuck at home cleaning, but he remembers that his web app updates in real time and adds beer to the list. After John adds beer to the shopping list, a REST call is made to Parse.com with a copy of the beer object. Beer is then added to Parse’s data base.
  3. Cloud Code - Parse notices that beer was just saved to the grocery Class and has the grocery Class run its afterSave method. This afterSave method sends the new shopping list item, beer, to the PubNub channel
  4. Publish - PubNub publishes beer to the shopping list channel and both devices receive it since they are subscribed to this channel.
  5. Display Changes - Both Devices update their lists with the new item and Jack now knows to get beer for the big birthday party


Storing the data:

Storing the data is fairly simple. The application uses Parse’s API to make REST calls  which quickly stores, deletes, and edits objects.


return $http.put('https://api.parse.com/1/classes/Groceries/' + grocery.objectId, editedObj,{
    headers:{
      'X-Parse-Application-Id': PARSE_CREDENTIALS.APP_ID,
      'X-Parse-REST-API-Key': PARSE_CREDENTIALS.REST_API_KEY,
      'Content-Type':'application/json'
    }
  });








A Responsive Backend:

The cool part about Parse is the Cloud Codehttps://parse.com/docs/js/guide#cloud-code]feature, more specifically the afterSave and afterDelete methods.

These methods act much like their names suggest. After a save is made to Parse, the afterSave method is automatically triggered and the code inside of the method is run. The same goes for the afterDelete method. If you wanted to use NodeJS instead, you could create your own afterSave and afterDelete methods and just copy the code from inside the methods.


The code below is broken down with comments so you can see just how simple the server side logic can be.


Parse.Cloud.afterSave("Groceries", function(request) {
  //Initializing PubNub
  var pubnub = require('cloud/pubnub.js')({
    ssl           : true,  // <- enable TLS Tunneling over TCP
    publish_key   : "pub-c-XYXYXYXYXYXY",
    subscribe_key : "sub-c-XYXYXYXYXYXY"
});
      //Creating Object with the same information Parse used to create an item in its database
     var grocery = request.object.attributes;
     var grocObj = { "name" : grocery.name,
                     "price" : grocery.price,
                     "quantity": grocery.quantity,
                     "objectId": request.object.id
                   }


      //Using PubNub to pass that object to all subscribers as a message           
      pubnub.publish({ 
          channel   : 'jakeb',
          message   : grocObj,
          callback  : function(e) { console.log( "SUCCESS!", e ); },
          error     : function(e) { console.log( "FAILED! RETRY PUBLISH!", e ); }
      });


});


Parse.Cloud.afterDelete("Groceries", function(request) {
  //Initializing PubNub
  var pubnub = require('cloud/pubnub.js')({
    ssl           : true,  // <- enable TLS Tunneling over TCP
    publish_key   : "pub-c-XYXYXYXYXYXY",
    subscribe_key : "sub-c-XYXYXYXYXYXY"
});
     //Declaring attributes of object that be deleted. Name = delete as a flag to let `app.js` know to delete this object
     var grocery = request.object.attributes;
     var grocObj = { "name" : "delete",
                     "price" : grocery.price,
                     "quantity": grocery.quantity,
                     "objectId": request.object.id
                   }
      //Using PubNub to pass that object to all subscribers as a message
      pubnub.publish({ 
          channel   : 'jakeb',
          message   : grocObj,
          callback  : function(e) { console.log( "SUCCESS!", e ); },
          error     : function(e) { console.log( "FAILED! RETRY PUBLISH!", e ); }
      });


});








The Power of Websockets:

It’s great that Parse knows to respond to changes in the database, but these changes would go unnoticed if it wasn’t for PubNub’s use of WebSockets and their Publish/Subscribe model. The WebSockets are used to keep a persistent connection between the client and PubNub’s server where both parties can send data at any time. With these persistent connections, PubNub publishes messages (the shopping list items) to all subscribed users. This makes it easy to always keep users up to date and informed on the most current shopping list.


The Party Never Ends:

And with PubNub’s Presence, users can confidently maintain their lists offline without worrying about changes going unnoticed. PubNub Presence keeps a second open WebSocket that runs concurrent to the Publish/Subscribe WebSocket. This WebSocket keeps track of who is connected to a channel and notifies the web app anytime a user connects, leaves, or has a timeout (loses connection). When connection is lost, PubNub notifies the shopping list and updates the user's local list but waits to save the actual database updates for when connection issues are resolved.


Conclusion:

Next Time you are thinking of making a RealTime Application, don’t fret if you don’t want to use polling or implement push notifications; just set up some WebSockets and grab a responsive backend, and you’ll be good to go.


You want to make the best possible project, but you keep on adding framework after framework until your project is so big and bulky that no one wants to use it, let alone have it waste space on their device. But how can you build a lightweight and attention-grabbing RhoMobile App?

 

One answer might be Pure.css, a lightweight CSS framework that provides  appealing layouts and stylings for native HTML elements.  Pure includes some of  the most common UI components, but none of the excess baggage. The entire set of modules in Pure clocks in at 4.0KB minified and gzipped.

 

Pure is responsive out of the box and was designed with mobile in mind. Elements look great on any screen, automatically adjusting to fit whatever device or monitor is in use.

 

You can include Pure in your projects with the following line:

 

 

<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css">







 

 

The Grid System


  • The grid system in Pure differs from that of other frameworks such as Bootstrap and it is powerful and easy to work with once you get past the basics. There are a few things to keep in mind when starting:


  • Pure Grids consist of two types of classes: the grid class (pure-g) and unit classes (pure-u or pure-u-*)


  • Units have various class names that represent their widths. For example, pure-u-1-2 has a width of 50 percent, whereas pure-u-1-5 would have a width of 20 percent. Your columns can get as small as pure-u-1-24, so you have plenty of options for customization.


  • Child elements contained within an element with a pure-g class name must be a grid unit with a pure-u or pure-u-* class name


  • All content which is visible to people needs to be contained inside a grid unit. This ensures that the content will be rendered properly.


Here’s an example of a pure grid with four columns that are each one-fourth width.

 

 

<div class="pure-g">
     <div class="pure-u-1-4"><p>Fourths</p></div>
     <div class="pure-u-1-4"><p>Fourths</p></div>
     <div class="pure-u-1-4"><p>Fourths</p></div>    
    <div class="pure-u-1-4"><p>Fourths</p></div>
</div>



 

 

Pure Grids are useful when you want to present pictures, text boxes, or any other form of information in a structured manner. You could create a picture gallery, a contacts page, or anything else that comes to mind.


Pure Responsive Grid


Pure has a mobile-first responsive grid system that can be used declaratively through CSS class names. It's a robust and flexible grid that builds on top of the default grid. You can include this responsive grid system in your projects by including the following <link> tag in your page:

 

 

 

<!--[if lte IE 8]
    <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/grids-responsive-old-ie-min.css">
-->
<!--[if gt IE 8]-->
    <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.6.0/grids-responsive-min.css">



Pure Responsive Grid allows you to include additional class names like pure-u-md-2-5 to control the width of different elements at specific breakpoints. Take a look at the following HTML:

 

 

<div class="pure-g">  
  <div class="pure-u-1 pure-u-md-1-4"> One </div>  
  <div class="pure-u-1 pure-u-md-2-10"> Two </div>
  <div class="pure-u-1 pure-u-md-2-10"> Three </div>
</div>






 


The above code does the following: When the screen size is smaller than 568px, all divs will be 100 percent wide. When the screen size is above the medium screen category (768px) the first div is set to a width of 25 percent while others are 20 percent wide each.

 

An example of a responsive Grid:


Screen Shot 2015-06-23 at 4.40.08 PM.png


Responsive Images


When using Responsive Grids, you'll want your images to be fluid as well so they grow and shrink with the content while maintaining the correct ratio. To do this, just add the .pure-img class on them.

 

 

Screen Shot 2015-06-23 at 4.43.20 PM.png


For more details about the grid system, you can check out Pure’s documentation.


Menus Galore


Pure offers a variety of different menu types ranging from drop-down menus to vertical menus with submenus. Minimal styling and use of low specificity selectors make it a lot easier to customize the menus.


Vertical Menus


The default menu type. Here is the code to create a vertical menu:

 

 

<div class="pure-menu">  
     <span class="pure-menu-heading">About Us</span>  
       <ul class="pure-menu-list">    
        <li class="pure-menu-item">      
         <a href="#" class="pure-menu-link">Home</a>   
       </li>    
         <li class="pure-menu-item">      
         <a href="#" class="pure-menu-link">Products</a>    
        </li>    
        <li class="pure-menu-item">      
       <a href="#" class="pure-menu-link">Contact Us</a>   
      </li>    
    <li class="pure-menu-item">      
   <a href="#" class="pure-menu-link">Blog</a>   
   </li>  
</ul>
</div>








You can easily change this vertical menu to a horizontal menu or scrollable menu  by adding the class name pure-menu-horizontal and pure-menu-scrollable respectively to the pure-menu <div>. You can also mark a menu item as selected, disabled using the class names pure-menu-selected, and  pure-menu-disabled.


Drop Down Menus   


Creating a dropdown menu requires small changes in the markup. You need to add the class name pure-menu-has-children  to the appropriate menu item. To display the submenu on hover, include the class name pure-menu-allow-hover. The code snippet below should help clear things up:

 

 

<div class="pure-menu pure-menu-horizontal"> 
 <ul class="pure-menu-list">    
<li class="pure-menu-item pure-menu-selected"> 
     <a href="#" class="pure-menu-link">About</a>
    </li>
    <li class="pure-menu-item pure-menu-has-children pure-menu-allow-hover">
        <a href="#" class="pure-menu-link">Services</a>
        <ul class="pure-menu-children">
          <li class="pure-menu-item">
            <a href="#" class="pure-menu-link">Designing</a>
          </li>
          <li class="pure-menu-item">
            <a href="#" class="pure-menu-link">Marketing</a>
          </li>
          <li class="pure-menu-item">
            <a href="#" class="pure-menu-link">SEO</a>
          </li>
        </ul>
    </li>
  </ul>
</div>







Example of Dropdown menu:


Screen Shot 2015-06-23 at 4.47.04 PM.png


 

You can also include this example script written in vanilla JavaScript to make the menu more accessible. It provides ARIA support, submenu arrow key navigation, and menu dismissal based on outside events.


Vertical Menu with Submenus


The same construct used to create dropdowns works in vertical menus as well. You may nest submenus, but keep in mind that complex menus can present usability challenges on small screens.
For more details about the menus, check out Pure’s documentation.


Purest of Forms


Pure’s form options include plain forms, forms within grids, grouped inputs, and more.


Default Forms


To create a form with Pure you need to add the class name pure-form to the <form> element, as shown below:

 

 

<form class="pure-form">
  <fieldset>
    <legend>Pure Login Form</legend>
     <input type="email" placeholder="Email">
    <input type="password" placeholder="Password">
     <button type="submit" class="pure-button">Sign in</button>
  </fieldset>
</form>








Which would create:



Screen Shot 2015-06-23 at 11.52.36 AM.png

 


To create a stacked form with input elements below the labels, add the pure-form-stacked class name to a <form> element alongside pure-form.



Screen Shot 2015-06-23 at 11.55.51 AM.png

 


Grouped Inputs


To group sets of text-based input elements, wrap them in a <fieldset> element with a pure-group classname. Grouped inputs work well for sign-up forms and look natural on mobile devices. Below is the code for a Grouped Input Form:

 

 

<form class="pure-form">
    <fieldset class="pure-group">
        <input type="text" class="pure-input-1-2" placeholder="Username">
        <input type="text" class="pure-input-1-2" placeholder="Password">
        <input type="email" class="pure-input-1-2" placeholder="Email">
    </fieldset>

    <fieldset class="pure-group">
        <input type="text" class="pure-input-1-2" placeholder="A title">
        <textarea class="pure-input-1-2" placeholder="Textareas work too"></textarea>
    </fieldset>

    <button type="submit" class="pure-button pure-input-1-2 pure-button-primary">Sign in</button>
</form>

 

 

Screen Shot 2015-06-23 at 12.00.30 PM.png

 


Input Sizing


Input elements have fluid width sizes in a syntax that is similar to Pure Grids. You can apply a pure-input-* class to these elements.
You can control input sizing even further by wrapping them in grid containers. In the example below, the <input> elements have a pure- class, but are wrapped in a <div> with a specific grid class.



Screen Shot 2015-06-23 at 4.37.31 PM.png

 


This is only the tip of the iceberg for forms, check out all the other features offered.


Extending and Customizing Pure


If you are not satisfied with the grid system that Pure provides you are free to create your own. You can use the starter kit tool on the official website to create your grid system. You may define your own breakpoints and grid units and you can specify the class names to use. Like any framework, you can add additional styling on top of minimal styling that Pure provides. For example, you can define the styling of a “success” button:



.success-button {
  background: rgb(28, 184, 65);
  color: white;
  border-radius: 0px;
}



 

 

Then all you have to do is add the .success-button class from that rule set to a button element, to go along with Pure’s existing styles:

 

<button class="pure-button success-button">Success</button>

 


If you’re familiar with CSS, then this is nothing new, but beginners can see how this and other frameworks allow you to customize the base look of existing components with new styles.


Pure Plays Well with Others


Pure has no issues with simultaneous use of other frameworks like Bootstrap and jQuery. For example, you can use Bootstrap’s modal.css and modal.js with Pure. This provides a lightweight way to create a modal. Here is the sample code:

 

 

<button data-target="#myModal"   class="pure-button" data-toggle="modal"> Launch The Modal</button>
 <div id="myModal" class="modal fade" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h1 id="myModalLabel">A Functional Bootstrap + Pure Modal</h1>
      </div>
       <div class="modal-body">
        <p>To create the modal you just need to include the <code>modal.css</code> and <code>modal.js</code> file from Bootstrap. Pure will take care of all low level styling. The result is a fully-functional Modal using just a fraction of the CSS.        </p>
     </div>
       <div class="modal-footer">
        <button type="button" class="pure-button"   id="success-button" data-dismiss="modal">  Close  </button>
      </div>
    </div>
  </div>
</div>






 


As a developer, you can pull in Pure as a foundational CSS framework, and then include specific Bootstrap or jQuery modules that your application may require. There are several benefits to doing this:

  • Your website or webapp's CSS will be a lot smaller — up to 5X smaller in some cases!
  • You get Pure's minimalist look that's easy to build on top of. No need to overwrite styles!
  • You can take advantage of Bootstrap's ecosystem without pulling in a monolithic Bootstrap CSS file.


regardless of its small size, Pure can meet most of your UI design needs. Give it a try and see how it fares. Follow the development of Pure and see what's new.


What gets measured gets managed” - Peter Drucker

 

Which parts of your RhoMobile app are being used most? Which parts are almost never being touched? What part of your app causes people trouble and drives them away? If you don’t have answers to those basic questions, it’s impossible to know what you should work on for version 2.0. Fortunately, there’s an easy way to find these answers.

 

Google Analytics let you track anything from eCommerce transactions to custom goals and conversions. Google even offers a feature where you can view your analytics in real time.

 

But all of these benefits are useless if we don’t know how to implement them. In this blog post, we are going to go over how to put analytics in our RhoMobile application and how to track pageviews, events, and social interactions using Google's javascript library, Analytics.js.

 

You can download the starter code for this tutorial

 

Getting Analytics Set Up in Your Code

1. Download the files to your desired workplace or simply clone the Git repository to your Desktop.

 

  Note: If you already have a tracking ID for your mobile web app, skip steps 2 and 3

 

2. Go to the Google Analytics page and sign in. If you don't have an account, create one.

 

3. Now go to the Admin tab, click on the drop down menu for “Property” and choose “Create new property”. When asked what you would like to track, pick website since we are creating a mobile web app. Fill out the rest of the fields however you wish and press “Get tracking ID”.

 

 

 



4. Go into Public/www/index.html and add the following snippet of code right before </head>.

<script type="text/javascript">
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','http://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-XXXXX-YY',{
  'storage': 'none',
  'clientId':'92bf24a5-20e5-4181-9778-2835f28c52d8'
  });
  ga('set', 'checkProtocolTask', null);
ga('send', 'pageview', {'page': '/my/home-page'});
  </script>




 


Now replace UA-XXXXX-YY with your tracking ID to enable Analytics.js inside of your RhoMobile application.

 

In future applications, you are going to want to add this snippet in the head of every HTML page that your users are going to visit.

 

Note: When working with mobile web apps and Google Analytics, there is no domain to store the data with. Instead, we need to turn off cookie storage in Universal Analytics and pass in our own clientId value. You have the following options for how to do this:

  • You can set your own clientId value if your users are logged in. Google recommends a UUID v4 value.
  • ga('create', 'UA-XXXXXX-YY', {
    'storage': 'none',
    'clientId': '92bf24a5-20e5-4181-9778-2835f28c52d8'
    });
    ga('send', 'pageview', {'page': '/my/page'});
  • You could generate a random one and implement logic to store it in localStorage of the browser. (code not shown for this option)


Now that we have the initial set up for Google Analytics, we will add some code to track the analytics we’re interested in.


Tracking Pageviews

One of the first things we want to know is which pages our users visit. Knowing popular pages can help us make sure we are focusing our time and efforts on the right parts of our app.


To do this, we add pageview tracking to all of our main pages. Go into public/www/js/HomeView.js, and then into the function this.render(). Add the following line right before return this;      

ga('send', 'pageview', {'page': '/socialMedia'});

 


This will notify Google each time a user is about to search for a friend on our Zebra Social Network. pageview indicates that we’re tracking a pageview as opposed to a transaction or an event. {'page': '/socialMedia'}); has Google Analytics  associate our action with the page /socialMedia and will let you know this page is where the action took place. We could make the page whatever we wanted though: {'page': '/WhateverWeWanted'});


Go into EmployeeView.js and add the following line inside of this.render() and right before the return statement.

ga('send', 'pageview', {'page': '/social/' + employee.firstName});






Now we know which friends’ profiles our users visit the most.


We also want to know when people visit our supplies store but don’t purchase anything. Go into suppliesHome.js and add the following line inside of this.render() and right before the return statement.

ga('send', 'pageview', {'page': '/visitStore'});



These three simple lines of code will keep us up to date on our users’ behavior and how the user traffic flows through our app.

 

Pageview tracking is useful, but there is more to analytics than what pages our users visit.


Tracking Events

Are a lot of people having problems with our technology and in need of IT help? A single line of code will give us the answer.


Go into useful_tools.js and into this.help(). Add the line:    

ga('send', 'event', 'location', 'search', {'page': '/office/location'});



Add another event call inside of suppliesHome.js. Go into this.buy() and add the following code:

ga('send', 'event', ‘supplies’, ‘buy’, {'page': '/supplies/buy'});

 


This will notify us whenever someone buys office supplies, helping us keep track of purchases.

 

The format of an event call is:

ga('send', 'event', 'category', 'action', {'page': 'page name'});

 


This is important to know for customizing your own events in the future. For our event, the category is supplies and the actions is buy, but you can put whatever you like for these fields.

 

Tracking events is useful for measuring the effectiveness of almost any function or feature. But how do we track something less tangible such as a user’s social behavior and interactions.


Social Interactions

Social Interaction Analytics allows you to measure the number of times users click on social buttons embedded in webpages. For example, you might measure a Facebook "Like" or a Twitter "Tweet." The breakdown of an analytics call for Social interactions looks like this: 

ga('send', 'social', 'social network', 'social action', 'social target');






We going to use social analytics to track our Zebra social network and see which users are using our ‘Like’ feature. Go into the EmployeeView.js and into this.thumbsUp(). Add the following line before the alert:

ga('send', 'social', ‘Zebra social network’, 'like', {'page': '/' + employee.firstName + '/like'});

 


Every time someone uses our mobile app and likes an employee’s work on our social network, Google Analytics will know all the details about it.


Let's look at how to put this all together and track a combination of analytics and track progress towards a specific goal.


Goals

Goals measure how well your site or app fulfills your target objectives. A Goal represents a completed activity, called a conversion, that contributes to the success of your business or application.

To create a goal:

 

  1. Log into Google Analytics
  2. Go to Admin→ View→ Goals→ +New Goal
  3. Pick a name, a Goal ID and set (you can have multiple goals as part of the same set), and a type of goal, and then hit next.
  4. Depending on the type of goal you picked, you will be asked to fill out specific Goal oriented details that let Google Analytics know when your goal is hit and how much that goal is worth.

 

  (Pic of details for an event based goal)

 



In the pic above, our goal is an event goal. It is hit if the category of a recorded event is equal to ‘category’. We set the value of each conversion at $5.00. Google Analytics will keep track of how much money we’ve made through all our goals and conversions and will add $5.00 to the total every time this goal is reached.

 

Where to Read the statistics we gathered

All the statistics we’re tracking can be found at Google’s analytics page after you log in.


Screen Shot 2015-06-15 at 3.13.04 PM.png

 

Real Time View

Google's Real Time view lets you watch certain analytics such as events, pageviews, and user locations be displayed in real time, which doubles as a great way to test out your code for Analytics.js and see that you are implementing it right.

 

google-analytics-real-time.png

 

Google can take 24-48 hours to display all other analytics (social interactions, eCommerce, etc)

 

Conclusion

With only a few lines of code, we were able to track almost all the features and screens viewed in our application, giving us tons of data to work with. We can track which features users love the most, which screens they spend the most time on, and most importantly, what to keep, get rid of, and improve upon.

 

This has only scratched the surface of what you can use Analytics.js for. Check out more useful features and experiment with them in your next RhoMobile application.

Filter Blog

By date:
By tag: