revolunet blog

web technologies for desktop and mobile

Extend Google Spreadsheets With npmcdn.com and React

| Comments

Sometimes you need a solid platform where people can collaborate in real-time and input data in a structured, reusable way. Depending on your needs and planning, Google Spreadsheet can be helpful in such cases, specially if you dont have time or ressources to create a full-blown “admin interface”.

Of course, a tailored-made admin interface can be “better”, but well, Google Spreadsheet is here, instantly available, battle-tested, powerful, very flexible and as we’ll see below, you can even extends the UI.

Also note the powerful Google data APIs gives you full access to your data in various ways and there many other ways to exploit yout documents data (create executable APIs, publish the feeds, embed spreadsheet…)

The main issues for me yet are the google apps scripts disastrous developer experience, but i’ll give you some tips to reduce the pain :) (most of it is due to security model) and the UI that can be a bit slowish due to client/server interactions.

This example use React, because it’s sooo 2015, but you’d better use what makes you happy :)

How it works

You google spreadsheets can be extended with Google Apps Scripts. These are hosted Javascript files that execute on the server-side, in the Google infrastructure, and have access to various Google APIs and can be triggered right from your documents.

In the case of spreadsheets, these scripts can open custom isolated modals or sidebars, which can host random client-side javascript inside an iframe, get data from your docs and send back results to them.

So in this example, we’ll add a multi-selection widget to our spreadsheet, which will help our users create many-to-many relations in our spreadsheet.

Google Apps Script editor

This is a major pain in the @$$. When you want to script your documents, you must use that editor which lacks all the basics. There are ways to use your preferred editor and GIT, but it’s for a later article.

We’ll reduce the code needed here to the minimum : just some glue between our documents and our react widgets.

From you spreadsheet, select Tools > Script Editor.

From here, you can add .gs scripts (server-side javascript) and .html templates which are your custom modals or sidebars and where you can interpolate some values from your docs, using old-school php-like tags.

The code from your html files cannot talk directly to the documents but can execute functions from your .gs files.

The .gs files

The code here is interpreted when the document opens, on the server-side.

For example, here’s how you’d add a new menu entry to your document :

1
2
3
4
5
6
7
8
9
10
// add a custom menu when the spreadsheet opens
function onOpen() {
   SpreadsheetApp.getUi()
      // create a new menu in your spreadsheet
      .createMenu('My Custom menu')
      // add an entry to that menu
      .addItem('Select Guests', 'selectGuests')
      // Warn: forgetting this line can drive you nuts
      .addToUi();
}

Then you can define a selectGuests function that do what you need.

In our case, it will open our custom sidebar and pass necessary data (a list of available guests to select from a range in the spreadsheet).

A custom sidebar with a React widget

There are various approaches here :

  • create a full bundle with all your javascripts and insert it in the page
  • use external scripts and add minimal code to the html

I prefer the second approach because its lighter and allow cient-side caching for 3rd-party libraries which is faster.

There is a little gem recently published at npmcdn.com; this service from the well-named Michael Jackson allows you to grab any npm module just by adding a script-tag to your html doc. This is the perfect tool to inject umd libraries into our iframe.

Here’s an example sidebar.html :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <script src="//npmcdn.com/react@0.14.7/dist/react.js"></script>
    <script src="//npmcdn.com/react-dom@0.14.7/dist/react-dom.js"></script>
    <script src="//npmcdn.com/react-multiselect-revolunet@1.0.5"></script>
  </head>
  <body></body>
  <script>

    // receive some values from spreadsheet here
    // turn back strings to JSON
    var choices = JSON.parse("<?= choices ?>");

    // instantiate the react component with props
    // using the umd library name
    var cmp = React.createElement(window['react-multiselect-revolunet'], {
       choices: choices,
       onChange: function(selection) {
          // send result to spreadsheet function
          google.script.run.setActiveCellValue(selection.join(','));
       }
    });

    // render the component
    ReactDOM.render(cmp, document.body);

  </script>
</html>

Open the Sidebar

The is an example code.gs that triggers our sidebar, send and receives values from it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// this function will be called by our sidebar to update the cell value
function setActiveCellValue(value) {
  SpreadsheetApp.getActiveSheet().getActiveCell().setValue(value);
}


// this is called when user selects the entry in the custom menu
function selectGuests() {
  // open our sidebar with a range of possibles from A2:A range
  openSideBar("Select Guests", "'Sheet1'!A2:A", 0, 0);
}

/*
 * title: Title of the SideBar
 * range: range where to get choices from
 * valueIndex: 0-based index of the column in the range responsible of values
 * labelIndex: 0-based index of the column in the range responsible of labels (can be the same as values)
*/
function openSideBar(title, range, valueIndex, labelIndex) {

  // grab list of available choices
  var choices = SpreadsheetApp.getActiveSheet().getRange(range).getValues();

  // create the template from HTML
  var tpl = HtmlService.createTemplateFromFile('pane.html');

  // add data to the templates. needs to be passed as strings !
  // data structure depends on your widget
  tpl.choices = JSON.stringify(choices.map(function(choice) {
    return {
      value: choice[valueIndex],
      text: choice[labelIndex]
    };
  }).filter(function(val) {
    return val.value && val.text;
  }));

  // now, evaluate and execute our template inside a sidebar
  var result = tpl.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
  result.setTitle(title).setWidth(300);
  SpreadsheetApp.getUi().showSidebar(result);
}

Conclusion

With these few tips, you’re now able to build on top of Google Spreadsheets and add the missing parts you need.

Dont build your company on top of any closed-source product.

Exploit the tools, push the limits, but always keep full control of your data. (see the recent Parse shutdown)

Once your spreadsheet is well-strutured, you can use the Google Data APIs to extract them, or use one of many available npm modules to do it, like spreadsheet-to-json.

Development Workflow With Rackt-cli

| Comments

As advertised in my Web developer survival guide, modularisation is key to keep code maintenable, testable and understandable to others, and npm makes this easy and fun.

see also why writing small modules is important by @sindresorhus.

But modularisation is not free, it comes at the cost of maintenability, specially if you use a complex toolchain like Babel + JSX + Tests… and have dozens of modules to maintain.

In this article, i’ll explain what is rackt-cli, and how it helps to stay productive, with a modern and sane workflow, especially in the context of ReactJS components.

If you’re in AngularJS world, be sure to check ng-factory from the awesomes @olouv and @douglasduteil, which has the same goals, but with a different tech stack.

Note that i work with a custom rackt-cli fork, which make workflow even smoother IMHO :)

