Skip navigation
1 2 3 Previous Next

RhoMobile Blogs

99 posts

Tau Technologies has been supporting RhoMobile since before it was open sourced. The newest updates to the tool have been provided by them.  We are pleased to announce they have recently opened up a developer forum.  This is a place to ask your development questions and new feature requests around the RhoMobile Suite.  Learn more at http://forums.tau-technologies.com.

 

 

 

 

Robin West

RhoMobile End Of Sale

Posted by Robin West Expert Dec 2, 2016

Since May 2016, RhoMobile Suite was fully open-sourced, enabling the platform to grow, and not be limited to what Zebra can provide. While RhoMobile continues to support both Zebra enterprise devices and consumer devices, many customers utilize the platform for consumer OS devices. Because of this new direction, Zebra will end the sale of RhoMobile suite effective immediately. To assist with this transition a grace period of support will continue through end of June 2017.

 

Developers: At the beginning of January 2017, we will no longer be supporting RhoMobile through the Developer Portal.  You will be able to search the forums until June 2017, but we will no longer allow new questions effective January.  See below for your support options.

 

Effective immediately, RhoMobile Suite versions 2.x to 4.x will reach its end-of-sale on Zebra portfolio and RhoMobile Suite version 5.0 and 5.x will reach its End-of-Sale state for licenses.

 

With these changes, the developer community can select options when creating their applications:

  • For those using cross-platform solutions, RhoMobile supports this through web skills. As a migration path off Zebra, partners TAU Technologies and Kutir Mobility will continue to update and support the open source software.
  • For customers with existing browser solutions, as well as new customers, Enterprise Browser (EB) suits this segment.  EB continues to be a feature-rich web application. New features are developed to align with Mobility DNA and new Zebra device support.
  • For native applications, utilize EMDK for Android.
  • For cross platform applications and developers who have .NET skill set, utilize EMDK for Xamarin.

 

  For continued Developer support for this product please contact TAU Technologies or Kutir Mobility  effective immediately.  Zebra will maintain this page during the transition period through June 2017, but will cease accepting new forum posts beginning January 1, 2017

 

FAQ

1. Q: I have RhoMobile in our application suite, can I still use it?

A: Yes, however, between now and June 30th, 2017 you must decide whether you migrate to Zebra's Enterprise Browser, EMDK for Android, EMDK for Xamarin or stay with Rho, but transition to one of Zebra's partners Kutir or Tau to get the necessary product support. After June 30th, 2017, Zebra will no longer sell Rho and its associated service contract.  It will only honor existing service contracts until they expire. 

 

2. Q: I have a service contract active for the licenses I purchased, will that be impacted?

A: No, Zebra will honor this service contract, provide the necessary technical support until your contract end date. Contract renewals will no longer be available.

 

3.  Q: I have an existing Service Contract expiring in April 2020, will I get the technical support I need?

A: Yes, contract will be honored until it expires in April 2020. No contract renewals will be made available.

 

4.  Q: What if I want to keep running my application with my existing licenses (2.x-4.x) and don’t need support – can I keep using/running that application?

A:  Customers can keep running their existing 2.x – 4.x applications without problem.

 

5. Q: Will I still be able to build new RhoElements applications after my Silver / Gold subscription period ends?

A: No.

 

6. Q: Does this affect my RhoConnect license?

A: RhoConnect will no longer be supported but previously distributed perpetual licenses will continue to function.

 

