revolunet blog

web technologies for desktop and mobile

AngularJS Tips’n’tricks Part 2

| Comments

Here’s a new batch of AngularJS tips and gotchas; If you didn’t read it yet, you can check the part 1 and feel free to comment below :)

Access an element scope from outside

This is useful for debugging: in your Chrome console, highlight a node in the Elements tab, then, in the console, to check its scope :

1
angular.element($0).scope();

or even :

1
angular.element(document.getElementById('elementId')).scope();

The Batarang Chrome Extension is much powerful and let you inspect any scope, anywhere :)

Unwatch an expression

Sometimes you want to watch an expression only a few times, and then forget it. The $watch function returns a callback just for that. You just have to execute it back to destruct the watcher.

1
2
3
4
5
6
7
var watcher = $scope.$watch('data.counter', function(newValue, oldValue) {
    iElement.css('width', 50 * newValue + 'px');
    if (newValue >= 10) {
        // autodestruction when data.counter reaches 10
        watcher();
    }
});

Group elements in a select

If you play with selects, the ng-options directive is quite powerful and has many syntaxes.

For example you can easily group a model by attribute to have a nested select menu :

1
<select ng-options="distrib.version group by distrib.name for distrib in distribs"></select>

Of course, just add an ng-model directive to your select to data-bind it to one of the values.

Filter falsy values

You can’t use the builtin filter for falsy attributes or values :/ I don’t know if this is a bug of feature, but a simple workaround is to use a custom filter function that you can expose on the scope. (you can also define your own filter).

1
2
3
4
$scope.testValues = ['a', 'b', 'c', false, true, 0, -1, 5, 42];
$scope.isFalsy = function(val) {
  return !val;
}
1
2
3
4
5
{{ testValues|filter:true }}        //  [true]
{{ testValues|filter:5 }}           //  [5]
{{ testValues|filter:0 }}           //  ['a', 'b', 'c', false, true, 0, -1, 5, 42]
{{ testValues|filter:false }}       //  ['a', 'b', 'c', false, true, 0, -1, 5, 42]
{{ testValues|filter:isFalsy }}     //  [false, 0]

Filter on objects

The builtin filter function is quite powerful and allows you to filter a list of objects on given properties only, or on everything, exactly or not, negated or not, etc…

1
2
3
4
5
{{ games|filter:'street' }}                       //  all games containing "street" in any property
{{ games|filter:'!street' }}                      //  all games NOT containing "street" in any property
{{ games|filter:{name:'street'} }}                //  all games containing "street" in their name
{{ games|filter:{name:'street'}:true }}           //  all games named exactly "street"
{{ games|filter:{name:'street', device:'PS3'} }}  //  all games containing "street" in their name and PS3 in their device

ng-repeat with duplicate items

If for some reason you need to have duplicate items in your ng-repeat you can extend your ng-repeat expression with track by $index and it works :)

Directive : parse attribute without watching it

In one of your directive, if you need to have a read-only access to an attribute model, but without the automatic watch/binding, you can simply use & instead of = when declaring the binding in the scope. You’ll then be able to access the model value . (this may break in future releases). This is a shortcut for using the $parse service on the current scope.

1
2
3
4
5
6
7
8
9
10
11
12
app.directive('rnCounter', function() {
  return {
    scope: {
      count: '&rnCounter'
    },
    link:function(scope, iElement) {
      iElement.bind('click', function() {
        console.dir(scope.count());
      })
    }
  }
})

Data-binding to a boolean

You can bind to a boolean value, but you can’t update that value from your directive.

This won’t work :

1
<div rn-zippy status="true"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.directive('rnZippy', function() {
  return {
    restrict: 'A',
    scope: {
      status: '=?'
    },
    link: function(scope, iElement) {
      function toggle () {
        scope.$apply(function() {
          scope.status = !scope.status;
        });
      }
      iElement.bind('click', toggle);
    }
  }
})

You need to use a real model, OR, initialise a new one and use it as your model instead :

1
<div rn-zippy ng-init="status=true" status="status"></div>

Includes onload

You can trigger a callback when your ng-include partial is loaded :

1
<div ng-inlude="'partials/' + page + '.html'" onload="callback()"></div>

For the ngView, you need to listen to the $viewContentLoaded event.

Express testing

You know you should write serious tests suites ? There’s everything to help you in AngularJS with Karma test runner + Jasmine.