There are already tons of good boilerplates around for the react stack, but once the boilerplate is used for your project, it became a part of it, and you end up with 50 different tooling for 50 modules.

rackt-cli solves this by isolating the workflow tasks from your code, so you can maintain them among various projects.

Basically, it’s just an opiniated boilerplate, a set of shared tasks that do the boring stuff for you, and rules to enforce some basic coding and publishing conventions.

What rackt-cli gives you :

  • Webpack, because being able to bundle assets into Javascript is awesome
  • Babel, because ES6 is explicit, powerful, and less verbose
  • Hot-reload, because a better developer experience means happier developer
  • Testem + Mocha + expect-jsx, to test your code in any browser, with CI support.
  • NPM : automated NPM publishing (bundle only) + GIT tagging
  • Changelog : automated changelog from commits messages with rf-changelog
  • Gh-pages : publish your examples to gh-pages
  • Eslint : code conventions based on eslint-react

Typical workflow

1
2
3
4
5
6
7
8
9
10
11
12
13
# create a nice boilerplate for your component
rackt init my-component

# start to make some changes
rackt server
git add awesome.js
git commit -m 'feat: awesome stuff'

# test & publish to npm
rackt release

# publish examples to gh-pages
rackt pages

And that’s it :)

You can now focus on your code, tests and documentation !

If you have some suggestions, ideas… feel free to ping me on twitter @revolunet or in the comments below.

My rackt-cli fork : https://github.com/revolunet/rackt-cli

Original project : https://github.com/mzabriskie/rackt-cli

The Web Developer Survival Guide

| Comments

Applicable mostly to frontend web development teams.

Comments welcome :)

On the same subject, by Eric Eliott : How to Build a High Velocity Development Team

Even if the main factor for a project success is human relations, providing a friendly, secure and comfy development workflow for your team is essential to improve happiness, and thus encourage high-quality standards.

TL;DR

  • Premature optimisation is the root of all evil
  • Simple and fast dev workflow
  • Standards tools
  • Early feedback
  • Modularity
  • Quality

Project management

Reduce the number of tools you use :

  • Issues, milestones… : GitHub + ZenHub
  • Slack for centralized communication
  • Markdown :)

As a product owner, define clear goals and deadlines, and detailed user stories and mockups that will act as base reference for future milestones.

As a developer, expose your plans when starting working on a new feature so you can have early feedback from your team. Github issues are cool for that. Think Readme Driven Development.

Avoid slack-driven-development which can lead to precipitate design decisions, premature optimisations and throwaway features through endless chats; prefer argumented GH issues.

Dont wait months to receive feedback on your feature. Release early to get early end-user feedback and favour useful features.

lean

Versionning

Use GIT with GitHub and a simple but effective model.

TL;DR :

  • Production-ready master branch
  • Pull requests from feature-branches to master
  • Hotfixes pull requests to master
  • Always mention related issues in commit messages
  • Use a develop branch to receive PRs for huge upgrades
  • Never, ever, push --force to a shared branch

github flow

Use standard commit messages, respect SemVer : Breaking.Feature.Patch.

See semantic-release for automatic semver based on commit messages.

Standards

Choose and use battle-tested industry tools, standards and styleguides.

Provide sane and homogenous defaults for developer environnement :

  • .editorconfig
  • .babelrc
  • .eslintrc
  • npm scripts

Don’t rush on the new hype; Tools are continuously and fastly improving but early adoption means upgrading the whole team skills and may have significant impact on your workflow.

Experiment, make proof-of-concepts, evaluate advantages and anticipate migrations.

Once maturity is here and adoption is wide enough and if effort is worthwhile, engage progressive migration.

Modularity

You don’t want to create a monolithic, highly-coupled application.

Break problems into smaller parts.

From the UNIX philosophy : Build small programs that do one thing, do it well, and compose easily with other programs.

Which means small, focused, testable and tested JavaScript modules, providing a nice API.

see JS Modules best practices and FIRST principles

npm ecosystem is an incredible ressource for JavaScript modules which come with already existing tests and corner-cases solved for you. USE IT !

npm is a also a powerful packaging tool that makes it easy to publish your own modules and manage dependencies at scale.

By separating concerns of your modules you’ll improve your velocity and reduce hassle due to working in complex contexts.

Focusing on a specific problem without worrying about third party is the key to solve it.

Quality

Unit tests will build trust, save your ass, and make the developer feel more confident to work without breaking something, thus, make him/her much more productive, and happy.

Test the public API, not the internal implementations.

Test coverage will give you cool insights and motivate you to do more.

Add UI tests (aka “smoke tests”) to ensure integration and prevent repetitive and boring manual in-browser course tests.

Tests won’t guarantee your code is bug-free but will increase quality (early bug fixes + easy refactoring), and most importantly, allow developers to iterate quickly in confidence.

The earlier the bugs are discovered, the cheaper they are to fix.

bugs cost

Developer Experience

Offer powerful MacBooks or Linux laptops.

Comment the code (JsDoc) and provide nice README.md files.

Adding inline comments is cheap and helps later refactoring; Explain the WHY of your code when its not obvious.

Make development fast and easy : when working on a new feature, the developer should be able to install the environment quickly, and iterate with blazing-fast build times and hot-reload.

Choose a solid boilerplate with all your sane defaults.

Team

Work hard, Play hard.

Be nice and always be yourself.

There’s nothing as powerful as trust when you want to move mountains.

Create and Host a Beautiful Website for Free Using GitHub

| Comments

In this tutorial i’ll show you how you can setup, publish and host a nice single-page website in minutes and completely free of charge using Github pages.

I assume you’re you are already familiar with :

We’ll use the awesome GitHub.com to host our static website and create it using an existing template. Github hosts your website code history publicly and you benefit, among other things, from code versionning and free, high-quality hosting (cdn, ddos protection…).

WARN Files stored on Github are public to anyone (except if you buy a private repo), so don’t store anything sensible here.

What is a “static” website ?

For small websites, CMS solutions like WordPress and equivalents are largely overkill and require some technical setup and mandatory maintenance mostly due to security patches and unattended upgrades.

Nowadays, it is possible to create awesome websites in a server-less way, using only client-side technologies. This mean you can host your website anywhere without any configuration, as all the code executes is only in the final user browser (HTML, Javascript and CSS), and this has several advantages :

  • simple
  • no maintenance
  • no hacking
  • better performance
  • less is more
  • simple is beautiful

