Easy Autocomplete with the datalist Element, the list Attribute and AngularJS’s ng-repeat Directive

Continuing to write about some Angular features I’ve been working with recently, I thought it would be interesting to show off one of the little conveniences I was able to put together using just a couple of Angular’s core features. This example leverages basically two basic features of Angular and two HTML5 features to create an easy, live autocomplete widget.

The pieces we’re going to use are:

  • Angular models This demo shows how easy it is to use Angular’s models. While they can get more complicated, this example shows the base case of simply binding to a variable in scope, creating dynamic links that update the UI when the data is updated with minimal or no JavaScript interaction.
  • ng-repeat Obviously if you’ve worked with Angular for any length of time you’ll be familiar with this directive. Here, it’s used here to iterate over a collection and create a dynamic datalist element.
  • datalist and list The datalist element and the list attribute are the two new HTML features that bind up the autocomplete functionality. In short, the datalist element defines a list of autocomplete entries. The list attribute binds the datalist to an input using an id reference. This combination is available in Chrome, Opera and Firefox with buggy support in IE10+

Let’s see how it all goes together.

First up, since this is Angular, let’s look at the markup.

To get started you need to define an Angular controller. This one is named MainCtrl. Since this is just a simplified demo to illustrate the autocomplete widget, it doesn’t do much, but the code it’s pulled from is a form used to add information about individual comic books to a simple database. Many of those comics are from the same titles (Amazing Spider-Man, Action Comics, etc.) so it’s handy to have an autocmplete list in order to improve data entry efficiency.

The input itself has two pieces to note, the data-ng-model attribute and the list attribute.

Note: As always, I append data- to Angular attributes in order to follow the HTML spec’s directions on custom attributes. That’s just me. You might want to let it all hang out. Fair play.

The data-ng-model attribute defines the model for the input as the title property of variable named item bound to the controller’s scope. Interestingly, this variable doesn’t exist at run-time. Instead of erroring with a warning about item being undefined Angular handles the creation of it once the form is submitted. This is a great example of how casual Angular is with their models. There’s no ceremony here. Add the data-ng-model attribute and the binding is done for you. If the variable exists in scope, the input is bound to it. If it doesn’t exist, Angular creates it. That’s pretty sweet.

The list attribute references the datalist element that the input should look for in order to build value hints. The value of the attribute is the id of the datalist you want to use.

The button at the end of the form is the next interesting piece of markup. On that input we’re using data-ng-click in order to bind the addItem function to click events on the form. Angular will look for addItem in the global namespace and in the controller’s scope. As you’ll see in the JavaScript example, we’re safely binding the method to the controller’s scope. If you click this button after adding a new title the title will be immediately added to the autocomplete list.

Finally, there’s the datalist element which uses data-ng-repeat to build out the list of autocomplete options. This directive is bound to the titles property of the controller scope. Since Angular models are bidirectional, once you update $scope.titles this list will be updated with new values. Pretty sweet. As you can see, this datalist element has the id we referenced earlier in the list attribute of the input.

<body data-ng-controller="MainCtrl">
    <form>
      <div>
        <label>title</label>
        <input type="text" data-ng-model="item.title" list="comicstitle">
      </div>
      <div>
        <input type="Button" value="Add" data-ng-click="addItem(item)">
      </div>
    </form>
  </div>
  <datalist id="comicstitle">
    <option  data-ng-repeat="ttl in titles" value="{{ttl}}"> 
  </datalist>
  </body>

Now that you’ve seen the markup, let’s look at the JavaScript that binds it all together. It’s actually pretty simple. Most of the wiring is done in the markup so the JavaScript is pretty sparse. That said, there are some interesting things so let’s look at it in-depth.

For starters we’re defining the starter list of titles as a variable on the controller’s scope. It’s a simple array bound to $scope.titles.

Have I mentioned that data binding is one of my favorite things about Angular? There’s absolutely no ceremony with creating a model. It’s just a variable on the controller’s scope (or even the global scope if you’re super lazy.)

After the model, the only other thing on the controller is a function called addItem.

This function is the method referenced in the data-ng-click attribute in the markup. It does a couple of things. The first is that is pushes the new value to $scope.titles. This example uses lodash’s contains function to test whether or not the title already exists in the array before it’s added. That keeps the list unique.

The next bit is another neat illustration of Angular models. Since the item we created when we added a title, setting it the object to an empty object is a shortcut to clearing the values out of the form. In this case there’s just the one form element, but if there were fifty or five hundred form elements with their data-ng-model attributes bound to properties of item this single line would clear all the values out of every bound form element.

app.controller( 'MainCtrl' , function( $scope ) {
  $scope.titles = [ "Action Comics" , "Detective Comics" , "Superman" , "Fantastic Four" , "Amazing Spider-Man" ];
  $scope.addItem = function( item ) { 
    if ( !_.contains( $scope.titles , item.title ) ){
      $scope.titles.push( item.title );
    } 
    $scope.item = {};
  }
});

Check it out, in action, with this plunker.


And there you have it. Let me know if you have any questions, comments or corrections in the comments.

My Slides from HTML5DevConf, Now with 100% More Browsing (and Photos)

let's do this

My slides from HTML5DevConf are now up for viewing on Github pages, so you don’t even need to type node web-server.js to view them. There’s one hitch where svgz files aren’t rendering, but I’m not going to worry about that right now. If, in the interim, you really want to see that image, you can grab it from the repo.

There should be video at some point in the future. When there is, I will be sure to let you all know.

I’m also going to be giving this talk at the Boston Front End Developers meetup in May. I’ll be sure to share that as well, once the meetup announcement is online.

I presented in twin peaks

ready to go

Reuse and Recycle: SVG

SVG has had a long and strange journey to the world of ‘emerging technologies” SVG was actually defined in 1999, so it’s not exactly the new kid on the block. Still, it took a while to catch on, so in some ways it feels like the new kid on the block. Like Canvas, SVG allows developer sot create graphics in the browser using native web technologies. Unlike Canvas SVG is a vector based grammar and, since it’s defined as XML it also allows for access using common DOM manipulation tools. One of the most difficult issues when dealing with Canvas is the need to manually manage the state, properties and events of individual elements. Since SVG elements are simple DOM elements, properties are stored as part of the regular DOM and access to individual elements is available using traditional DOM access methods like document.geElementById and document.getElementsByTagName.

One driver of popularity for SVG has been the emergence the RaphaelJS library. Raphael provides a convenient API on top of the specification and provides some measure of support for legacy IE browsers by rendering the output of Rapehl instructions as Vector Markup Language (VML.)

The following example shows Raphael and SVG in action. Example output can be seen in the following figure.
Created with Raphaël 2.1.0

The following code sample illustrates using Raphael to draw 10 random circles on an SVG element, filling them with a random gradient fill. The code is quite simple, with a new paper variable containing the instance of Raphael in use and then straightforward methods circle() and attr() used to create circles and fill them with the fancy gradient fill.

<!DOCTYPE html>
<html>
<head>
  <meta charset=UTF-8">
  <title>SVG</title>
</head>
 <style type='text/css'>
    #svg {
    width:100%;
    height:600px;
}
  </style>
</head>
<body>
  <div id="svg"></div>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js'></script>
  <script src='demo.js'></script>

</body>
</html>
window.onload=function(){
var svg = document.getElementById("svg"),
    paper = Raphael( svg ),
    circle,
    width = svg.offsetWidth,
    height = svg.offsetHeight;

for (var i = 0; i < 10; i++) {
  circle = paper.circle(
    parseInt(Math.random() * width), 
    parseInt(Math.random() * height ), 
    parseInt(Math.random() * 200));
  circle.attr({
    "fill": "r" + hex() + "-" + hex(),
    "fill-opacity": "0.5",
    "stroke": "none"
  });
  circle.click(function() {
    console.log(this);
    this.animate({
      cx: parseInt(Math.random() * width),
      cy: parseInt(Math.random() * height),
      r: parseInt(Math.random() * 200)
    }, 1000, "bounce")
  });
}

function hex() {
  //http://paulirish.com/2009/random-hex-color-code-snippets/
  return '#' + Math.floor(Math.random() * 16777215).toString(16);
  }
}

Inspecting the output the underlying markup isn’t quite so succinct, although it should be readable if you’re familiar with XML syntax and can follow along with the code that generated the example. The following code sample shows a single circle marked up using SVG syntax.
The interesting pieces to note are the definition of the radialGradient in the defs element. defs are used for reusable content. You can see it referred to in the circle element’s fill url.

<svg height="400" version="1.1" width="400" xmlns="http://www.w3.org/2000/svg" style="overflow: hidden; position: absolute; left: 0px; top: 0px;">
  <desc>Created with Raphaël 2.1.0</desc>
  <defs>
    <radialGradient id="0r_7d7f2f-_bbb372" fx="0.5" fy="0.5">
      <stop offset="0%" stop-color="#7d7f2f"/>
      <stop offset="100%" stop-color="#bbb372" stop-opacity="0.5"/>
    </radialGradient>
  </defs>
  <circle cx="170" cy="68" r="61" 
          fill="url(#0r_7d7f2f-_bbb372)" 
          stroke="none" 
          style="opacity: 1; fill-opacity: 1;" 
          opacity="1" fill-opacity="1"/>
</svg>

Bits and Bobs (Book Updates, the H5BP Ant Build Script, CanvasJS and My First Six Months at Sapient)

Time to share some random updates.

Mobile Web App in Practice

My Manning book has shifted tracks slightly. It was originally going to be a cookbook. It’s now part of Manning’s In Practice series so the title has changed to reflect that. With that slight adjustment out of the way we’re heading into serious production mode. For what it’s worth, my contribution is about 1/2 done at this point. The other authors are a little bit behind me, but they’ll start to crank soon. I’ll be sure to update once I have some solid dates.

Project Z

I won’t call it Project X since I worked on one of those already and I don’t want to confuse anyone that previously heard me talk about that behemoth. I signed a contract for another book project a few weeks ago. That’s in the planning stage right now. So much so I’m not even sure of the final title just yet. What’s interesting is that it’s a revised edition of a popular title by another author, so once we get the title sorted out and I can talk about it it’s going to be exciting.

I think it’s going to be a great project.

Sapient

Six months in, Sapient is going well. I’m enjoying myself personally and in terms of the big picture things are really starting to fall into place. I’ve got two excellent JavaScript engineers starting on my team in Bangalore this month. We’re really trying to build out a great team in India so it feels good to have found two strong guys to anchor our efforts over there.

What I’m trying to do now is build out the team here in North America. The focus is here in Boston, but I don’t think I’d turn our nose up at someone in New York or Toronto, for example. In Boston I’m looking for both senior and mid level people who are interested in building nutty JavaScript applications using bleeding edge technology. For example, my current project has me hacking away at both Raphael and D3. Fun times. If you’re an experienced front end engineer who’s tired of working on marketing sites and want to expand your horizons with some real application work, lemme know. I’d love to hear from you.

Also, I’m not a bad guy to work for (unless everyone is lying to me.)

CanvasJS

Call this a soft-alpha. I’ve been working (along with Bob Holt and Marc Neuwirth) on a little Canvas helper library called CanvasJS.

There are currently two areas of focus:

  • Chaining any method that doesn’t return an explicit value is chainable.
  • API Enhancements These range from new concepts (getting the boundingBox of the last operation, getting the currentPos (x and y) of the ‘cursor’,) missing methods (circle, rectangle) to convenience methods (canvas properties are now getter/setter methods.)

Additionally, I’m thinking it would be cool to offer polyfills for certain features that might have been missed in implementation or might be new to the specification.

Marc and I have both used it on projects at this point so I feel like it’s probably okay to talk about at this point. I’m going to buckle down and finish up the loose ends for a decent release. That’s basically documentation and a few API loose ends.

Here’s what the code looks like:

var ctx = new Canvas( &quot;ctx&quot; );           
ctx.reset();          
for ( var i = 0; i&lt;1000; i++ ){
  var color = 'rgb(0,' + Math.floor(255 - i/7) + ',' +  Math.floor(255 - i/100) + ')';
  ctx.beginPath()
     .line({
        x:.4*i,
        y:.4*i,
        angle: i, 
        distance:i * .40 
      })
     .strokeStyle(color)
     .stroke();
} 

And here it is in action.

I think it’s kind of cool.

H5BP Ant Build Script

Finally, we’ve been busy as hell on the Ant Build Script. In just the past couple of weeks we’ve simplified the way we handle concatenation (thanks dholth!) and added Less CSS (thanks Chris Rowe!) support. I’ve been really happy with the project since it’s split from the main HTML5 Boilerplate repo. We’ve got over 400 watchers and over 60 forks, which isn’t too shabby- we’re working our way up the “shell” rankings on github (we’re coming for you wemux). Which isn’t to say we don’t have a lot to do. We do. It’s just fun to watch the progress and feel like the project still has some good traction, even on its own.

My Latest IBM Article is Live: “An introduction to Ajax”

An introduction to Ajax.

Get a technical introduction to Ajax programming, and discover the core JavaScript code and popular library implementations. This article presents a brief history of the technology, then outlines the technical basics of Ajax interactions using core JavaScript coding as well as three popular JavaScript libraries.