Once you have many tests, they can take some time to execute and there’s a little trick to speed you up : you can limit the test-runner to a given test group by using ddescribe instead of describe and to a single test with iit instead of it.

That’s awesome to focus on specific tests for a while.

Service decorators

You can easily decorate and modify any existing service or directive. That’s how the ngMobile overrides the ngClick directive to handle transparently the FastClick behaviour.

Here’s an exemple that overrides the $sniffer service and fix the animation detection for older androids devices (landed in ef5bc6c) :

1
2
3
4
5
6
7
8
9
app.config(['$provide', function ($provide) {
  $provide.decorator('$sniffer', ['$delegate', function ($delegate) {
    if (!$delegate.transitions||!$delegate.animations) {
      $delegate.transitions = (typeof document.body.style.webkitTransition=== 'string');
      $delegate.animations = (typeof document.body.style.webkitAnimation === 'string');
    }
    return $delegate;
  }]);
}]);

That’s all for today, feel free to ask and comment below :)

Learn AngularJS in 5 Steps

| Comments

I’ve seen some developers quite shy about AngularJS when confronted to all the magical powers; don’t be afraid, AngularJS indeed introduces several new concepts but you can get them in just a few weeks of practice… The Google team is working hard to lower the Angular entry barrier and to make everything as simple as possible for developers.

AngularJS is a new disruptive innovation in the Javascript frameworks world and it prefigures the future of web browsers : templates, data-binding, web components…

In this post i’ll give some advices for the AngularJS newbies and share with you the best pointers to learn and understand the core concepts quickly.

First, to understand AngularJS you of course need to know a bit of Javascript. If you’re not comfortable enough yet, pick a free book on JSbooks and practice.

If you have a jQuery background, you absolutely need to read this in-depth Stack Overflow answer : How to think in AngularJS if i have a jQuery background. Just imagine the productivity boost you’ll get when you won’t need to maintain the DOM anymore :)

To sum up key features of AngularJS :

  • Templates : Your templates lives right in the DOM. Forget mustache, handlebars, hogan…
  • Two-way data-binding : Your javascript data automagically updates your DOM and vice-versa.
  • Routing : Define routes, associate templates and controller in just a few lines.
  • Forms : Many helpers to help dealing with forms, one of the worst webdevs nightmare.
  • Directives : Encapsulates templates and code in reusable components easily.
  • Testability : AngularJS was designed in a way your web app can be fully testable.
  • Animation : Easily animate elements and views.

Here are 5 steps to boost your AngularJS skills right now :

  1. follow the official tutorial and the dev guide.
  2. watch the awesome egghead.io video series. they’re 3mins each only :)
  3. watch the 60mins AngularJS fundamentals video
  4. read the code of some great AngularJS app examples.
  5. read as many times as needed the nuances of scope prototypal inheritance

Of course, dont just watch/read, you need to experiment every step :)

After that, you should have a pretty good understanding of the framework and be able to angularize all the things :)

Need help ? Ask your questions in the comments below, or on IRC #angularjs channel, or on the dedicated google group.

BTW, if you read French, great ressources here : frangular.com and Thierry Lau’s introduction slides.

Need more ressources ?

GoogleIO 2013 Keynote Recap

| Comments

So today was the opening of the 2013 GoogleIO developer conference in San Francisco and the Googlers announced a insanely crazy amount of new projects/services/features in just the first few hours, which is very exciting for every web/android developer. The next three days will be very busy, with a lot of focus on Chrome, Android and Google+.

Though, we don’t have new Nexus devices, no Android 4.3, and no Javascript AppEngine runtime. So huge fail on my previous predictions.

The keynote started with a big fail demo effect happened in the very first demo about Android multiplayer gaming when demoing a new Google Cloud Messaging feature, quite embarassing, but then everything ran smoothly.

Here’s a little recap of the most important things announced just during the 3 hours keynote :

Android

  • Google cloud messaging : can now be used in a bidirectionnal way, and apps can now also store data in the cloud and sync it across devices (scores, leaderboards…).
  • Google Game Services platform to help game developers with new APIs
  • Samsung Galaxy S4 will be available in June on Google Play with standard Android build and unlocked bootloader ! 649$ though but without that ugly, messy, shitty Samsung overlay.
  • Android studio to replace Eclipse nightmare, finally ! good move !
  • Unified and synced notifications between devices.
  • Translation service for apps, better analytics…
  • Enhanced maps API