Any serious business now offers an API that you can integrate in some way your static website if you need so this static approach has NO LIMIT.

Next articles will provide more advanced examples using a static website generator.

Create the Github repository

create a new repository for your website : https://github.com/new ex: mywebsite

This will create a public repository at https://github.com/USERNAME/mywebsite

Single page website with Github page generator

3-clicks setup using one of the GitHub built-in templates

Go to your github project page, https://github.com/USERNAME/mywebsite

  • Go to repository settings (icon in the right-bottom)
  • Automatic page generator and click Launch
  • Edit your website text
  • Select a template and Publish Page

Wait a few seconds and goto : http://USERNAME.github.io/mywebsite

your website is up :)

Edit the website online

Go to your github project page, https://github.com/USERNAME/mywebsite

This is all your websites files.

Click the file you want to edit, for example index.html (main page), make your changes then click the “pen” icon on top-right of the editor.

Under the file editor there’s a “Commit changes” form, set the title to a good description of your changes : ex: “contact info update”

Press Commit changes button.

Wait a few seconds and goto : http://USERNAME.github.io/mywebsite

your website is updated :)

Edit the website on your computer

For more advanced edition and comfort you may want to edit from your preferred text editor on your computer.

Setup the GitHub app with your account : mac.github.com or windows.github.com

  • Clone the project on your computer, it will download the code from github
  • Edit the files on your computer, add javascript, images, etc…
  • Test if everything works locally
  • Then Commit those files with an explicit message from github app
  • Sync with Github

your website is updated :)

Test the website locally

You can open the file in your web browser to test the result.

WARN If you added google maps or some javascript API, be sure to run python -m SimpleHTTPServer from your console in the project folder, and use http://127.0.0.1:8000 in the browser.

Use great templates

If you want different templates than github ones, you just need to replace your repository content with the new one.

Choose one of the high-quality template from html5up.net or pixelarity.com, download and extract to your project folder.

Edit the files, test, commit & sync and you’re done.

Next steps

Learn Markdown syntax : reference

Learn GIT : try.github.io

Learn Javascript : jsbooks.revolunet.com and Eric Elliott book

FAQ

How do i setup a domain name ?

  • Buy a domain from gandi.net (or other serious one)
  • Edit the domain “DNS zone” and follow the github dns instructions
  • add CNAME file, containing www.myproject.com, to your repository

The DNS changes can take up to 24h before propagating so try www.myproject.com tomorrow

Github Help : custom domain, dns setup

How to add a page ?

Just add some more html files, like “products.html” and add a link to it from index : <a href="products.html">products</a>.

How to get traffic analytics ?

If you want to track your visitors and analyse where they come from and what they do on your website, create an account on Google Analytics and add the tracker to your page code.

How to add a Map ?

If you need an interactive map, you can follow Google Maps instructions.

Alternatively, follow Leaflet instructions to take avantage of the OpenStreetMap project, which is community driven, free and open source.

If you just need a map image : generate the HTML code from here : staticmapmaker.com ad copy the “HTML” to your page. Full static images doc : https://developers.google.com/maps/documentation/staticmaps/

How to add comments ?

Create an account on disqus.com and add the javascript code to your page.

How to add a contact form ?

Create an account on typeform.com and add the javascript code to your page, create the form there and copy the javascript “embed” code to your page.

Alternatively : Create an account on mailchimp.com, create the form there and copy the javascript “embed” code to your page.

How to receive payments ?

Create an account on stripe.com and add the javascript code to your page.

What is gh-pages ?

gh-pages is a special “branch” (source code version) on github that automagically publish and host a static website.

If you have any question, feel free to ask below :)

Object-oriented AngularJS Services

| Comments

Javascript prototypal inheritance can be confusing at first if you come from classical OOP languages, due to Javascript versatility, and the variety of Javascript OOP patterns available. Combined with the new service and factory concepts introduced in AngularJS, implementing OOP in your applications can lead to serious headaches, so i’ll try to show you some solutions here.

If you need a step-by-step explanation of the Javascript prototypal inheritance, you can read the great Dr. Axel Rauschmayer JavaScript inheritance by example article.

Once your app grow and your services multiply, you’ll quickly feel the need to reuse your code, and to split it in small modules to be able to separate concerns and setup some serious unit testing.

In this post, i’ll show how to create a base AngularJS service, based on the Github API, that we’ll be able to extend and reuse in different scenarios. We’ll also leverage the power of promises chaining to extend the server responses and add additional data before returning the final result.

Something important to note here is that factories are useful to define our classes that you can instantiate many times using the new keyword, while services always create singletons.

Create our base service

Our first service will be responsible of fetching Github basic user data and return the result. We’ll use a factory instead of a service, which will make it easier to instantiate many versions of the service in our application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
app.factory('SimpleGithubUser', function($http) {

    var apiUrl = 'https://api.github.com/';

    // instantiate our initial object
    var SimpleGithubUser = function(username) {
        this.username = username;
        this.profile = null;
    };

    // define the getProfile method which will fetch data
    // from GH API and *returns* a promise
    SimpleGithubUser.prototype.getProfile = function() {

        // Generally, javascript callbacks, like here the $http.get callback,
        // change the value of the "this" variable inside it
        // so we need to keep a reference to the current instance "this" :
        var self = this;

        return $http.get(apiUrl + 'users/' + this.username).then(function(response) {

            // when we get the results we store the data in user.profile
            self.profile = response.data

            // promises success should always return something in order to allow chaining
            return response;

        });
    };
    return SimpleGithubUser;
})

So we can now easily inject our factory anywhere and use it like this :

1
2
3
4
5
6
7
8
9
// we first inject our factory
app.controller('MyCtrl', function(SimpleGithubUser) {
    // instantiate a new user
    var user = new SimpleGithubUser('substack');
    // fetch data and publish on scope
    user.getProfile().then(function() {
        $scope.userLogin = user.profile.login;
    })
});

Extending the base service

Now we’d like to attach some additional data to our users. Instead of modiying the original factory, or even worse, duplicate it, we can create another factory that extends the original one, just by using the regular javascript prototypal inheritance. We’ll override some methods and use promises chaining to deliver the final data only when all the subsequent requests have been completed.

This has the advantage of encapsulating the logic inside the new service, making it easily testable while keeping your controllers light.

In this example we’ll add some data from the Github events API and attach it to the user profile before returning the final result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// we define a new factory and inject our original service so we can extend it properly
app.factory('AdvancedGithubUser', function($http, SimpleGithubUser) {

    var apiUrl = 'https://api.github.com/';

    // create our new custom object that reuse the original object constructor
    var AdvancedGithubUser = function() {
        SimpleGithubUser.apply(this, arguments);
    };

    // reuse the original object prototype
    AdvancedGithubUser.prototype = new SimpleGithubUser();

    // define a new internal private method for this object
    function getUserEvents() {
        var self = this;
        return $http.get(apiUrl + 'users/' + this.username + '/events').then(function(response) {

            // attach the events API result to our user profile
            self.profile.events = response.data;

            // promises should always return a result
            return response;
        });
    }

    // Now let's override our original getProfile method
    AdvancedGithubUser.prototype.getProfile = function() {

        var self = this;

        // we first call the original getProfile method (aka super method)
        var originalGetProfile = SimpleGithubUser.prototype.getProfile.apply(this, arguments);

        // we use promises chaining to add additional data
        return originalGetProfile.then(function() {

            // before returning the result,
            // call our new private method and bind "this" to "self"
            // we need to do this because the method is not part of the prototype
            return getUserEvents.call(self);
        });
    };
    return AdvancedGithubUser;
});

Usage is exactly the same, except the service added the events data for us :

1
2
3
4
5
6
7
8
9
// we first inject our factory
app.controller('MyCtrl', function(AdvancedGithubUser) {
    // instantiate a new user
    var user = new AdvancedGithubUser('substack');
    // fetch data and publish on scope
    user.getProfile().then(function() {
        $scope.userEvents = user.profile.events;
    })
});

Create a service instance

Now that you have some solid factories, you can also instantiate some app-wide services that expose pre-configured instances.

1
2
3
4
5
app.service('MyUserProfile', function(AdvancedGithubUser) {
    var user = new AdvancedGithubUser('revolunet');
    user.getProfile();
    return user;
});

And use it like this :

1
2
3
4
app.controller('MyCtrl', function(MyUserProfile) {
    $scope.user = MyUserProfile;
    alert(MyUserProfile.location);
})

Final result



Hope this has been useful to you, please ask below or on twitter @revolunet for any question/suggestion :)

Unit Testing an AngularJS Directive

| Comments

In this article, i’ll detail the process to unit test the stepper directive we’ve created in the last week custom component creation article. Next week, i’ll cover how to distribute your component via GitHub and Bower.

Unit testing is the art of testing individually every smallest part of your code, which are the foundations of your apps sanity. Once correctly tested, these parts assembled together will also play nicely, as their behaviour has already been validated independently.

Unit testing helps you prevent regressions, increase quality, maintenability, and trust in your codebase, thus better team collaboration, easier refactoring… and WIN :)

Another usage is, when you get a new bug report, you add the revelant test that demo the bug, fix it in your code so the test will pass, then keep it there as a proof of reliability.

Among AngularJS best friends there is the KarmaJS test runner (A nodeJS server to launch the tests in browsers and reports the results) and the Jasmine behaviour-driven testing framework (the language to define your tests and expectations). We’ll use the grunt-karma task to integrate karma in our classic yet awesome grunt workflow and launch the tests in our browsers. Note that karma can run the tests in remote cloud browsers, for example via SauceLabs or BrowserStack.

AngularJS is made from ground-up for testing, so make yourself a favor, start NOW :)

Glossary

There are some terms that may need clarification before we go further :

  • spec : the specifications of something you want to test, consisting one or many tests suites. should cover all the expected behaviour.
  • test suite : This is a group of tests; defined within a describe block in Jasmine. blocks can be nested as much as needed.
  • test : Test instructions, that ends with one or more expectations; defined within a it block in Jasmine.
  • actual : this is the value you test in your expectation.
  • expected value : this is the value you test the actual value against.
  • matcher : A function that compares the actual and the expected values and returns a boolean success result to Jasmine. eg : toEqual, toBeGreatherThan, toHaveBeenCalledWith… you can even define your owns.
  • expectation : Use the expect function to test a value, called the actual. It is chained with a matcher function, which takes the expected value.
  • mock : a stubbed service that replace a real one at runtime with fake data/methods that you can control during your tests.

Here’s an example spec file :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// a test suite (group of tests)
describe('sample component test', function() {
    // a single test
    it('ensure addition is correct', function() {
        // sample expectation
        expect(1+1).toEqual(2);
        //                  `--- the expected value (2)
        //             `--- the matcher method (equality)
        //       `-- the actual value (2)
    });
    // another test
    it('ensure substraction is correct', function() {
        expect(1-1).toEqual(0);
    });
});

Setup the test environnement

Add grunt-karma to your project dependencies

1
npm install grunt-karma --save-dev

Create karma-unit.js file

Here is our full example. This file defines :

  • the javascript files to be loaded in the browsers for the tests. Typically, this is the libraries you use, your application files, but also the files for your tests and the eventuals mocks.
  • which browsers to run the tests against.
  • how to reports the results : console, browser… ?
  • optional plugins.

Here’s our example “files” section :

1
2
3
4
5
6
files: [
  "http://code.angularjs.org/1.2.1/angular.js",       <-- angular source
  "http://code.angularjs.org/1.2.1/angular-mocks.js", <-- angular mocks & test utils
  "src/angular-stepper.js",                           <-- our component source code
  "src/angular-stepper.spec.js"                       <-- our component test suite
]

NB: One could add jquery here if it helps you write your test code (more powerful selectors, CSS tests, size computation…)

Add the karma grunt tasks to your Gruntfile.js

1
2
3
4
5
6
7
8
9
karma: {
    unit: {
        configFile: 'karma-unit.js',
        // run karma in the background
        background: true,
        // which browsers to run the tests on
        browsers: ['Chrome', 'Firefox']
    }
}

Create angular-stepper.spec.js and paste the content of the sample test above. You can now simply run grunt karma and see your tests executing in the browsers and reporting the results in the console.

1
2
3
4
....
Chrome 33.0.1712 (Mac OS X 10.9.0): Executed 2 of 2 SUCCESS (1.65 secs / 0.004 secs)
Firefox 25.0.0 (Mac OS X 10.9): Executed 2 of 2 SUCCESS (2.085 secs / 0.006 secs)
TOTAL: 4 SUCCESS

Each dot represent a successfull test and you can see our two tests runs in the two browsers we’ve configured before in our karma-unit.js file. woot !

Now let’s code the real tests :)

Code our directive unit tests

Our component unit test suite, aka the spec should cover all the expected behaviour of our component, but also test the edge cases (eg : invalid input, unexpected server behaviours…)

Below you can see an extract of our angular-stepper component test suite (angular-stepper.spec.js), and here’s the full spec. Our tests for such a component are quite simple, no need for mocks here. The only tricky thing is that we wrap our directive inside a form to be able to test that it plays well with ngModelController and updates form validity correctly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// the describe keyword is used to define a test suite (group of tests)
describe('rnStepper directive', function() {

    // we declare some global vars to be used in the tests
    var elm,        // our directive jqLite element
        scope;      // the scope where our directive is inserted

    // load the modules we want to test
    beforeEach(module('revolunet.stepper'));

    // before each test, creates a new fresh scope
    // the inject function interest is to make use of the angularJS
    // dependency injection to get some other services in our test
    // here we need $rootScope to create a new scope
    beforeEach(inject(function($rootScope, $compile) {
        scope = $rootScope.$new();
        scope.testModel = 42;
    }));

    function compileDirective(tpl) {
        // function to compile a fresh directive with the given template, or a default one
        // compile the tpl with the $rootScope created above
        // wrap our directive inside a form to be able to test
        // that our form integration works well (via ngModelController)
        // our directive instance is then put in the global 'elm' variable for further tests
        if (!tpl) tpl = '<div rn-stepper ng-model="testModel"></div></form>';
        tpl = '<form name="form">' + tpl + '</tpl>';
        // inject allows you to use AngularJS dependency injection
        // to retrieve and use other services
        inject(function($compile) {
            var form = $compile(tpl)(scope);
            elm = form.find('div');
        });
        // $digest is necessary to finalize the directive generation
        scope.$digest();
    }

    describe('initialisation', function() {
        // before each test in this block, generates a fresh directive
        beforeEach(function() {
            compileDirective();
        });
        // a single test example, check the produced DOM
        it('should produce 2 buttons and a div', function() {
            expect(elm.find('button').length).toEqual(2);
            expect(elm.find('div').length).toEqual(1);
        });
        it('should check validity on init', function() {
            expect(scope.form.$valid).toBeTruthy();
        });
    });

    it('should update form validity initialy', function() {
        // test with a min attribute that is out of bounds
        // first set the min value
        scope.testMin = 45;
        // then produce our directive using it
        compileDirective('<div rn-stepper min="testMin" ng-model="testModel"></div>');
        // this should impact the form validity
        expect(scope.form.$valid).toBeFalsy();
    });

    it('decrease button should be disabled when min reached', function() {
        // test the initial button status
        compileDirective('<div rn-stepper min="40" ng-model="testModel"></div>');
        expect(elm.find('button').attr('disabled')).not.toBeDefined();
        // update the scope model value
        scope.testModel = 40;
        // force model change propagation
        scope.$digest();
        // validate it has updated the button status
        expect(elm.find('button').attr('disabled')).toEqual('disabled');
    });
    // and many others...
});

Some notes :

  • A directive needs to be compiled in a given scope to be tested
  • A non-isolated scope can be acceded via element.scope()
  • An isolated scope can be acceded via element.isolateScope()

Why to we have to call scope.$digest() when we change a model value in the tests ?

In a real angular app, the $digest is automatically triggered by the framework in reaction to various events (clicks, inputs, requests…). There’s no such user-based events during the automated tests so we just need to force the $digest. (the $digest is what update all the bindings).

Bonus #1: real time tests

Thanks to grunt, we can make the tests run when the source changes and be alerted in real time.

If you want the tests to be run on each code change, just add a section to your watch task :

1
2
3
4
js: {
    files: ['src/*.js'],
    tasks: ['karma:unit:run', 'build']
},

You could update your default grunt task like this

1
grunt.registerTask('default', ['karma:unit', 'connect', 'watch']);

Now, just run grunt and you’ll get real-time tests and a builtin webserver :)

Bonus #2: add code coverage reporting

As developers, we love solid metrics; and we also love continous improvements. “coverage” refers to the code coverage of your test suite; It gives you metrics and detailed info to increase your code coverage without pain.

Here’s a sample coverage HTML report :

We can see, for each folder and file, how much code is covered by our test suite. And this is updated in real-time thanks to grunt+karma integration. For each file, we can see line by line which blocks stays untested, which makes writing the remaining tests more straightforward.

100% test coverage doesnt mean your code is bug-free, but it increase quality for sure !

Its really easy to integrate this in our karma+grunt setup. Karma has a “plugin” system that allows you to plug the fantastic Istanbul code coverage tool so we just need to configure the karma-unit.js file and we’re done :)

Add coverage to karma

1
2
# add the necessary node_modules
npm install karma-coverage --save-dev

now update the karma config file with these new settings :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// here we specify which of the files we want to appear in the coverage report
preprocessors: {
    'src/angular-stepper.js': ['coverage']
},
// add the coverage plugin
plugins: [ 'karma-jasmine', 'karma-firefox-launcher', 'karma-chrome-launcher', 'karma-coverage'],
// add coverage to reporters
reporters: ['dots', 'coverage'],
// tell karma how you want the coverage results
coverageReporter: {
  type : 'html',
  // where to store the report
  dir : 'coverage/'
}

More coverage config options here : https://github.com/karma-runner/karma-coverage

You now need to run your tests again to generate your first report. It should be located in the project root “coverage” folder.

Feel free to comment/ask below :)

Next week, we’ll talk about distributing our now well tested directive on Github and Bower :)

Create a Reusable AngularJS Input Form Component

| Comments

One of the beauty of the AngularJS framework is its ability to isolate complexity with services and directives; This is perfect for segmenting our code, create very testable components, and make them reusable. A directive, which is an anticipation of the future web components, is a piece of isolated javascript, HTML and CSS that encapsulate a specific behaviour and that you can easily reuse in various apps. Once created, a directive is simply invoked by adding it to the DOM via a HTML tag (<accordion></accordion>, or a custom attribute <div accordion></div>, or a CSS class <div class="accordion"></div> (and even as HTML comment).

In this tutorial we’ll go through the creation of a custom stepper directive that can be used as a reusable input component in your applications. We’ll cover the classic directive creation but also the input validation, and the use of the ngModelController, that will allow a seamless integration with any form, leveraging the existing AngularJS forms superpowers. The next part will cover the test suites with Jasmine and KarmaJS, and the publication and distribution of our widget with GitHub and bower.

For this example we’ll build a custom numeric input widget, named “rn-stepper”. We’ll use the last AngularJS 1.2 that brings some important fixes to the private scopes management (capital point for reusable components). The full widget code is available on github as a reusable component and you can see the final result here :



Markup generation

The first step is to create a naïve directive that build our markup, and renders correctly. We just declare the directive name, and template to use.

1
2
3
4
5
6
7
8
9
10
11
12
13
// we declare a module name for our projet, and its dependencies (none)
angular.module('revolunet.stepper', [])
// declare our naïve directive
.directive('rnStepper', function() {
    return {
        // can be used as attribute or element
        restrict: 'AE',
        // which markup this directive generates
        template: '<button>-</button>' +
                  '<div>0</div>' +
                  '<button>+</button>'
    };
});

Now, to use our directive, its quite straightforward :

  • declare our revolunet.stepper module as one of our app dependencies
  • use <div rn-stepper></div> (attribute form) or simply <rn-stepper></rn-stepper> (element form). to integrate the directive somewhere.

The attribute form is better if you want to support IE8 as it works out-of-the-box.

demo : http://jsfiddle.net/revolunet/n4JHg/

Add internal behaviour

Now we need to add behaviour and internal variables to our custom component. We’ll declare a “private scope” that will hold internal variables and functions, and add the link function to our directive, which is responsible of initialising the component behaviour just after the markup has been inserted in the final page.

Here’s the updated directive code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.directive('rnStepper', function() {
    return {
        restrict: 'AE',
        // declare the directive scope as private (and empty)
        scope: {},
        // add behaviour to our buttons and use a variable value
        template: '<button ng-click="decrement()">-</button>' +
                  '<div></div>' +
                  '<button ng-click="increment()">+</button>',
        // this function is called on each rn-stepper instance initialisation
        // we just declare what we need in the above template
        link: function(scope, iElement, iAttrs) {
            scope.value = 0;
            scope.increment = function() {
                scope.value++;
            }
            scope.decrement = function() {
                scope.value--;
            }
        }
    };
});

We now have a functionnal component with an isolated code and template.

demo : http://jsfiddle.net/revolunet/A92Aw/

Communicate with the external world

Our component works great but it would be more useful if it could control a real public variable, known as a ngModel in AngularJS.

Let’s add a databinding between our component internal value and the outer world (our application).

We just need to update our scope declaration like this :

1
2
3
scope: {
    value: '=ngModel'
}

This will automagically bind our internal value variable to the external one declared in the ngModel attribute. The = means “double data-binding” which means if ngModel is updated externally then the internal value will be updated, and vice-versa.

Say my app expose a rating variable, we could now bind it to our component simply like this :

1
<div rn-stepper ng-model="rating"></div>

demo : http://jsfiddle.net/revolunet/9e7Hy/

Make our component form-friendly

We now have a dynamic input that can manipulate arbitrary ngModel data. We need to modify a bit the code to make it play nice with the AngularJS forms. For example, AngularJS forms and input generally expose a $pristine and $dirty state which can be useful in many situation. To make the forms aware of our model changes from inside our component, we need to make use of the ngModelController.$render and ngModelController.$setViewValue API methods, which are available as soon as you “require” a ngModel on your directive.

The ngModelController.$render method is a method which you should override yourself in the directive and is responsible of updating the view; it will be called by the framework when the external ngModel changes. When the model changes, the framework executes the $formatters pipeline which is responsible of eventually converting the $modelValue raw value to a usable $viewValue.

For example, if your model is a real Date object, you’d want your input to display it as dd/mm/YY. The model-to-view conversion is made by the $formatters pipeline and the view-to-model by the $parsers pipeline. Once you get a ngModelController instance, you can easily insert new items in these pipelines.

The ngModelController.$setViewValue method should always be called when you want update a model from your directive (view). It takes care of calling the eventual $parsers pipeline. Then it applies the final value to the internal $modelValue, update the input $dirty state, update the optional parent form $dirty state and call any registered $viewChangeListeners. Here’s the full code for this function.

As pointed by a comment from @ThomasBelin4 below, we don’t need anymore to have a scope value variable, as we now have a reference to the original ngModelController which holds a reference to the viewValue.

Here’s how we update the directive declaration :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
.directive('rnStepper', function() {
    return {
        // restrict and template attributes are the same as before.
        // we don't need anymore to bind the value to the external ngModel
        // as we require its controller and thus can access it directly
        scope: {},
        // the 'require' property says we need a ngModel attribute in the declaration.
        // this require makes a 4th argument available in the link function below
        require: 'ngModel',
        // the ngModelController attribute is an instance of an ngModelController
        // for our current ngModel.
        // if we had required multiple directives in the require attribute, this 4th
        // argument would give us an array of controllers.
        link: function(scope, iElement, iAttrs, ngModelController) {
            // we can now use our ngModelController builtin methods
            // that do the heavy-lifting for us

            // when model change, update our view (just update the div content)
            ngModelController.$render = function() {
                iElement.find('div').text(ngModelController.$viewValue);
            };

            // update the model then the view
            function updateModel(offset) {
                // call $parsers pipeline then update $modelValue
                ngModelController.$setViewValue(ngModelController.$viewValue + offset);
                // update the local view
                ngModelController.$render();
            }

            // update the value when user clicks the buttons
            scope.decrement = function() {
                updateModel(-1);
            };
            scope.increment = function() {
                updateModel(+1);
            };
        }
    };
});

demo : http://jsfiddle.net/revolunet/s4gm6/

Add min/max attributes

Now our component is form-friendly, so let’s as some builtin validation rules.

We could add optional min/max attributes to our component, which will handle the form validation by himself when they are present. These attributes will be data-bound so they can be updated at any time by the application (some other inputs in a form may impact the min/max here).

The ngModelController API gives us also a $setValidity method that can inform the parent forms about our component validity, and automatically add some handy CSS classes related to validity to out form and inputs.

We just need to call ngModelController.$setValidity('outOfBounds', false) to make our input, and thus parent forms invalids, and have ng-invalid and ng-invalid-out-of-bound CSS classes added to our forms and to our component.

Our stepper component is now full functionnal and integrates seamlessly in any form.

demo: http://jsfiddle.net/revolunet/HCUNC/

Prevent invalid input

Another nice-to-have feature would be to prevent the user from entering invalid data, which means disabling the buttons when the internal value reach the min/max limits. This could be achieved in two ways :

  • BAD : manually in our link function, toggling our buttons states on each click.
  • GOOD : automagically, using a builtin ng-disabled directive in our template, that will disable the buttons under some conditions.

The second option is much more Angular-ish and there are several ways to achieve this so let’s see how we can do.

We can add ng-disabled="isOverMin()" to our first button template and add a scope.isOverMin function that returns a boolean indicating if we should disable or not the given button. same with overMax that would check if the max has been reached or not.

Our template is now :

1
2
3
<button ng-disabled="isOverMin()" ng-click="decrement()">-</button>
<div>{{ value }}</div>
<button ng-disabled="isOverMax()" ng-click="increment()">+</button>

demo : http://jsfiddle.net/revolunet/26ghx/

The next part will detail the tests suite and distribution subjects over github and bower.

Stay tuned :) and feel free to comment/ask below !