7. Will applications hosted in the RhoMobile.com cloud (http://rms.rhomobile.com, formally known as RhoHub) continue to be supported?

            A: No, you should backup any applications currently stored in the RhoMobile.com cloud and transition to offline builds.

 

8. Q: How do I transition to the open source RhoMobile Suite?  Where is the open suite forum and code?

A: These questions are covered in the Open Source FAQ: https://developer.zebra.com/community/rhomobile-suite/rhomobile-community/rhomobile-blogs/blog/2016/03/30/rhomobile-open-source-faq

 

9. Q: I do not have a license but have built apps using RMS 5.x targeting only Rhodes, will this affect me?

      A: No, you will want to move to the latest open source version of RhoMobile Suite which contains additional features and supported products.

Tau Technologies has released a new version of RhoMobile 5.5 that includes many new features, updates and enhancements.

 

New Features Added

● iOS 10 Support.

● Android M Support.

● Android N Support.

● Crosswalk web engine for Android. Version of Crosswalk is 19.49.514.5.

● Android M: app permissions requested at runtime if app built with SDK 23+.

● WebKit web engine for Windows Mobile/CE.

● Android: added openssl-edge and openss.so-edge into rho-tau-extensions. OpenSSL version for this extensions is 1.1.0-stable. You may need this upgraded version to submit application to the Google Play store.

 

Enhanced Features

● Android SDK 23 supported.

● Latest changes in Android build tools supported (like Maven local repository dependencies etc).

● Android Camera API improved.

 

For full details please see:

Release Notes: http://rhomobile.tau-technologies.com/5.5/5.5.0/5.5ReleaseNotes.pdf

Documentation: Rhomobile | Home

Why go open source?

To enable the platform to grow as a whole and not be limited by what just Zebra can provide.

RhoMobile is only one part of a set of tools and utilities that developers can use to build enterprise grade devices. We provide multiple options so developers/customers can choose what best fits their skill set and solution requirements:

  • RhoMobile for cross-platform using web skills
  • EB for web-based applications
  • EMDK for Android, for native apps
  • EMDK for Xamarin, for cross platform apps for developers who have .NET skill set

RhoMobile supports both Zebra enterprise devices and consumer devices.  We recognize that there is wider interest in RhoMobile beyond Zebra devices, as many customers use the platform for consumer OS devices.

 

Please visit the RMS Open Source Forum.

 

What are the benefits of going open source?

By taking RhoMobile open source, we believe our customers and partners can better recognize the benefits open

source brings:

  • Innovation Acceleration: Companies and individuals can move faster, build more quickly; customers & partners can gain control of their IT investment; and all parties can benefit from each other’s enhancements, new ideas and challenges to the status quo.
  • Better Software: When work is visible to the world, culturally developers tend to build better code – architecting their open-source code to be extensible, have minimal dependencies, and stable APIs.
  • Community Learning: Encourages adoption for both Zebra & non-Zebra devices – advancing the platform as an industry platform, not just a Zebra platform.
  • Share Challenges: During an era of scale, complexity, and ever-evolving operating systems, open source allows for collaborative creation of scalable solutions to new problems – making projects evolve faster.
  • Control: Provides control to customers and partners using the platform – opening up the option to add new features, new OS support and more, specific to their solutions.

 

What are the migration options?

There are three migration options: 

  • Stay on Zebra releases. For customers who are satisfied with their current version/solution, Zebra will continue to sell and support existing versions of RhoMobile Suite (2.x through 5.4). There is no need to move to open source.
  • Migrate to Enterprise Browser. If you are using older versions of RhoElements for a web-based application, you may continue to do so, or you have the option to migrate to Enterprise Browser if new features or new device support is required and is covered by Enterprise Browser.
  • Migrate to RhoMobile Open Source. If your solution(s) require new platform enhancements, new OS support (such as iOS 10, or Android M) or new device support you will be able to migrate to the open-source platform – based on your own timing/need. Customers desiring assistance on the open-source platform can work with the open-source community as well as our partners, Tau Technologies and Kutir, who are committed to offering support options.

 

Which components of the current RhoMobile Suite will not be open-sourced?

  • Hosted cloud builds will remain available to customers with subscriptions up to version 5.4. Customers using open source will be responsible for their own build environments or will be able to take advantage of environments if offered by other partners.
  • Support for the Windows M/CE platform webkit is a third-party proprietary component that cannot be open sourced. Customers with solutions that use/need Zebra WinM/CE devices can remain on their current version of RhoMobile up to version 5.4.
  • The WM/CE framework APIs will be open sourced without the webkit – allowing the community to provide their own webkit replacement or work with a partner such as Tau Technologies who is working on a webkit option.
  • The RhoElements RFID plug-in for Windows Mobile/CE will not be available as open source.
  • Open Source components of the current RhoMobile Suite include:
  • Rhodes
  • RhoStudio
  • RhoElements
  • RhoConnect (client & server)

Enterprise Browser and AppGallery use RhoMobile – will they be open sourced?

No. Although built on the RhoMobile framework, both are considered separate products and will not be transitioned to open source. They will remain separate offerings as they are today.

 

Where can I find the source code?

The open sourced version of RhoMobile can be found on GitHub: https://github.com/rhomobile/rhodes

 

What resources will still exist on Launchpad?

The resources on the RhoMobile space in Launchpad will refer to the RhoMobile 2.x - 5.4 versions of RhoMobile Suite. If you are using these versions, then these resources apply to you. If you are using the open source version, you should consult the readme documentation in the open source repo.

 

Will the pricing structure change?

Prices for existing closed-source versions will not change. Versions 2.x, 4.x and 5.x will continue to be sold and supported. The open-source release will be free to the public to use and provide contributions. Customers desiring support on the open source line will be able to purchase support from our partners, Tau Technologies and Kutir.

 

Will my existing Silver or Gold subscription change?

Silver and Gold subscriptions used with version 5.x will continue to receive support. If you want to move to the open-source line, you may work with the community for free, or purchase support from support partners.

 

What happens if I am using RhoMobile v4.x or v2.x?

There are no changes to RhoMobile v4.x and v2.x – they will not be open-sourced. You  may continue to purchase support and licenses for your solution on these versions.

 

How will I get bugs fixed?

If you remain on your current version (v2.x, v4.x or v5.x) and have a current support contract, you will follow standard procedures and submit bugs to the ZenDesk Support Portal. If you move to the open-source code line, you will have the ability to hire support from other vendors also using/supporting the open-source software, or, address the bug and submit the fix back to the community.

 

Isn’t RhoMobile already open source?

Only Rhodes and RhoStudio are currently open source. With this move, we will be open-sourcing all enterprise grade capabilities including RhoElements enterprise API’s and our enterprise grade data synchronization engine RhoConnect and RhoConnect Client.

 

Will RhoMobile be included in the Gartner MADP Quadrant in 2016? 

No. Once RhoMobile is open source, we will no longer have a commercial offering on the open source release and will not qualify for the quadrant.

 

How do I engage with partners if there is a customer requirement for RhoMobile?

RhoMobile partners, Tau Technologies and Kutir, have committed to offering support options for the open-source platform.

Dear RhoMobile Users:

 

To encourage and expand opportunities for innovation, Zebra is committed to building a thriving developer community that enhances RhoMobile beyond what Zebra can do alone. To accomplish this, we will be making RhoMobile Suite an open-source software in Q2 2016.

 

Zebra will continue to sell and support existing versions of RhoMobile Suite (2.x through 5.4). If you are satisfied with your current version, there will be no need to move to open source.  If you are developing a solution that requires new platform enhancements, new OS support (such as iOS 10 or Android M) or new device support, you will be able to make the move to the open-source platform at your own pace.

 

The following portions of RhoMobile Suite will NOT be open-sourced:

  • Hosted Cloud Builds: Will remain available to customers with subscriptions up to version 5.4. Open source users will be responsible for their own build environments or will be able to take advantage of environments offered by other partners.
  • Windows M/CE Platform Webkit Support: Being a third-party proprietary component, this cannot be open-sourced. Customers with solutions that use/need Zebra WinM/CE devices can remain on their current version of RhoMobile up to version 5.4.
  • WM/CE framework APIs will be open sourced without the webkit – allowing the community to provide their own webkit replacement or work with a partner such as Tau Technologies, who is working on a webkit option.

 

For more information on how the transition to open-source will affect your subscription to RhoMobile Suite, please see the attached RhoMobile Suite FAQ. If you are in need of assistance on the open source line, our partners Tau Technologies (Tau-Technologies.com) and Kutir (www.kutirmobility.com) will be providing support options.

 

For more info, read the RhoMobile Open-Source FAQ.



Time is running out to get the best price on AppForum, this year's must-attend conference for Zebra partners and developers. It's happening Sept. 21-23 at the Hard Rock Hotel and Casino in Las Vegas, and will feature a keynote from Zach Shelby, co-founder and former CEO/CTO of Sensinode, which ARM acquired in Aug., 2013, to accelerate its IoT strategy. Shelby was recipient of the Nokia Foundation's 2014 award for his pioneering work in developing networks of low-power IP devices and sensors.


Zach Shelby, dir. of technical marketing at ARM, and Tom Bianculli, VP of Zebra's Enterprise Technology Office.


Following Shelby's keynote will be Tom Bianculli, vice president of Zebra's Enterprise Technology Office, and is responsible for exploring new opportunities, development initiatives and strategies. Bianculli himself holds more than 20 U.S. patents, and is a Zebra Distinguished Innovator and Science Advisory Board Associate.


This year's conference also delivers more than 50 talks and hands-on workshops to guide and instruct developers through today's most relevant topics, including two sessions on the Zatar IoT and MSM development infrastructure, more than 30 sessions on Android development, five on the use of Zebra's RhoMobile cross-platform IDE, seven on printing, plus lightning talks covering strategies for targeting healthcare, retail, manufacturing and transportation and logistics vertical markets.


 


Of particular interest might be sessions on technologies new to the Zebra portfolio, including All-Touch Terminal Emulation, which can enliven older TE apps with a touch overlay; new cordless scanner SDKs; iFactr, a virtualization toolset for running Windows Mobile/CE apps unchanged on Android devices; how to use Xamarin to build new or migrate existing .NET and C# apps to Android; and enterprise use cases for smart glasses, Enterprise Browser and Zebra's emerging tablet strategy.


In case that weren't enough, there's also a Hackathon, which developers can dive into immediately after they register. Developers can work individually or in teams to build a vertically-targeted mobile app using tools and operating system of their choosing. Extra points will be awarded for using Zebra's tools and locationing technologies. The first 100 registrants will receive two Zebra MPack locationing  beacons, and the grand prize will be something truly special.


Hurry. You have until Monday, Aug. 31, to register for
just $99 and lock in a room rate of $84 per night in Las Vegas. And you might even win your very own drone.  Find out more at zebra.com/appforum, or send questions directly to appforum@zebra.com. And if you can't make to Las Vegas, you might consider AppForum London, Oct. 12-14 or AppForum Hong Kong, Nov. 16-18.

If you were tasked with designing a car, the first few requirements would obviously be that it start, that it go where the driver intends, and that it stop safely when it arrives. Your initial interfaces might include gas and brake pedals, a steering wheel and perhaps a shifter to control direction. Testers of your new device would immediately complain that it's difficult to drive while standing up. You add a seat. Notice a pattern?


01_Dilbert_requirements.png

Dilbert cartoons copyright: United Feature Syndicate Inc.


The more something is tested, the greater the revelations of what's required. The same applies to application design. It is therefore critical to involve testers, users and other stakeholders early in development, when it's easy to make changes. This should generally begin with the requirements phase.


Here's a look at some best practices for determining what's required of your application, and some requirements that should be on your list every time. 


REQUIREMENT ANALYSIS

The decision to create an app generally stems from a business need. For Zebra customers, that typically involves reading barcodes or RFID tags to register sales, manage inventory, or to track packages, pallets or patients.

 

Behind all apps are the Product Goals, which might include:

 

  • Collecting, storing and forwarding data
  • Actions or processing the data
  • Physical output such as an invoice or receipt
  • Additional actions by the user or customer such as swiping a card
  • Information displayed to the user or customer as on a kiosk


User Needs are equally important to the analysis, and the need of the app's intended users should be gathered during the pre-prototype stage. Aside from user-stated requirements, your apps always should include the following attributes:

  • Easily discoverable features and functions
  • An intuitive, efficient workflow that's easy to learn
  • Simple, non-destructive recovery from erroneous actions
  • Interruption handling; recoverability from long pauses in activity
  • Enjoyable, satisfying user experience that includes visual feedback


To build your app with easily discoverable features, present only the functionality that's required to reach the goal. Avoid splash screens that must be manually dismissed, and present all app functions using a single page design, but only if it's possible to do so without clutter. Refer to Touch Target Best Practices for guidance on the presentation layer.

 

02_Three_inventory_shots.png

These screens are part of Zebra's tutorial for building a simple inventory app.


An efficient workflow presents the minimum number of steps to get things done, particularly for the app's primary functions. That's one of the keys to a helping you build a satisfying user experience that's easy to remember and that enables workers quickly gain proficiency. Conversely, deleting records and other destructive or irreversible tasks should require at least two steps to complete and be located away from the app's main functions. This helps ensure that erroneous actions don't end in data loss.


Among the most overlooked attributes of an app is how it will respond after long periods of inactivity. If the user is unexpectedly called away from inventory tasks to help out at the register, for example, will your app timeout and cause records to be lost? It's important to design your app to recover gracefully from interruptions in workflow, which are inevitable in most line-of-business usage scenarios.


People should feel confident while using your software and never wonder whether a task has been left undone. Build screens that provide feedback on all activities. This gives the user a sense that the're in complete control of whatever they're doing. If multiple screens are necessary, all screen interfaces should work in basically the same way, and each should have a "Back" button.

 

03_Prototype.png

 

PROTOTYPING

With requirements close at hand, develop a prototype of your app that blocks out its basic workflow and present it to all stakeholders as soon as possible after the requirements meeting. The mock-up screens don't have to contain any underlying logic, but should be detailed enough to facilitate a discussion of the application's basic functions and flows. Incorporate suggested changes into the next version of your prototype and present them to the team once more.


Once you've gotten stakeholder buy-in, you can begin building your app putting logic behind the screens. Convene the team at each major milestone to demonstrate that development is on track and confirm that requirements have not changed.

 

04_Dilbert_requirements.gif

Dilbert cartoons copyright: United Feature Syndicate Inc.


Business and competitive pressures often can lead to changes in features or requirements of an app while it's still in development. Major features or minor, at some point the decision must be made to freeze an application's feature set and implement those changes in a future version. This decision usually varies by project and by company.


One way to fight feature creep is to adopt an MVP philosophy, that is to focus on creating a minimal viable product. Rather than trying to build an app that is all things to all users, narrow the focus to include the business goals and nothing more. Concentrate on doing one or two things extremely well, and leave the rest for an update or another project altogether.


SOURCES

SmarterPlanet: Oft-overlooked requirements

OpenRoad: Mobile-UI Design Best Practices
TechTarget: Defining Requirements
Appmethod Blog: Top 5 Best Practices for Mobile UI Design
Dilbert cartoons copyright: United Feature Syndicate Inc. 

Angular JS is an immensely popular JavaScript framework for creating dynamic, responsive web pages with built in support for MVC.  Angular can be used for desktop, tablet or mobile applications and is the engine behind Ionic which there was a recent previous blog about.  Any framework that renders the application’s view in an HTML5 compatible browser can build their applications in Angular and Rho is no exception.  Other recent blogs have gone into more detail on Angular specifics but I wanted to share my experience on using Angular and Rho together.

 

If you’re new to Angular I highly recommend following their standard tutorial that runs you through the basics of creating an Angular apps and some of the fundamental concepts, there are countably infinite tutorials on Angular JS already on the web so for the rest of this blog I’ll assume at least a basic working knowledge of Angular.

To get started I’ve uploaded a RhoElements application that uses Angular to github, https://github.com/darryncampbell/ng-rho-demo.  You may notice more than a passing resemblance to the official Angular tutorial. Please go ahead and clone the demo to your local disk.

 

Pre-Requisites:

You’ll need NodeJS installed on your machine and accessible from a command prompt in order to get going as well as RhoMobile Suite.  I've developed this example with RhoMobile Suite 5.1 so I recommend you use that but previous versions should also work.

Since it’s easier to develop web applications on the desktop it makes sense to start there.  Navigate to the public directory of ng-rho-demo and install the node dependencies:

 

/ng-rho-demo/public>npm install





 

This will install the dependencies of the application including angular and a local http server we can use to test our application.  Start the server with the following command

 

ng-rho-demo/public>npm start





 

Now launch your favourite web browser and navigate to http://localhost:8000 to see the application.

 

To see the application running without any of the above steps then head over to http://darryncampbell.github.io/ng-rho-demo/public/index.html

 

Since the application UI is designed for mobile devices you’ll see it does not render properly on the desktop in the detail view.  Chrome has a “device mode” to simulate viewing the page on a device,

https://developer.chrome.com/devtools/docs/device-mode that makes the page render well.

 

device view.png

 

What is happening?

At this stage we have no dependencies on Rho, we are just viewing the Angular application in our web browser.  Since Angular relies on a lot of dynamic JavaScript, the browser will prevent it running from the local file system so we need to run a local server.  I recommend having a look through the code at /ng-rho-demo/public and noticing that this is just a standard Angular application:

  • Bower_components: The dependencies installed by bower including Angular itself
  • Css: The application’s CSS.  I’m not a graphic artist.
  • Img: The images of Zebra devices which will be shown in our application
  • Js: JavaScript files:
    • App.js: Angular bootstrapping and routing
    • Controllers.js: Controllers for our device list and device detail view.  Notice how the detail controller makes use of the Rho barcode API to enable the barcode scanner
    • Directives.js: empty
    • Filters.js: An example filter that prepends the barcode data with an arbitrary string depending on the scanned data.
    • Rhoapi-modules.js: This is the Rho API as you’ll find in any Rho application
    • Services.js: Empty.  this blog describes how you can expose the Barcode API through an Angular service should you choose to do so but in this example we take the more simple route.
  • Node_modules: The dependencies installed by npm.  Take care to delete this folder prior to building your Rho application as it will be quite large.
  • Partials: The views associated with both the list and detail pages
  • Devices: Information on the Zebra devices

 

Compiling for Rho

 

So far, everything we have done has been in the web browser with no Rho dependencies but now we want to use Rho for two things in this example:

  1. Produce a native package so we can install it as an application on our mobile device
  2. Take advantage of native device features through JavaScript

 

Firstly, add in JavaScript capabilities by uncommenting the rhoapi-modules.js script include in the following file: ng-rho-demo/public/index.html

 

  <script src="js/rhoapi-modules.js"></script>




 

Important: Before compiling make sure you delete the following folder: \ng-rho-demo\public\node_modules as this contains node dependencies and is far too large to be included in the native app

/ng-rho-demo/public>rmdir /S node_modules
/ng-rho-demo/public>cd ..
/ng-rho-demo>rake device:android:production




 

The application is now building for Android provided you have RhoMobile Suite installed and have configured it appropriately to point to your Android build chain.

 

Whilst the application is compiling, open up the following file: ng-rho-demo/public/js/controllers.js and observe the DeviceDetailCtrl controller:

 

if (typeof Rho !== 'undefined')
  {
    Rho.Barcode.enable({}, function(scan)
      {
        $scope.barcodes.push(scan);
        $scope.$apply();
        window.scrollTo(0,document.body.scrollHeight);
      })
  }




 

Here we test for the existence of the Rho namespace which will be present after we included the rhoapi-modules.js file.  This is just a quick (and dirty) way to allow us to develop our application view and business logic on the desktop before moving to the device.

 

The Barcode API is being called just like it is in a standard Rho application, this particular example is adding the scanned data to the model scope which gets displayed in the 'device-detail' view after running through an Angular filter.  You can run this example on any supported Zebra Android device to see the scanner beam but on some of our older models you will also need to ensure that datawedge is manually disabled prior to scanning in Rho.

 

IMG_20150812_104222.jpg

 

Note: If you do not have a RhoElements license you won’t be able to build the application.  You can still use Angular in any Rhodes application, just modify the example to remove “app_type: rhoelements” from ng-rho-demo/build.yml and remove the JavaScript scanning logic from controllers.js.

rhobundle:
  exclude_items:
  - thumb.db
#app_type: rhoelements
capabilities: []




I was inspired to write this blog from a recent post about which JS framework should be used for application development nowadays and it got me realizing that I should have written this a while ago. As you may know, RhoMobile has its roots in Ruby and what you get out the RhoMobile box is centered around Ruby views and controllers. You probably have tried our application generator in RhoStudio and you might have noticed that it starts you off thinking that you have to use JQuery Mobile in your application stack. Not only is it wrong to assume that, quite frankly it is wrong that we even suggest JQuery Mobile as the recommended framework. With its last release date of Oct 2014 and other signs of decline, one has to question the current state of JQM in modern hybrid application development. At the same time Ionic Framework has skyrocketed in adoption, growth and overall awesomeness. Even though Ionic is built on top of another hybrid app framework (Cordova), you can still reap the core benefits in your RhoMobile application and easily deliver a performant hybrid application.

 

Isn't the Hybrid vs Native Debate Over Already?

Before we get into the key features of Ionic and how to use it within RhoMobile, let's quickly summarize some of the key pieces that make up a great hybrid app design:

For those who want to jump right into it, clone this repo and you will have 3 starter Rho-onic apps as well as a demo app that I allude to in this article

  • Single Page App Pattern - if you are still treating your application as individual HTML pages, you are doing it all wrong. SPA is arguably the only way to get the most performance of your hybrid application. I strongly urge you to research this concept and understand the premise behind it, especially from a mobile device point of view.
  • Routing - in order to accomplish SPA, you need to have an easy way to break up your application into routes/states so your code can be maintainable and portable.
  • Templating - right along side routing, you need an easy way to build your UI and separate it into views or individual UI components
  • Mobile First Only - This is probably the most important piece. We all want our app to be as fast as possible and for our users not to notice or even care if it was not developed natively. To get that you want a framework that only cares about running on a mobile device and is designed with that in mind. Other frameworks that are trying to be website and mobile at the same time will never be able to give you 100% of what you need. Your app may fit on the screen automatically, but may not perform as well as it can.

Why I Can't Stop Using Ionic

As touched on above, Ionic is built on top of Cordova but is also includes Angular.JS. Cordova gives you the device APIs, but since we are building a RhoMobile application we will be throwing that part away from our stack. Angular is the foundation of the SPA design and gives Ionic the ability to build on top of that offering you not only great UI components, but also some built in directives and services that you can use in your application with ease. If you are not familiar with these terms, for now just think of them as self-contained intelligent components that you can instantly drop into your application. I don't know about you, but I do not like re-inventing the wheel every time I start a new application. As an added bonus, since the Ionic team is 'Mobile Only', they are already handling some of the things us hybrid-developers struggle with. Like how many times have you tried to figure out how to make a super long list of items scroll and perform as if it was native. Ionic has you covered, as we will explain and show some examples of this further along in this article.

Common UI Components

I am not going to spend a lot of time on this topic, any UI framework worth its weight will give you the common UI components that we need in our application like buttons, forms elements, icons, etc. You can head over to Ionic's site and see them in their glory. As with everything else, Ionic has designed these with mobile performance and usability in mind. They even give you great ways to customize the theme and look so that your app does not look like all other Ionic apps. Many of these components also automatically behave differently on Android and iOS to give you the best native like experience possible.

Screen Shot 2015-07-29 at 12.33.08 PM.jpg

Drop in Greatness

One of the best features in Angular that Ionic takes advantage of is the use of Angular Directives. Angular's docs do not really convey how great these are, but think of them as 'smart html markup'. In short, directives tie some self-contained javascript with html tags and attributes to give your application certain behavior without having to write much code at all. To demonstrate this, let's take a very complicated problem and make it dead-simple: large lists. There is so much work involved in making large lists to perform well in a hybrid-app like keeping the dom small enough without losing the scroll speed, or remembering position when navigating back to the list on page change to name a few. Well, Ionic has included a directive that does all of this for you - all in a few lines of code:

<ion-list>
  <ion-item collection-repeat="user in users" class="item item-avatar">
      <img src="{{user.user.picture.thumbnail}}">
      <h2>{{user.user.name.first | uppercase}} {{user.user.name.last | uppercase}}</h2>
      <p>{{user.user.location.city | capitalize}},{{user.user.location.state | capitalize}}</p>
  </ion-item>
</ion-list>

You will notice the <ion- tags and collection-repeat. This keeps my view nice and clean (not to mention super easy application prototyping) and leaves the complexity to the directive that has already been implemented for me. In my demo app that is included in the Rho-onic Git Repo I am pulling a list of 1000 users and using this directive to give you butter smooth scrolling. I dare you to try and reproduce this performance using your own homegrown techniques.

Screen Shot 2015-07-29 at 12.16.03 PM.jpg

Service With a Smile

Angular provides a foundation not only for smart markup, but it also provides the ability to package up intelligence that you may need to use within your controller logic in the form of services. Services are packaged up and named, so that you simply drop in the service into the controller that you need to use it in. Ionic includes several of these services. For example, say you want to provide an set of options when an item is selected in your application, you can drop in $ionicActionSheet and use it in your controller:

.controller(function($scope, $ionicActionSheet, $timeout) {

 // Triggered on a button click, or some other target
 $scope.show = function() {

   // Show the action sheet
   var hideSheet = $ionicActionSheet.show({
     buttons: [
       { text: '<b>Share</b> This' },
       { text: 'Move' }
     ],
     destructiveText: 'Delete',
     titleText: 'Modify your album',
     cancelText: 'Cancel',
     cancel: function() {
          // add cancel code..
        },
     buttonClicked: function(index) {
       return true;
     }
   });

   // For example's sake, hide the sheet after two seconds
   $timeout(function() {
     hideSheet();
   }, 2000);

 };
});

Screen Shot 2015-07-29 at 9.30.15 AM.jpg

But Wait - There's More

I have only covered a few goodies that come out of the box with Ionic, there are plenty of good reads and reviews for you to Google. The community has exploded in the past year at adopting to Ionic and you can find other open source directives and services that the community has developed that you can use in your RhoApplication. Keep in mind that if any of them require a Cordova plugin, then those will most likely not be fully usable with RhoMobile. Others that simply provide some ui/js goodness should be able to be used without many issues. Here is a nice roundup of must have directives you should consider which BTW was written by an ex-JQuery Mobile ambassador who now professes a lot about Ionic.

One of my favorites is the ionic-datePicker. After adding the corresponding JS and CSS files for this directive, I can easily add a datePicker to app and bind to a data model without writing a single line of JavaScript:

<ionic-datepicker idate="currentDate" disablepreviousdates="true"  disablefuturedates="false" callback="datePickerCallback" disableddates="disabledDates" title="title" mondayfirst="true">
    <button class="button button-block button-positive"> {{ currentDate | date:'dd - MMMM - yyyy' }} </button>
</ionic-datepicker>

You can see this directive includes some options exposed via HTML attributes. The iDate attribute allows me to bind the value of the the chosen date in the datepicker to the local model I am using currentDate which gets displayed on an Ionic button

Screen Shot 2015-07-29 at 12.16.23 PM.jpg

Bionic RhoMobile Apps with Ionic

Ok, so now that you have a thirst for using Ionic inside your RhoMobile application, lets walk through the steps you need to take. This same concept really applies to other frameworks as well. And again for those who want to jump right to it, I have created a Git repo that contains multiple Rho-Ionic starter template apps based on Ionics starter projects. It also contains a demo app that demonstrates the features I mentioned in this article as well as a demonstration of using RhoMobile api's within the framework.

Be sure to install Ionic (npm install -g cordova ionic) and RhoMobile Suite before attempting these instructions

1. Create a new project with RhoMobile

Screen Shot 2015-07-29 at 12.18.39 PM.jpg

2. Create an Ionic project

Open a command prompt and go to the root of the project you just created. Then use ionic CLI to generate a new project. For this example, I chose to create a sidemenu based application called ionic. This will create a subfolder in my RhoMobile application called ionic so I can keep the two separate

Screen Shot 2015-07-29 at 12.20.15 PM.jpg

3. Replace RhoMobile public Folder

The Public folder acts as your 'web root'. If you look at the Ionic folder structure you will see a www folder that is basically the same concept. This is the folder we want to use in our RhoMobile application. Replace the entire contents of public with the contents of ionic\www

Screen Shot 2015-07-29 at 12.26.09 PM.jpg

4. Change RhoMobile Start Path

Open up the rhoconfig.txt file and look for start_path. By default it is setup to use Ruby views and controllers, which we will not be using in this example. Change it to point to the new start page:

start_path = '/public/index.html'

5. Modify Index.html

Remember that Ionic is built on top of Cordova, so it is including the cordova.js file by default. We want to remove this and replace it with the RhoMobile JavaScript api file. This file will be generated when you build your application (because it depends what extensions you include) and will be found in api/rhoapi-modules.js. Yourpublic\index.html` file should look like:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">

    <!-- ionic/angularjs js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>

    <script src="api/rhoapi-modules.js"></script>

    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
  </head>

  <body ng-app="starter">
    <ion-nav-view></ion-nav-view>
  </body>
</html>

6. Tweak Default Wrapper Behavior

There are a few things that drive me crazy about our default app generation, but especially on Android we add a Title Bar and Toolbar to the application that just wastes a lot of real estate and does not fit into the design of application I usually am going for.

First I will remove the toolbar, by opening app\application.rb and uncommenting the default Toolbar line:

@@toolbar = nil

Then I will remove the 'Title Bar' that gets inserted by default on top of the Webview container. I do this by opening build.yml and adding a line under the android section:

android:
  android_title: 0

7. Ready To Run on Device

Ok you are all set to run the app on a device. Go ahead and setup a Run Configuration for Android and check out your masterpiece.

Screen Shot 2015-07-29 at 2.31.09 PM.jpg

 

For actual development, you will probably want to do a lot of your UI and prototyping using Ionic's command line and live reload by going into your projects ionic folder we created above:

ionic serve

This will start a local server and load your app in your browser. You can then work on your JS and HTML and the app will instantly reload. Then have at it with your browser dev tools to ensure your app looks and flows they way you want to. When you are ready to either add device or RHoMobile APIs then simply copy your ionic\www folder to you public folder like we described earlier.

Note: I would not use RhoSimulator for this setup. Quite frankly it is using a different/dated Webkit engine than what you will see on your device. Not to mention Chrome Dev Tools are far superior and you also get Ionics awesome live reload when content changes.

Conclusion and Caveats

Like with any development efforts, there are so many choices and paths you can take to get to the same end result. Overall no matter what sets of components you choose for your application stack, you should fully understand how and why it is behaving the way it does and what aspects will effect your end users perception of how well your application is performing. As mentioned before, don't expect any Cordova plug-ins to work in a RhoMobile application using Ionic, but the rest of the framework will give you many opportunities to get your hybrid application started on the right foundation. I am always interested in hearing different options and suggestions for doing things a better way. For those who may be just starting with RhoMobile and possibly Hybrid application development, I hope you find the starter apps and suggested stack described here useful and a positive experience in your getting started journey.

To err is human; to program, devine. Doing both can result issues ranging from user frustration to total app failure. Nobody wants either, of course, and a few simple app-design concepts will help keep your apps easy to use and hard to break.


This first-in-a-series installment on mobile UI best practices covers Touch Targets, the places that put functions at the fingertips.


Touch Targets

Human pointers have far less precision than mouse pointers. It's therefore important when designing your app's buttons and other touch targets that you optimize them for the imprecision of the human finger. Here are a few best practices.


In general, buttons should be large enough to remain visible and provide feedback when touched by a finger or thumb. This important visual cue lets the user know that their choice has been registered and that something is about to happen. Also, the button's appearance should remain "pressed" if the app doesn't immediately dismiss the current screen. This will help prevent subsequent presses of a button that the user thinks wasn't pressed the first time.


02_index_finger2.png03_thumb.png


The ideal size for human-interactive buttons was included in research published in 2003 by the Touch Lab at MIT, which sought to understand the mechanics of tactile sense. It reported an average human index finger at 16 to 20 mm (about three-quarters of an inch) wide, and an average thumb at 25mm (about one inch). Of course to calculate button size, those length measurements must be converted to pixels using the target screen's pixels-per-inch (ppi) specification. Use this pixel calculator to determine the number. 


Interestingly, Apple's Human Interface Guidelines for iPhone currently recommend that touch targets be at least 44 pixels square; Nokia recommends an even smaller 28 pixels square and parent Microsoft advises 34 pixels x 26 pixels. While such dimensions might have suited a 640x480 screen, today's resolutions can make even Apple's suggestions look like a postage stamp on the other side of the room. So it's best to use absolute measurements and convert using the ppi of the target device. It's also good practice to place touch points away from the edge of the screen, where they can sometimes be hard to tap cleanly because of a protective case or cradle. The minimum size for a reliable touch target is 6mm square for stationary users; 8mm for users in motion.

 

04_Apple_iPhone_design.png

 

Also important are variances in tap method. Sometimes people tap with the tip of the finger, other times with the pad of the thumb. What you might not have known is that touchscreens register touch input in the center of the contact area, a point known as the centroid. Depending on the platform, information such as touch size and pressure also are captured, but the centroid is the point of action.


05_Centroid.png

 

From the illustration above, the thumb's "contact patch" appears to be touching both Nearby and Events buttons. But Nearby will be selected because its centroid falls within its boundaries. It's important to note that the so-called hot section should not be limited to a button's text. The clickable area should include button text and the area of its enclosing geometry, which in this case is a rectangle. To further reduce incorrect presses, known as interference, create an invisible 3mm gap between buttons by shrinking the clickable area at the top and bottom of adjacent buttons by 1.5mm. Conversely, if a screen object is too small to be a suitable touch target, increase the size of the clickable area surrounding it and make it transparent (and therefore invisible).


To minimize interference errors, touch targets should be separated by 8-10mm on center. If only a few buttons are needed, making them oversized can minimize errors and direct attention to the most common action. But be careful not to go overboard. Developers of the alarm clock app below clearly thought that most users would choose to snooze, and perhaps further believed that groggy fingers were better served by a larger landing area. Perhaps they're right, but experts say that touch targets should never exceed 15mm. Anything larger starts to look like a background element.



06_Alarm_clock.png


The UI design is ultimately up to you, and should be built according to the needs of its users. The Zebra Utilities app shown below offers six main functions, each on a dedicated button with plenty of separation between them. This app is easy to operate with finger or thumb. 


08_Zebra_Utilities.png

 

For an app that presents a scrolling list of selection objects, a stack of buttons or sets of functionality presented in tabbed screens, it's important to build it with minimization of interference in mind. Remember that design elements such as tab bars can be as slender as you like visually, and their touch targets can exceed those geometric borders. The figure below shows a pair of apps with an overlay of 8mm and 10mm concentric circles depicting the spacing for minimum and optimal interference reduction. The overlay can be achieved programmatically on the actual screen or by placing it over a screenshot. In the app at left, notice that the tabs overlap touch targets above and below.


07_Spheres_of_interference.jpg


Regardless of how carefully the UI is designed, it's normal to expect an errant selection now and then. It is therefore important to build your app so that it minimizes the severity of those wrong taps.One critical best practice is to avoid placing trivial functions near non-trivial ones. Non-trivial functions might include logging off, sending or deleting objects or other actions that are hard or impossible to undo. Such functions should be placed in a menu or at the very least require multiple taps to complete.


No app is perfect, just like no developer is perfect. By using some best practices for UI design, you can help minimize the errors that users will make when using your app. Here's a recap:


  • Buttons should be large enough to remain visible and provide feedback when touched by a finger or thumb
  • Appearance of a pressed button should remain "pressed" if the app doesn't immediately dismiss the screen
  • Use absolute measurements for button sizes and convert to ppi for the target device
  • The minimum size for a reliable touch target is 6mm square for stationary users; 8mm for users in motion
  • Touchscreens register touch in the centroid, the center of the contact area
  • Touch targets should be separated by 8-10mm on center
  • Clickable area should include button text and the area of its enclosing geometry
  • If a screen object is too small to be a suitable touch target, increase the size of the clickable surrounding area
  • Touch targets should not exceed 15mm under normal circumstances
  • Avoid placing trivial functions near non-trivial ones

 

 

 

 

SOURCES

http://www.uxmatters.com/mt/archives/2013/03/common-misconceptions-about-touch.php

http://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/

http://developer.android.com/reference/android/view/MotionEvent.html

http://touchlab.mit.edu/publications/2003_009.pdf

http://www.hfes.org/publications/ProductDetail.aspx?ProductID=69

https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/LayoutandAppearance.html#//apple_ref/doc/uid/TP40006556-CH54-SW1
http://sculpt-fx.com/alex-oliver/alex-oliver-clay-sculpture-1/
https://www.sven.de/dpi/

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.



The release of RhoMobile Suite 5.1 last month has generated a good deal of media buzz. In late June it was one of Network World's New Products of the Week, and outlets as diverse as AD Trends, Beta News, Mobile Advertising Watch and Software Mag have helped spread the word about the new APIs and device support in 5.1. The reporting also touted Live Update, RhoMobile's ability to see the results of code changes in real time. Some also covered one of its most-demanded features--the ability to print via USB. Android apps made with RMS 5.1 now have a non-wireless means to output to Zebra printers that support USB printing, a list that's growing all the time. If you're interested in USB printing, be sure to check the RhoMobile Printing Guide for details and restrictions.


Zebra ZD500R printer.jpg

Zebra's ZD500R printer, one of the latest to support USB printing from Android apps.


Taking a deeper dive into the trend of mobile-device usage in bricks-and-mortar retail stores was CIO Magazine, which included a quote from RhoMobile product manager Alison Clark as it described "9 ways mobile and social tech improves the retail shopping experience."


Also of interest, Zebra's ever-prolific senior director of enterprise software Mark Kerstein penned a pair of pieces recently worthy of your attention. In "Fast, Cheap and More Controllable," Kerstein methods for building enterprise apps more quickly, for less cost and with better performance and control over data assets. And in a piece published this month in SD Times, Kerstein points to Android's roots in consumer devices to explain "Why Android is Poised for Industrial Mobility," and provides some best practices as action items.


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.


Filter Blog

By date:
By tag: