Simplest event system you can use with React.js

A common scenario when dealing with React is inter-component communication, particularly when the components do not share a parent child relationship.

For example, consider the case of a context-dependent header bar action that affects the main content window.

<Layout>  
    <AppBar />
    <Content />
</Layout>  

Arguably, there are a number of alternative solutions such as providing a callback to the AppBar, and propogate back down to the content (you can invoke component methods directly via a ref), but we’ll focus on the non parent-child case mentioned in the official docs.

It turns out that if you’re using browserify you already have access to an implementation of NodeJS events, so emit away.

// eventService.js
'use strict';

var events = require('events');  
var eventEmitter = new events.EventEmitter();

module.exports = eventEmitter;  
var bus = require('./eventService');

...
// caller
handleAction: function(arg) {  
  bus.emit('addCard', arg);
}

// receiver
componentDidMount: function() {  
  bus.on('addCard', this.addCard);
}

Closing popovers in Ratchet

Ratchet is a css/js library for prototyping iOS apps. Maintained by Twitter, it looks similar to its more established older brother Bootstrap but is much less mature and has an anemic API.

Simple things like closing its popovers don’t seem to exist. If you need to do this, digging into the source reveals

element.addEventListener('touchend', function () {  
      popover.addEventListener('webkitTransitionEnd', onPopoverHidden);
      popover.classList.remove('visible');
      popover.parentNode.removeChild(backdrop);
});

for the popover backdrop, so doing something like

var backdrop = document.querySelector('div.backdrop');  
if (backdrop) {  
  // sad there's not real way to close this
  // https://github.com/twbs/ratchet/issues/625
  var evt = document.createEvent('TouchEvent');
  evt.initUIEvent('touchend', true, true);
  evt.target = backdrop;
  backdrop.dispatchEvent(evt);
}

to dispatch the touch event was sufficient for my purposes.

See https://github.com/twbs/ratchet/issues/625 for a short discussion on this.

Uncaught Error: DATA_CLONE_ERR: DOM Exception 25

If you’re using IndexedDB in Chrome and getting this exception, AND you’re not using web workers like this StackOverflow question, you might have members of type Functions on the object you’re saving.

See MDN’s article on structured clones for more info on what’s allowed. Also worth noting are that prototype chains and property features are not cloned.

Hope this helps πŸ™‚

Browser storage options, specifically Cordova, Android + iOS

Specifically cordova, Android + iOS

Interesting alternatives:

getBBox returns a 0 sized rect

TL;DR: Your svg has to be visible for getBBox to be useful

While working with d3 recently, I was trying to calculate the width of some elements using getBBox , except it kept returning a 0-sized rect.

It turned out that the svg was being rendered when its parent <div> was not visible. Calling getBBox when the svg is visible gives the right results.

Apparently webkit used to some have issues with this but was patched a long time ago.

Hope this helps.

Hands on with Ionic, part 1

I’ve been playing with a nifty framework called Ionic recently. Ionic is a framework for developing hybrid apps that is tied to AngularJS. Since I’d previously worked with AngularJS it seemed right up my alley.

I’m going to walk through building an app using the framework. In particular, I’m going to build an application called BGG Scanner that will look up Board Games on Board Game Geek based on a barcode scan. Through this, we’ll cover getting started, using libraries, native device features and testing. More on this later, but first, what is it I’m actually talking about.

It’s going to take a few posts, so this will be a brief introduction and making sure you’ve got things set up before going any further. In this post we’re going to:

  • Introduce Ionic and Angular
  • Make sure you can run the emulator
  • Introduce our board game problem

.. so we’re all set up for part 2!

What is Ionic?

Ionic is like Bootstrap for hybrid apps – it has CSS styles and elements that mimic native mobile platform elements, as well as javascript components specifically for use in Angular applications. Most of its javascript components deal with UI elements, such as modals, tabs and loading screens, but it also has utilities such as gesture detection and history management.

Here’s a kitchen sink example:

See the Pen winLd by Steven Iseki (@StevenIseki) on CodePen.

Ionic uses Cordova for native support, giving it access to the plethora of Cordova/Phonegap plugins. The same folks behind Ionic, Drifty, also have a project called ngCordova that exposes a number of popular Cordova plugins as Angular components.

You can find a more detailed introduction over at the Ionic framework website.

What is Angular?

If you build web applications, you’ve probably already heard of AngularJS. AngularJS is a framework for building single-page web applications. It seems heavily inspired by Spring and JSP-style webapps as it makes use of dependency-injection and an MVC-like architecture involving routes, controllers and views.

Here’s a set of route declarations, which include the mapping between views, routes and controllers.

The views are HTML templates (hence templateUrl), and are tied together with controllers by a $scope object. Any properties available on the scope object are available in the view through the use of {{}} interpolations.

Unlike JSPs, the views cannot directly include code blocks <% %> style, but you can write your own equivalent of custom tag libraries.

Controllers in turn do much of the heavy lifting for each view. They get their dependencies injected at run time, dependencies being Angular constructs such as factories, services, constants and so on. The $scope object is injected in this fashion. It’s very spring-ish and provides similar testability benefits.

and altogether now:

See the Pen xbENeR by Min’an (@mtan) on CodePen.

There’s tons more that Angular provides, including crown jewels like two-way binding. I started off with Angular after watching an hour-long video introducing various Angular constructs.

If you prefer a just-let-me-code approach, the Angular project also has a step-by-step tutorial available that covers not only the basics but how to test each step along the way. They’ve recently added a codeschool course that I haven’t tried but looks pretty cool.

Ionic and Angular, sitting in a tree

Ionic provides a number of UI components through Angular constructs. In JSP parlance, it not only includes custom tag libraries, but also components that can be dependency injected.

Note the presence of the status bar and back button, as well as the larger, mobile friendly buttons below:

See the Pen MYjRGX by Min’an (@mtan) on CodePen.

If we compare the Ionic example with the barebones Angular example we had earlier, we see a number of differences.

  • Controllers look almost the same, except when mapping out routes Ionic doesn’t use $routeProvider but uses something else called $stateProvider. It’s because Ionic uses the more UI-router which allows for more flexible routing rules. That said, the syntax is mostly similar.
  • Ionic introduces custom elements like <ion-nav-view> and <ion-content>. These elements provide the default mobile-themed styling as well as additional javascript behaviour. If you are interested in how this is done in Angular, check out its custom directives

Apart from that, it looks pretty similar and you get mobile-themed styling for free.

Actually getting started

Diego Netto has provided a yeoman generator for ionic that does a number of things out of the box. Yeoman is a scaffolding tool for setting up projects and boilerplate code. Different yeoman generators are available for different projects. I’ll go into some of the cool things the ionic generator does as we cover testing, but for now, let’s get started!

Prerequisites

To test locally:

To actually create an app,

If you’re using Android, you’ll also need to set an ANDROID_HOME env var to point to where the SDK is installed

I’ll also assume you have Chrome to help with mobile device previews and Safari if you’re doing iOS.

I’m using

npm -v 1.4.23  
node -v 0.10.31  
ionic -v 1.2.13  
cordova -v 3.6.3-0.2.13  
yo -v 1.3.3  
npm list -g ionic-generator 0.6.1  

Once you’ve got them installed, let’s dive in

Creating an ionic project

mkdir bgg-scanner  
yo ionic:app bgg-scanner  

You’ll end up with a project structure like so:

β”œβ”€β”€ Gruntfile.js            - Configuration of all Grunt tasks
β”œβ”€β”€ package.json            - Dev dependencies and required Cordova plugins
β”œβ”€β”€ bower.json              - Lists front-end dependencies
β”œβ”€β”€ config.xml              - Global Cordova configuration
β”œβ”€β”€ .gitignore              - Best practices for checking in Cordova apps
β”œβ”€β”€ resources/              - Scaffolded placeholder Icons and Splashscreens
β”‚   β”œβ”€β”€ ios/
β”‚   β”œβ”€β”€ android/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ index.html          - Main Ionic app entry point
β”‚   β”œβ”€β”€ lib/                - Libraries managed by Bower
β”‚   β”œβ”€β”€ scripts/            - Custom AngularJS Scripts
β”‚   β”œβ”€β”€ styles/             - Stylesheets
β”‚   β”œβ”€β”€ templates/          - HTML views
β”œβ”€β”€ platforms/              - Targeted operating systems
β”œβ”€β”€ plugins/                - Native plugins
β”œβ”€β”€ hooks/                  - Cordova lifecycle hooks
β”œβ”€β”€ merges/                 - Platform specific overrides
β”œβ”€β”€ coverage/               - Istanbul reports
β”œβ”€β”€ test/                   - Unit tests
β”‚   β”œβ”€β”€ spec/
β”œβ”€β”€ www/                    - Copied from app/ to be used by Cordova 