Google Music

  • Access to all the music for 9.99$/month
  • Discover to new recommended songs with google suggestions
  • Google powered radio feature based on what you like

Chrome / Web

  • 750 millions active chrome users globally
  • WebP and VP9 royalty-free compression standards for images and videos to increase speed specially on mobile devices.
  • Google provides a free (deactivable) proxy for Android to improve speed based on this codecs.
  • New payment autocomplete API will help process payments quickly on websites, specially useful on mobile (2 clicks payments)
  • Google will finally release a rock solid UI framework based on web components standards : polymer-project.org . I wonder how it will fit with AngularJS.
  • V8 will integrate asm.js to improve Javascript performance.

Google +

  • New interface :)
  • Better Hangouts
  • Unified notifications across devices
  • Better pictures management, event an ‘auto awesome’ mode :)

Maps

  • iPad version this summer
  • Brand new Google maps, with new design, more space for content, and builtin 3D thanks to WebGL

Gmail

In other news

Anything missed ?

You can still watch the full keynote on Youtube of course.

Making of the Angular-carousel

| Comments

Our first real life experiment with AngularJS is a mobile and touch-friendly carousel directive.

As this project shed some lights on various AngularJS aspects, we’d like to share it with you; The full demo+code is available here : http://blog.revolunet.com/angular-carousel

The goal is to transform any ng-repeat directive into a carousel, just by adding the rn-carousel attribute to its ul container. The directive take care of the dirty work for you, without any jQuery.

Here’s a very basic example of a Youtube API call, with the data displayed in a regular Angular template; We just add a rn-carousel attribute to the ul element to ask a carousel :)



This rn-carousel directive is responsible of :

1) wrapping the real ul/li in a carousel container and add CSS classes

We first wrap the original ul block in an outer div using JQlite wraps method during the directive link phase. This cannot be doneduring the compile phase as it would lead to an infinite recursion loop. (AngularJS will wrap again an again as the original directive stays untouched).

Then we add some CSS classes and an unique carousel identifier class so we can target that instance easily later.

The CSS makes the ul/li block appears as a single horizontal line composed of many li blocks, so its very easy to slide the blocks horizontally.

2) watching the ng-repeat expression

Because we want to monitor precisely the items in our carousel, and we want to be able to dynamically add/remove items, we need to extract and watch the original ng-repeat expression. We can then watch that expression and then be notified when its updated. This is useful to know how many slides we have and update our indicators accordingly for example.

This extraction is done by literally parsing the ng-repeat expression in a generic way, using the generated normalized comment node. Credits goes to @bennadel for this awesome trick.

Update : I ended up with just extracting the li ng-repeat attribute during the compile phase which is much less overhead.

3) binding touch and click events

Like most directives, we bind events in the linking phase. We just take care of binding both touch and mouse events.

1
2
3
4
// bind events
container.bind('mousedown touchstart', swipeStart);
container.bind('mousemove touchmove', swipe);
container.bind('mouseup touchend', swipeEnd);

4) animating the slides smoothly

When we detect a touchstart we just store the initial event position. On touchemove we update the horizontal offset and move the whole ul accordingly using CSS. Then on touchend, we detect the desired position (prev or next) and dynamically add CSS styles and classes to move the slides accordingly, or restore the current position in some cases.

The slide movement is made thanks to the CSS transition and translate3d properties to force CSS 3D transforms and thus animate the slides using the GPU which make the movement much more smooth especially on mobile devices.

1
2
3
4
5
6
7
8
// the offset value is simply : desiredSlideIndex * slideWidth
carousel.css({
    '-webkit-transform': 'translate3d(' + offset + 'px,0,0)',
    '-moz-transform': 'translate3d(' + offset + 'px,0,0)',
    '-ms-transform': 'translate3d(' + offset + 'px,0,0)',
    '-o-transform': 'translate3d(' + offset + 'px,0,0)',
    'transform': 'translate3d(' + offset + 'px,0,0)'
});

5) adding an indicator to our carousel

As we already watch the ng-repeat expression, we track the number of slides in the carousel and so its quite easy to add data-bound indicators to our carousel. You can enable these by adding a rn-carousel-indicator="true" attribute on your ul.

