Tuesday, June 24, 2014

AngularJs custom directive trouble

So I decided to build very simple directive in AngularJs. Came up with this simple code:

<!doctype html>
<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.js"></script>

    <script>

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

      app.directive("taWithMessage",function() {
    return {
    template: "<p><h2>sample</h2></p>",
    replace: true
    };
   });

    </script>

  </head>
  <body>

    <div ng-app="myApp">

<div ta-with-message></div>

    </div>

  </body>
</html>

.. ran it and BOOM! Got this:


To make it more interesting replacing:

template: "<p><h2>sample</h2></p>",

with

template: "<p><span>sample</span></p>",

or:

template: "<div><h2>sample</h2></div>",

Worked just fine. It obviously didn't have anything to do with display of the element as div s well as p are both block, yet div works fine, p didn't. 

Then I started debugging and after a while I realised what a dummy I've been. Given original code, the browser was interpreting it as:

<p></p><h2>sample</h2><p></p>

This excerpt from specs should clarify what the issue was:

A p element's end tag may be omitted if the p element is immediately followed by an address, article, aside, blockquote, div, dl, fieldset, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, hr, main, nav,ol, p, pre, section, table, or ul, element, or if there is no more content in the parent element and the parent element is not an a element.

If you ever read the HTML specs you already know that some elements do not need to have closing end tags. I was well aware of this yet at this point didn't realised that was the issue. 

Decided to dig further into AngularJs source itself to see what exactly is going on in there and as it turned out innerHTML is the culprit:



AngularJs nicely passes template code to JQLite (highlighted in yellow below) and JQLite at some point runs it via innerHTML. This spits out modified as per above specs code. Then as shown below, AngularJs checks if the code is valid, so have one root only:



One root meaning:

<div><p>something</p><p>something else</p></div>

Rather than:

<p>something</p><p>something else</p>

As the code JQLite spits out is:

<p></p><h2>sample</h2><p></p>

It fails the check and Angular throws Error.

All this Googling and head scratching could be avoided if I only remembered the basic specs. Shame on me.. =/

Thursday, June 19, 2014

(Mis)adventures with Zepto

I was blessed with a task of developing mobile version of existing site. “Easy” I thought as the site had decent HTML, CSS and decent JS code. After all I developed it ;). Module pattern kept things separated nicely, namespacing was in place and all the files in production were concatenated into one and minimized.

Then someone very smart suggested that as mobile site will not be Javascript heavy I should dump jQuery and go with lighter option Zepto. And so I did.

For those not familiar with Zepto, according to their website its:

Zepto is a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto.

Minimalist, sure, less than 10kb gzipped and largely compatible with jQuery. As I was writing JS from scratch it seemed like a great idea. Todays mobile browsers are quite capable anyway and I could do without fadeOut's and such.

Right, so I started developing. Code I developed for desktop version had bits that I could reuse in mobile site. Things like Cookies management, form validation and all this sort of stuff could (should) be reused and so I decided to put these bits into Common.js. In production, this Common.js file would be concatenated with desktop JS or mobile JS source depending on what device would make request. So far so good. Brilliant.

Only then I realized that these bits of code, sometimes contain functionality that is supported by jQuery, but not by its lighter brother (sister?) Zepto.

As you might expect effects like:

$("div p").fadeOut(3000)

Will not work. To be honest there was no need for that. Most of these can be done with CSS transitions anyway so it’s not a biggy. But then I noticed few other things didn't quite behave as expected. This was a bit worrying as not being familiar with Zepto I couldn't pick these up just by looking at the code. E.g. using some of the selectors was causing trouble:

$("div p:even")

As useful as this construct is with Zepto it will fail. There are few other, sometimes subtle differences. If you ever tried to clone element, jQuery provides very convenient “clone” method. In Zepto it seems to work just fine until you realize that it doesn't take optional argument that specifies if the event handlers should also be cloned. Element itself will be cloned, but you will have to attach events yourself somehow (or deal with these higher in DOM somewhere).

All in all Zepto is really nice and does most of the stuff. However if you have existing code that might not be worthwhile spending time modifying it just so it works with Zepto. There is always a risk that something might not work as expected and the behavior of “clone” method is the best example. After all, jQuery 2 is around 30kb gzipped and in most cases size is not so much of an issue. If it is however make sure you test everything when migrating to Zepto to avoid painful surprises.