EmberJS
- Adding properties to a model within an EmberJS router
- Sorting controller model results in EmberJS with
sortProperties
- Having a bidirectional computed property on an EmberJS text field
- EmberJS debugging tips
- Adding a Select2 View in EmberJS
- Using Radio Buttons in EmberJS
- Capturing EmberJS errors with Raygun.io
- Integration Testing with EmberJS and JQuery
- Adding an Index to an each iteration over a computed property in EmberJS
- EmberJS Handlebars sanity tests with Grunt
- Waiting for EmberJS to update its views manually
- Testing Mocha applications with Grunt using Jenkins
- Extending App.reset() in EmberJS
- Understanding Ember.Object
- Transient DS.Model Attributes in EmberJS
- Listing the Attributes of an EmberJS DS.Model at Runtime
- The Problems with Chai
- Adding an ‘application loading’ screen to an EmberJS application
- Multiple JQuery versions with EmberJS
- npm, bower, Handlebars, Grunt, JQuery
How I learnt EmberJS and Coffeescript and Git
I’ll be using the tutorial at http://emberjs.com/guides/getting-started.
- Install Sublime Text 2 as an IDE.
- Install git and nano. Use nano as the default git commit message editor, because vim support on the Windows command prompt is pretty poor:
git config --global core.editor nano
- Install the Sublime Linter plugin for ST2, which will provide syntax checking support, –by checking out SublimeLinter into your ST2 packages directory (since Sublime Linter is ST3 now):
%APPDATA%/Sublime Text 2/Packages
– by installing Package Control and installing Preferences > Package Control > Install Package > (wait for list to load) > Sublime Linter for ST3 (seems to work OK). This will give you things like PHP syntax error checking. - Install Node.js which will also install NPM (Node.js package manager)
- Use npm to install coffeescript:
npm install -g coffee-script
- Check that coffeescript is working:
coffee -v
- Install the CoffeeScript ST2 plugin by going Preferences > Package Control > Install Package > (wait for list to load) > CoffeeScript
- Sublime Text should now support coffeescript linting. (I couldn’t get this to work properly yet.)
- Create a file
%APPDATA%/Sublime Text 2/Packages/Default/CoffeeScript.sublime-settings
and configure ST2 to use spaces rather than tabs for CoffeeScript files. - Edit the csslint settings because they are pretty insane. Preferences > Package Settings > SublimeLinter > Settings - Default and change
csslint_options
:ids
tofalse
,overqualified-elements
tofalse
- Create a new Github repository https://github.com/soundasleep/todomvc-emberjs-coffee
- Checkout:
git clone https://github.com/soundasleep/todomvc-emberjs-coffee
- Update push.default on git to a more intuitive value (and also removes a warning):
git config --global push.default simple
- Follow the instructions in the EmberJS getting started tutorial, committing and pushing as necessary.
- Try to Cake building; unfortunately I couldn’t get this to work because of win32 problems
- Install Grunt for building Coffeescript instead:
npm install -g grunt
- Install the Grunt CLI:
npm install -g grunt-cli
- Create a package.json. Understanding package.json
- Create a Gruntfile.js.
- Install all of the necessary packages referenced in the Gruntfile:
npm install grunt-contrib-uglify grunt-contrib-qunit grunt-contrib-concat grunt-contrib-watch --save-dev
.--save-dev
will also modifypackage.json
with the new dependencies (underdevDependencies
). - Install https://github.com/gruntjs/grunt-contrib-coffee to compile coffeescript in Grunt:
npm install grunt-contrib-coffee --save-dev
- You can now compile Coffeescript by running
grunt coffee
. You can also configure Grunt to watch for new files by modifying your Gruntfile and runninggrunt watch
. - Configure Sublime Text to treat Handlebars templates as HTML: HTML syntax validation within Handlebars templates in Sublime Text 2
- Use the js2coffee interpreter a lot
- You will have problems with things like
function(){...}.property("x")
; wrap the function with brackets
DEPRECATION: Action handlers implemented directly on controllers are deprecated in favor of action handlers on an actions
object
Can also mean that an error has been thrown and your ApplicationController does not define an ‘error’ action (or EmberJS has created you an ‘error’ action and its incorrectly misinterpreting it as a directly implemented action? I have no idea).
This looks like an EmberJS bug and I guess it can be ignored - any other errors will be correctly reported (just sadly with no reliable stacktrace).
arrangedContent.addArrayObserver is not a function
Are you trying to #each over a bare JS object rather than an Ember array? e.g. instead of going:
Todos.TodosIndexRoute = Ember.Route.extend(
model: ->
todos: @store.find 'todo'
feeds: @store.find 'feed'
# ...
)
{{#each itemController="todo"}}
Going:
{{#each todos itemController="todo"}}
Computed model properties
An idea. Instead of:
date: DS.attr('string')
time: DS.attr('string')
Maybe:
datetime: DS.attr('date')
date: (->
datetime = @get('datetime')
moment(datetime).format('YYYY-MM-DD') unless Em.isEmpty(datetime)
).property('datetime')
time: (->
datetime = @get('datetime')
moment(datetime).format('HH:mm) unless Em.isEmpty(datetime)
).property('datetime')
Cannot perform operations on a Metamorph that is not in the DOM
The Error: Cannot perform operations on a Metamorph that is not in the DOM. can be caused if you have a Handlebars template using HTML comments rather than Handlebars comments. e.g.:
<!-- something
{{#each controller}}
...
-->
This should be:
{{!-- something
{{#each controller}}
...
--}}
Tested with emberjs-handlebars-sanity.
Hooking to valueBinding
You can modify an existing View
to handle valueBinding
in any way you want:
updateValue: (->
# this.value is set to the date, correctly
@$().select2 "val", @value
).observes('value')
Prevent JQuery.ajax.error from triggering an error state in Ember
Normally if you call $.ajax() and the requests results in an error, your errorCallback
will still be called, but then Ember in all its wisdom will redirect you to the error state regardless.
One way to solve this is to wrap the AJAX call in a Promise, which prevents the error state from being called - it looks like Ember assumes that a JQuery.ajax() error can reject the entire Promise within say, an afterModel
callback.
App.MyAPI =
query: (method, data, callback, errorCallback) ->
App.MyAPI.queryPromise(method, data).then (results) ->
# promise passed
callback(results)
, (status) ->
# promise failed
errorCallback(status)
queryPromise: (method, data) ->
new Ember.RSVP.Promise (resolve, reject) ->
$.ajax
type: "get"
url: "http://whatever.com"
crossDomain: true
dataType: "json"
success: (data) ->
# All async code has to be wrapped with an Ember.run
Ember.run ->
resolve(data)
error: (xhr, ajaxOptions, thrownError) ->
Ember.run ->
if (xhr.responseJSON)
resolve(xhr.responseJSON)
else
reject(xhr)
Get an object from the data store synchronously
this.store.getById('user', 1)
(thanks @eoinkelly)
ManyArray
A ManyArray
has no .length
or [index]
or for obj in array
. Instead, use .get('length')
, .objectAt(index)
and array.forEach(function(obj) { ... })
.
Adding a label from="id"
to an Ember text field or view
From http://stackoverflow.com/questions/10468164/using-ember-js-text-field-ids-for-a-label-tag, the following won’t work:
<label {{bind-attr for="content.field_id"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.data" id="content.field_id"}}
You need to instead use a viewName
and reference this with a bind-attr
:
<label {{bind-attr for="view.textField.elementId"}}> {{content.label}}</label>
{{view Ember.TextField valueBinding="content.value" viewName="textField"}}
Initialising model properties
See also this discussion on why Ember uses prototype inheritance and how it can mess things up wonderfully.
var Month = Ember.Object.extend({
setWeeks: function() {
this.set('weeks', Em.A());
}.on('init')
});
Run Javascript after EmberJS Route Render
For example, if you need to enable JQuery tooltips on an element in a Handlebars template, you can Ember.run.next:
App.FoosNewRoute = Ember.Route.extend
model: (params) ->
@store.createRecord('foo')
renderTemplate: ->
@render 'foos.new',
controller: 'foosNew'
Ember.run.next @, ->
$(".tooltip-span").tooltip
container: 'body'
Or, if you are using a view:
App.FooFormView = Ember.View.extend
templateName: "foo/form"
didInsertElement: ->
Ember.run.next @, ->
$(".tooltip-span").tooltip
container: 'body'
TypeError: cyclic object value
Make sure that you aren’t trying to save an object property that isn’t a DS.Model object. If you are, you may need to define a transient flag on the property, so that the JSONSerializer does not try to serialize the cycle: see Transient DS.Model Attributes in EmberJS.
Untested
Em.set(App, 'mapInstance', Map)
Em.get(App, 'mapInstance')
orApp.get('mapInstance')
App.__container__.lookup('store:main')
- maybe to get the data store instance (thanks @jamesotron)App.__container__.lookup('router:main').router.transitionTo('myroute')
- manually transition to a route (thanks @eoinkelly)