The indicators are produced by the rn-carousel-indicators directive, completely decoupled, during the linking phase. For the directive to be executed, we compile it through the $compile service before appending it to our container.

It just takes items and index attributes that will be injected in an isolated scope to produce the indicators and set the CSS classes.

1
2
3
4
5
6
7
8
9
10
11
// enable carousel indicator
var showIndicator = (iAttrs['rnCarouselIndicator']==='true');
if (showIndicator) {
    // as the HTML contains a directive, we need to $compile it
    // before appending it to our container
    var indicator = $compile("<div id='" + carouselId +"-indicator' " +
        " index='carouselIndex' items='carouselItems' rn-carousel-indicators " +
        " class='rn-carousel-indicator'></div>"
    )(scope);
    container.append(indicator);
}

6) offer a two-way data binding for the active slide index

The carousel current index position can be data-bound to an optional rn-carousel-index attribute if provided. This allows you to display the carousel position somewhere else, add custom controls/indicators, initialise to a different slide…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// set initial position at first slide
scope.carouselIndex = 0;

if (iAttrs.rnCarouselIndex) {
    //  if a binding attribute is present, use it.
    var activeModel = $parse(iAttrs['rnCarouselIndex']);
    scope.$watch('carouselIndex', function(newValue) {
        // when the carousel internal index changes, update the model
        activeModel.assign(scope.$parent, newValue);
    });
    scope.$parent.$watch($parse(iAttrs.rnCarouselIndex), function(newValue) {
        // when the model change, update the local position
        // this is also useful for the initial position
        scope.carouselIndex = newValue;
        updateSlidePosition();
    });
} else {
    // if no index attribute, just watch internal index and update display
    scope.$watch('carouselIndex', function(newValue) {
        updateSlidePosition();
    });
}

The updateSlidePosition() function simply moves the ul block based on the carouselIndex value and the width of the slides. We do this with CSS transition and translate3d so this is animated and hardware accelerated.

7) tests and grunt automation

These subjects will have a dedicated article soon :)

Limitations

  • Only works with ul/li structure
  • Carousel dimensions is based on the first li detected

UPDATE : the carousel can now be buffered to improve performance with large datasets !

Easy isn’t it ?

If you have questions, suggestions, issues, pulls requests… just comment below or on Github :)

AngularJS Tips’n’tricks Part 1

| Comments

In the past few months we learned a lot of new AngularJS stuff. Here is a first bunch of tricks, please comment and help us improve theses exemples if your tricks are even more awesome :)

We compiled most of our best AngularJS ressources in a gist and we maintain a list of related twitter accounts here : https://twitter.com/revolunet/angularjs.

Scopes

You definitely need to read this article : http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs

Directives

The official documentation is not crystal clear at first approach but it documents most of what you need to understand: http://docs.angularjs.org/guide/directive. So read it. And again. And again.

Watch an object or a list

If you watch a list or an object with the objectEquality parameter not set, then the $watch callback won’t be triggered if the object is only updated partially. Setting it to true will trigger it on any modification; this has performance impact so use carefully.

1
2
3
4
// the last `true` means compare by value and not by ref, useful for nested objects :)
$scope.$watch('myConfig', function(newValue, oldValue) {
    console.log('myConfig changed !', newValue);
}, true)

Two way data binding attribute without explicit scope declaration.

Sometimes you need to access a directive attribute with a two-way data binding without declaring it explicitely in the directive scope configuration. You can do this using the $parse service.

If your directive scope is isolated (scope: {}) you have to use scope.$parent instead of scope.



Use a callback function with arguments in your directive

To call a function defined in one of your attribute, declare it in the scope with &.

If you need to pass arguments, you must execute the callback using an object form for the arguments. AngularJS then call the function with your arguments in the correct order comparing your object and the callback declaration.

The following example also demonstrates how to watch multiples values at once.



Custom transclusion

This example shows how to define a custom transclusion function and modify its scope.



Announcer/Notifier pattern

If you need to listen to DOM objects creation/deletion, you can simply create a ‘notifier’ directive and an ‘announcer’ directive that you’ll add to the objects you want to monitor. That directive will announce new instances creation (link called) and destruction (scope.$destroy called).

It can be useful if you want to monitor arbitrary objects lifecycles, for example a ng-repeat. The ‘announcement’ can be made via events, or better, using a custom notifier controller. All credits goes to @garanajpr for the ideas.