Universal .htaccess CORS Support

| Comments

What is CORS

In the internet AJAX early days (~2000) browsers vendors implemented a strict cross-domain communication policy that prevented javascript on a given page to communicate with third party domains. Many workarounds have been implemented since, like JSONP, which is only a ‘hack’ allowing cross-domain communication using javascirpt callbacks; Handy, but this prevents any other method than GET and forces the use of querystring to pass parameters.

With the RISE of internet APIs and decentralised services, having a way to communicate with 3rd party domain became critical; thus, the W3C defined the CORS protocol, acronym which stands for “Cross-Origin Resource Sharing” and allows different domains to communicate together, the same way Adobe Flash did it a long time before with the crossdomain.xml policy (remember?).

Now with CORS, the server defines which domains (applications) can communicate with him (or anyone). When CORS is enabled on the server, your javascript can communicate transparently using any HTTP method; For non-GET requests, the browser transparently make an initial OPTIONS request to check if the request is allowed or not, and then makes the real request.

CORS-enable your API

You can find the whole specs and various implementations at enable-cors.org.

Pain-free CORS for Apache servers

If you don’t want to change your code, we’ve crafted a small .htaccess for you that will force CORS on your API, without even touching your code. Just drop one of the implementations in your .htaccess and your API is magically CORS-enabled :)

Note: If you use the withCredentials version, be sure to add withCredentials: true in your AJAX requests.

You can validate your API CORS support using this demo : http://embed.plnkr.co/1E8ot9e1eVs23IrjzqKr/preview

Hope this helps !

Extending the Topcoat CSS Framework

| Comments

Introduction

Topcoat.io is the new kid on the block of the CSS-only frameworks. It’s the first one that’s built for performance from the ground up, thanks to the constant performance tracking using Chrome telemetry, which makes it a tool of choice to build your mobile applications UI.It is also easy to theme and customize thanks to a smart CSS organisation and a powerful toolchain.

Topcoat has been built for the future, thanks to solid conventions and a cutting-edge javascript architecture, and more goodness :

It’s fully open source, not “fauxpen source”, and the contributors uses various online tools to run the project :

  • Github of course for code and issues
  • codepen.io to prototype and host widgets demos
  • huboard to manage milestones on top of Github issues

All of this make it a powerful framework made to be extensible and performant.

Install Topcoat

Just download the latest release from github and extract it. The release contains the final CSS with dark and light themes for desktop and mobile, so you can use then directly; just add a link to the topcoat-mobile-light.min.css file in your app for example.

If you want to customize the themes or add new components, you need to run npm install from the topcoat folder. This will download all the widgets sources and local dependencies to your node_modules folder.

Some important things to notice :

  • Each component (button, list, checkbox…) has its own folder in the node_modules subfolder (so each one has its own npm repo). Each one also has its own gruntfile in case you want to build its css separately.
  • Most of components have a topcoat-[COMPONENT]-base folder with just enough CSS to completely reset the styling of the element, so other components can override it entirely. For example the default button hasn’t any default border or background and looks like a simple text block;
  • The topcoat-theme folder defines the 4 builtin Topcoat themes which are just a combination of variables that defines some variations of the components.
  • The main Gruntfile list the available themes and associated widgets using pattern globbing and this is from where the final CSS are built, including the given widgets.

Adding a custom theme

You can easily tweak the builtin themes by modifying the variables inside the topcoat-theme folder then run grunt to rebuild the final CSS. But its much better to add your own theme and leave the others untouched.

Let’s add a simple green theme :

  • copy the topcoat-theme/src/theme-topcoat-* file from the theme that matches the most your design, eg copy theme-topcoat-mobile-light.styl to theme-green.styl.
  • create a variables-green.styl with a bunch of variables that override the default ones (see the list of variables in the other variables-* files). For example :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// base colors
var-color = #00b400
var-background-color = #e2ffe2
var-placeholder-color--input = #4eff4e
var-background-color--down = #00b400
var-background-color--hover = #02ff01
var-border--focus = 1px solid #00b400
var-box-shadow--focus = 0 0 0 2px #00b400

// cta button colors
var-background-color--cta = #00b300
var-border--cta = 1px solid #006700
var-background-color--cta--down = #00b400
var-background-color--cta--hover = #02ff01
  • add @import variables-green to your theme-green.styl. (This allows you later to combine some variables files)
  • add the theme link in the main gruntfile, ‘stylus’ section:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
stylus: {
    green: {
        options: {
            import: ['theme-green', 'utils']
        },
        files: [{
            src: [
                // here you can eventually add/remove some components from the output
                'node_modules/topcoat-*/src/**/*.styl'
                ],
            dest: 'css/topcoat-green.css'
        }]
    }
}
  • Now run grunt and you’ll see your final topcoat-green.css and topcoat-green.min.css appear magically in the CSS folder :)
  • You also have a demo/topcoat-green.html file for free that show you the final result, generated via TopDoc.

Add a custom component

Now, let’s say you want to add a custom button to the generated theme, for example button-round.

Add a node_modules/topcoat-button-round folder and create src/topcoat-button-round.styl file. Inside, add your round button definition, with the documentation to generate the demo :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// import anything you need to extend
@import topcoat-button
/* topdoc
  name: Round Button
  description: A simple, yet round button
  modifiers:
    :active: Round button active state
    :disabled: Disabled state
    :hover: Hover state
    :focus: Focused
  markup:
    <button class="topcoat-button--round">Button</button>
    <button class="topcoat-button--round" disabled>Button</button>
  tags:
    - desktop
    - light
    - mobile
    - button
    - round
*/
.topcoat-button--round {
    @extend .topcoat-button
    border-radius: 999px;
}

.topcoat-button--round:disabled {
  @extend .topcoat-button:disabled
}

.topcoat-button--round:hover {
  @extend .topcoat-button:hover
  text-shadow: var-text-shadow;
  box-shadow: var-box-shadow;
}

