Wtf is Ember.js and How Do I Use this Blog
This Blog Teaches Ember.js through advanced examples not found in introductory material.
Ember A Javascript MVC framework for complex client-side web applications.
Ember Data A persistance and relation-based object system for Ember.js.
JSFiddle Browser-based Web-development "playground" app. EmberPlay.com and JSBin.com are other similar tools.
To set up JSfiddle, add jQuery 1.9.1 to the onLoad Framework. Add these two urls to your Managed Resources:

https://raw.github.com/emberjs/ember.js/release-builds/ember-1.0.0-rc.1.js

https://raw.github.com/wycats/handlebars.js/1.0.0-rc.3/dist/handlebars.js

Shortlist of Valuable Resources for Emberjs Developers
PeepCode $ PeepCode's Emberjs tutorial costs $12 bucks but is worth every penny
Toran Billups Toran has the best intro-to-ember materials around...in my opinion.
DarthDeus DarthDeus explains many of Ember's major systems
Kasper Tidemann Kasper's blog is short and sweet and includes helpful links and tutorials
Contact stv_kn to add your site.

Dynamic Ember View Updating

View

Dependencies

The code for this post is built with Ember.js 1.0.0-RC1, handlebars RC3, and jQuery 1.9.1

Objective

We will see how the Ember.View updates when changing the model properties using mouse event coordinates. The end result is drawing a box which is an Ember.Object using click and drag.

Demo

This jsFiddle demonstrates the end result: Completed Fiddle. Click on the gray box and hold down the mouse button. Drag to create and update a box object.

Ember Application Setup

Here we have defined two handlebars templates. The first is unnamed and will be used by the Application View. The second is called “box” using data-template-name and will be used by the Box View.

1
2
3
4
5
6
7
<script type="text/x-handlebars">
{{ outlet }}
</script>

<script type="text/x-handlebars" data-template-name = "box">
{{content.text }}
</script>

Here we setup the App, Router, Routes, and Ember Object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#SETUP APPLICATION
App = Ember.Application.create()
App.Router.map () ->
  @resource "box"

App.IndexRoute = Em.Route.extend
   redirect: ->
        @replaceWith 'box'

App.BoxRoute = Em.Route.extend()

App.Box = Ember.Object.extend
    height: 0
    width: 0
    top: 0
    left: 0
    text: 'YAY COMIC SANS'

Application View / Controller

The Application View and Controller will be auto-generated by Ember if they are not declared. For this demo, the Application View will be handling the mouse events.

1
2
3
#uses 'needs' API to access the box Controller
App.ApplicationController = Em.Controller.extend
    needs: ['box']

The needs property lists other controllers so that you can access them from this controller by using this.get(‘controllers.otherController’). ‘otherController’ being ‘box’ in this case.

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
#events handled on this view
App.ApplicationView = Ember.View.extend
    classNames: ["main"]

    #EVENTS below
    mouseDown: (event)->
        viewOffsetLeft = @$().offset().left
        viewOffsetTop = @$().offset().top
        boxCon = @get('controller.controllers.box')
        X = event.pageX - viewOffsetLeft
        Y =  event.pageY - viewOffsetTop

        boxCon.set('initialX', X)
        boxCon.set('initialY', Y)
        boxCon.createBox(X, Y)
    #calculate view offsets to get accurate click positions
    mouseMove: (event)->
        viewOffsetLeft = @$().offset().left
        viewOffsetTop = @$().offset().top
        boxCon = @get('controller.controllers.box')
        boxCon.updateBoxArea(event.pageX - viewOffsetLeft,
                             event.pageY - viewOffsetTop)
    #stops the box area update
    mouseUp: (event)->
        boxCon = @get('controller.controllers.box')
        boxCon.set('isUpdating', false)

Event Handling

Ember.View’s can handle many/most of the browser events, a description may be found here: Ember View API . Here we have used the mouseDown, mouseMove, and mouseUp events to capture a click and drag in the browser. We declare a ‘boxCon’ variable that is set to the Box Controller, which was made available by using needs on the Application Controller.

The mouseMove event is using the pageX and pageY attributes of the event. The reason for this is moving the mouse over a Box View would give the offsets relative to that view instead of the App View’s offsets. Since we are using event.pageX and event.pageY, we should account for the offset of the App View relative to the absolute top and left of the page. @$() or this.$() is a jQuery element which we then call the jQuery offset() method on. This returns an object with top and left properties. Each event then passes on the event information to the Box Controller via a method call or setting an attribute.

BoxController and BoxView Setup

The BoxController has an ‘isUpdating’ flag, and the initial coordinates of the mouseDown event. The two methods are to create a box object at the click and to update the area of the new box based on mouse movements.

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
#controller to handle the calculations on the box properties
App.BoxController = Ember.ObjectController.extend
    initialX: null
    initialY: null
    isUpdating: false
    #calculate the new properties of the box
    updateBoxArea: (X,Y)->
        if @get('isUpdating')
            box = @get('content')
            origLeft = @get 'initialX'
            origTop = @get 'initialY'
            if X >= origLeft
                left = origLeft
                width = X - origLeft
            else if X < origLeft
                left = X
                width = origLeft - X
            if Y >= origTop
                top = origTop
                height =  Y - origTop
            else if Y < origTop
                top = Y
                height = origTop - Y
            box.setProperties({left: left, top: top, height: height, width: width})
    #create a new box at the click coordinates
    createBox: (X, Y)->
        a = App.Box.create({top:Y, left:X})
        @set('content', a)
        @set('isUpdating', true)

The BoxView is what allows the updated box object to be displayed. attributeBindings will attach an attribute to the view element. In this case the attribute we want is style. ‘Style’ is defined here as a computed property which returns a string. The result is the view element will have style=”height:50px;width=50px; etc…” This value will update whenever the properties of the box update, and cause the view to be re-rendered.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#View dynamically sets the 'style' attribute via computed property and rerenders
App.BoxView = Ember.View.extend
    classNames: ['box']
    attributeBindings: ['style']
    contentBinding: 'controller.content'
    style: (->
         height = @get('content.height')
         width = @get('content.width')
         top = @get('content.top')
         left = @get('content.left')
         heightString = "height:#{height}px;"
         widthString="width:#{width}px;"
         topString="top:#{top}px;"
         leftString="left:#{left}px;"
         return heightString + widthString + topString + leftString
     ).property('content.height', 'content.width',
                'content.top', 'content.left').cacheable()

Improving the System

Here is the working jsFiddle showing the results of this demo: Completed Fiddle

What can we improve? Currently, during the box update there is a flag on the controller called ‘isUpdating’ which is set to true, then when the update ends this flag is set to false. We could run into problems in the future if we add more features and forget what this flag is for. Then we could run into some crazy behavior since those mouse events are still firing when we move the mouse. A better solution might be to use a state machine instead of a flag and change states when appropriate. Each state would handle events and only the appropriate outcomes would be allowed.

That wraps it up, thanks for reading!

Peanut Gallery (with affection)