Many more tricks to come, stay tuned :)

UPDATE : checkout PART 2 :)

Using AngularJS Views Animations for Mobile Applications

| Comments

AngularJS team recently added the very expected animation feature to their framework. You can have a great overview with the Miško Hevery introduction video and a complete overview article at the great Year of Moo blog.

From now (AngularJS 1.1.4), you can easily animate any directive, just by adding an ng-animate attribute and defining the animation CSS class.

UPDATE : AngularJS team fixed the following problem in the last 1.1.5 release. The ng-animate will now be evaulated on each animation, Yahoo :)

UPDATE : Just created an much more simpler example based on AngularJS 1.2; check it out : http://embed.plnkr.co/p7WZdsy0FznV3zo7VWjU/

As a mobile web developer, the first thing that come to mind is animating your mobile applications views. Its quite easy to add slide-in effects with the current routing system :



As you can see, the code is very simple code and the animation is always the same (appear from right). But typically, if you open a screen in your mobile application, and there is a ‘Back’ button, the user expects the slide animation to be reversed when he touches it. If you don’t do this, you expose your users to an unexpected behaviour, and an akward #WTF moment.

With the actual AngularJS implementation, there is a problem with the ng-view directive, which is heavily used for the AngularJS routing : it doesn’t handle dynamic ng-animate directive at all, despite what says the doc. You can only set the enter and leave animation once, because these are set at linking time in case of the ng-view. And thus, you can only have one pre-defined enter and leave animation for your application view and for the whole application lifecycle.

There are a few issues opened and we can hope the team must be working on it and that should be resolved some day.

But an easy workaround to achieve this is to act on the enter and leave CSS classes themselves directly. Instead of changing the CSS classes names at the DOM level, one could update the CSS classes definitions to achieve the same result without even touching the DOM. The ng-view directive still has the same enter and leave CSS classes names and instead we change theses classes definition, and thus the animation direction.

There are several ways to do this. We could dynamically add a stylesheet to the document, or change the stylesheet source using ng-src attribute but this wouldn’t be very efficient due to multiple downloads of the ressource.

But, as we are AngularJS powered, we could simply bind a style tag to a javascript variable so it can be updated dynamically like any other DOM element.

Here’s how we’ve done bi-directionnal animations for the ng-view. Note the usage of the ng-bind-html-unsafe that take cares of not escaping the CSS output and the use of transition and transform CSS properties to force the use of GPU and improve the animation smoothness, especially on mobile devices.



Another clever solution, as pointed out by Andrej in the comments below, is to simply wrap the ng-view in another div that have a dynamic class name, which could change when we need to change the animation direction, like LR then RL. This way we just need to update that classname and the CSS will pick the good one.

1
2
3
4
.LR .enter-setup {...}
.LR .leave-setup {...}
.RL .enter-setup {...}
.RL .leave-setup {...}

Octopress Cheat Sheet

| Comments

When you switch to Octopress for blogging there are a very few things to know :

Command line

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# create a new post
rake new_post['Title of the post']

# create a new page 
rake new_page['Title of the page']

# preview your work
rake preview

# publish it
rake generate && rake deploy

# commit and backup(automatic message)
git commit -am "`date`" && git push origin source

Creating a post

You can create a post just by adding a .markdown file in the source/_posts folder and preview it with rake preview. The name of the file will be your post url so use an appropriate slugified name.

You must add some metas at the top of the file :

1
2
3
4
5
6
7
8
9
---
layout: post
title: My Awesome article.
date: 2013-05-1 00:00
categories: news technology web
author: Super Man
comments: true
published: false
---

If you set published: false, your posts will only be visible in preview mode.

Publish

When ready, just use rake generate && rake deploy to publish it :)

Here’s a micro script to simplify the publish process, jsut put it in the blog root folder :

1
2
3
4
5
#!/bin/sh
# push.sh : publish & commit with a single command
git add source
git commit -am "`date`" && git push origin source
rake generate && rake deploy

A Fresh Start

| Comments

Our good old WordPress blog has been shutdown since a few months and now its time for us to start a brand new blogging adventure with OctoPress. As expected, we’ll talk about Web technologies, Rich web applications, Javascript, Python and Phonegap among others.

We’ve got so many stories to tell, so many tips to share and so much code to dissect, so stay tuned.

140 chars isn’t long enough.

1
console.log('Welcome :)');