Most things you’ll need to touch will be in the app subfolder. For now, let’s check out how our app looks.

grunt serve  

which will open a browser with your spanking new app. You can then use something like Chrome’s developer tools to preview mobile browsers.

Seeing it on an actual device

Let’s see how it looks like on a native platform. You’ll first have to add an appropriate platform to the project:

grunt platform:add:ios  

and then

grunt emulate:ios  

Replacing ios with android if needed.

If you leave off the platform, e.g. grunt emulate, it will emulate all the platforms you have added.

If you have a device, you can run the app directly using

grunt run  

You will need the npm module ios-deploy to to do direct deploys on iOS devices. npm install -g ios-deploy will do the trick.

That said, the ios emulator is pretty snappy and should be sufficient for most cases (and quicker!)

If you want to distribute archives for installation, you will need to build them first. To build an apk for Android

grunt build:android  

and you will find it in platforms/android/ant-build

If you want to build an archive for iOS, it’s a little more complicated. You’ll still have to do

grunt build:ios  

which will create an Xcode project in platforms/ios. You can then open it in Xcode, from which you can create archives or make use of iTunes Connect for distribution.

A really cool feature of Ionic is its live reload capability. If you add --livereload to your emulate command, i.e.

grunt emulate --livereload  

It will run emulators with the app for all project platforms AND reload them when a change is detected. Now that we’ve got that out of the way, let’s get started!

Our problem

We don't often shop for board games...but when we do we buy the whole shop

Ok, not the whole shop, but it’s a tiresome affair. We usually walk into game stores with hundreds of games, and it’s not obvious which ones will be good. We want a way to pick up a game and tell if it’s worth getting there and then.

The usual solution to this is to search on Board Game Geek. Smartphones have come a long way but it’s still no walk in the park to enter in a dozen or so titles and pore through a mobile-unfriendly site to get the information you want.

A solution

Let’s scan barcodes instead. We can obtain information from BGG and Amazon – BGG to figure out how good the game is, and Amazon to figure out if the price is reasonable.

BGG Scanner mockup - scan, lookup and display info

After I showed something working to Marty things got more complicated but we’ll get to that πŸ˜‰

In the next part we’ll sketch out how this might work and start making some actual mockups using Ionic.

Parents and the Karate Kid

Initial thoughts after browsing through the React docs –

AngularJS is like your parents, complicated relationship but can get the heavy lifting stuff done for you quickly. It can also have some performance issues because it’s trying to look out for things you don’t care for.

React is like a young karate kid. Capable of great things and performance but needs lots of training. Expensive upfront because you need to think through your models more.

To be fair I’ve used AngularJS alot more, but React is looking pretty interesting!

The Curse of Knowledge

Try this. Pick a tune in your head, something most people would easily recognise. Find someone and tap the tune out. Tap, not hum. See if they get it.

How annoyed or frustrated do you get if they can’t get something that seems so obvious to you? I am somewhat ashamed that I wasn’t able to recognise “Happy Birthday” when Marty tried it on me, though it did prove a point.

When you know something, and you know it well, it’s hard to imagine how someone else could possible not know about it. Until we develop ET-like powers of psychic connection, most experiential or knowledge exchange is carried out under context deficient conditions and restricted by language. Remember the mythical swing?

This is hardly an insight. What is perhaps less stated is its censorship effects. One of the common reasons I abandon an idea or poject is because it seems so obvious that I feel embarassed to mention it, or even write a whole article about it. Especially since someone could easily reach the same level of understanding if they spent enough time googling for it.

I think this self-censorship robs us twofold. It robs us of an opportunity to synthesise and refine our thoughts in writing, but also robs the rest of the world of your insight that may not be all that obvious to them.

One of the most visited pages on this blog is about an Android related build system problem. This is despite there existing a stackoverflow post about it that’s shorter and more concise. There is value in your unique synthesis and contextual insight.

So stop crossing out those ideas for projects and articles. It could even be about something as mundane as fried rice. πŸ™‚