.topcoat-button--round:focus {
  @extend .topcoat-button:focus
}

.topcoat-button--round:active {
    color: var-color;
    text-shadow: var-text-shadow;
    background-color: var-background-color--down;
    box-shadow: var-box-shadow--down;
}

Now, when you’ll run grunt again, the final CSS will include your button definition and the demo/topcoat-green.html demo page will present your new button with related documentation.

Creating new Topcoat components

Now you know how to add custom components and integrate them in the Topcoat workflow properly.

If you’re willing to share new components with the community (and you should), here’s the workflow :

  • demo your component markup and css on codepen.io
  • create a github repo for your component, using the same structure as any of the node_modules/topcoat-* folder.
  • if accepted, your component will be added to the main Topcoat package.json then auto-installed with npm install.

Need help ?

Comment below, use Github issues or join the #topcoat irc channel to discuss :)

I hope this quick intro will help you choose Topcoat as your next CSS framework and you will contribute to the project so we can build together a better CSS framework.

Phonegap Day Amsterdam 2013

| Comments

Once again, i’ve been lucky enough to go to Amsterdam today to attend the Phonegap Europe annual conference.

The venue was great, the talks covered various subjects, and, most importantly, the atmosphere reflected a positive and healthy community : open-minded and confident in the open web as a major platform for the future of mobile applications.

Almost 2 years after Adobe took control of Nitobi (the team behind the Phonegap genesis), the Phonegap project is in a great shape, dynamic, more open than ever, and with a sane and solid codebase, powered by node.js and a modern javascript toolchain, thanks to the hard work of the core team and many contributors.

With the 3.0 version, released a few weeks ago, Phonegap is now mature and flexible enough to serve as a foundation for your various mobile projects. Every single plugin has been extracted from codebase, which is now focused on bridging performance and plugin management.

Phonegap is the perfect example of how open source can increase code quality and grow a solid community while serving a company business.

Here is a quick recap of the most notable talks.

Distimo

Distimo is an app-analytic company that analyses app downloads, conversions and profits and compute smart data for the app developers.

They shared some interesting metrics :

  • 33% of worlwide app revenues comes from Asia.
  • Globally, ~65% of revenues comes from the AppStore and ~35% from Play Store (growing fast).
  • Candy Crush saga makes as much money in Asia on both iOS or Android
  • Games of course dominates stores revenues

More data at : http://www.distimo.com/publications

Untappd

Demo of one of the most successfull Phonegap apps : Untappd, a beautiful beer-centered social network app with 700k users and 4 stars rating. Join now !

Blackberry loves the web

Too late maybe ?

Native UI in Phonegap Apps

Tanaka Masahiro demoed the Monaca hybrid framework which gives you the ability to add native UI elements to your Phonegap app. You can wrap the phonegap webview with native tabbar/navigation bar to improve your UX, specially revelant on older Android devices. The framework also allows native transitions between various webviews, but this breaks application state. The framework hasn’t yet migrated to phonegap 3.0 so not usable as is IMHO.

Interesting real hybrid approach that demonstrates again that Phonegap is not necessarly a “full web” solution, but you can use as much “native” stuff as needed.

Apple phonegap apps rejection prevention

Rob Lauer from kendoui shared his experiences about mobile web apps rejection reasons on the Apple Store.

Here’s a bunch of common best practices to prevent your Phonegap app to be rejected :

  • Make an app, not a website
  • Create unique and useful apps
  • Don’t use annoying ads
  • User registration shouldn’t be mandatory (at least a free demo access)
  • Don’t force users to validate CGU
  • Never tag your app version lower to 1.0
  • Should work offline with minimal features
  • Don’t load remote code that change your app behaviour
  • Use beautiful, homogenous icons
  • Write accurate app description
  • Don’t try to steal Apple money, use in-app payments or die instantly

Topcoat.io

Kristofer Joseph, original TopCoat author, talked about this beautiful and slick CSS framework focused on performance and themability.

TopCoat.io provides various widgets as pure HTML/CSS and, most importantly, provides the best technical stack ever to extend it cleanly :

Another great example of a successful project, open-sourced from day one and following the best practices from the ground up.

TopCoat is definitely shaped for the future and you can already use it in your Phonegap projects !

Check out @devgirlfl article & demo : http://devgirl.org/2013/08/29/totally-rad-topcoat

WizCorp HTML5 gaming

Yes you can build great games with Phonegap. You don’t necessarly draw directly inside the webview, but as phonegap is very open and extensible, you can easily plug native features as needed, like Ejecta (off browser canvas+audio for iOS), webGL views… WizCorp team shares some interesting code on their github, like a native spinner, navigation views helper…

Chrome Cordova plugins

This may be the biggest announce of this event : We can now use Chrome APIs right in our cordova applications. For the moment, we just have socket, storage, notifications, and identity (oauth), but in a near future, we’ll see support for syncFileSystem (synced data across devices) and more.

Basically, this means that you can code a Chrome Packaged App using only chrome APIs, then distribute it on desktop via the chrome store and then on mobile app stores using cordova !

This is very early stage but Google puts some efforts to stabilize and extend capabilities quickly.

All Chrome javascript APIs : http://developer.chrome.com/apps/api_index.html

Cordova implemented APIs can be found at : http://plugins.cordova.io

Testing with appium.io

Fil Maj demonstrated appium, an open source end-to-end testing platform that fully support phonegap applications. You can write automation scripts (think selenium) then run them on your own devices, or use appium infrastructure to run the tests on various devices and produces detailed reported.

In a world of such devices fragmentation, this can be very useful :)

Firefox OS

Phonegap now also support Firefox OS ;) and Firefox provides many more javascript APIs, like ‘Activites’ (think Web intents). If you support the real open web, Mozilla is definitely the way to go :)

Phonegap CLI

This awesome talk by Michael Brooks was the most interesting and technical talk of the day. Michael showed us how powerful the cordova and phonegap CLI are. Based on a cutting edge javascript architecture, the phonegap CLI is made of nodes modules that you can use directly in the CLI, or as external nodeJS modules, which allows you to integrate the whole workflow in third-party tools and vice-versa. open source power :)

He also demonstrated how easy it is to create and compile apps with or without the locally installed SDKs, using phonegap build as a fallback. Just run for example phonegap compile ios from your machine, then in seconds you get back a QRcode that you can scan to test your final app on a real device instantly; neat !

Finally, a big thanks to Colene, Peter Paul Koch and all the Phonegap team for this event, can’t wait for next year :)