Top 5 Best Resources for EmberJS
getting-started-with-ember-js-using-ember-cli
http://www.sitepoint.com/getting-started-with-ember-and-ember-cli/
The PacktPub books have code files than can be downloaded from the website.
Ember.js Web Development with Ember CLI from PacktPub
Mastering Ember.js from PacktPub.
Ember CLI 101 from leanpub.com.
Ember.js Guides from leanpub.com.
Ember.js Guides Plus from leanpub.com.
These are features of Ember’s objects. See the Ember.js Web Development with Ember CLI book for more information.
Reusable code that can be shared amongst your other modules (classes).
A Method that returns a value.
Use to access properties of an Ember class.
Code that is triggered by an event (when a property’s value changes).
When the value of an object’s property actually comes from a different object.
A route is a url path and what should be done when that URI is requested. The route will specify two things:
A resource is a group of routes. i.e. user/, user/1, user/1/edit, user/1/delete
, etc.
/app/router.js
holds your Router Map, which is used to determine which Route to use.
Because one of Route’s jobs is to get your data, that is where you will place additional logic (say filtering the data source). Put it in app/routes/path.js
First, a bit of history. Asynchronous module definition (AMD) is a standard by which Javascript code is encapsulated and can have dependencies. This code can then be asynchronously loaded at run time. This was developed because Javascript doesn’t have a clean way to write classes nor manage dependencies. RequireJS is one implementation (and loader) of AMD modules.
The next version of Javascript (ECMAScript 6) attempts to resolve this through a new Module specification. By default anything you declare in a file in a ES6 project is not available outside that file. You have to use the export
keyword to explicitly make it available. This bears repeating: If you attempt to create a global variable using:
var foo = 'bar';
it will be available to the code inside the same *.js
file, but not anywhere else.
This is new technology, and it is not available in browsers yet. However we can use it today with the use of a transpiler that converts ES6 syntax into plain Javascript.
Ember uses jQuery. If you need to call jQuery then use this syntax to access Ember’s jQuery instance:
Ember.$('your selector').method();
Ember will use HTML5 Pushstate for browsers that support it. This let’s you stay on the same page, but change the URL when accessing a different route. Users will be able to bookmark a deep link and get back to the same spot. They only problem here is those routes do not physically exist on your server so you’ll need to use rewrite rules, a fallback resource, or similar function of the web server.
If the browser doesn’t support Pushstate, then hashes are used on the URL (I think).
HTML5 pushstate is awesome. It enables you to change the URL of your site dynamically without refreshing the page (goodbye hashes!). Libraries like Backbone have great support for this. Unfortunately if a user bookmarks or refresh a page on an app that’s using HTML5 pushstate, it makes a request to the server for that deep linked content. Here are the rewrites for Nginx and Apache to internally redirect that call to the same html file. Browser thinks its a unique page but it’s the same.
In your vhost :
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L] </IfModule>
Note: A different blog post suggested this instead:
I found a bunch of examples on the web of Apache rewrite rules that people are using for AJAX-based HTML5-history-using sites. All of them didn't work for me. I don't know why. But I thought it was Ember's fault. It wasn't. So I spun my wheels for a while blaming the router. It wasn't. It's just that there are some strange examples of rewrite rules on the web for AJAX-based HTML5-history-using sites.
Below is what worked for me. I wanted to send every request for URLs to index.html, except for certain subdirectories (js, css, img, svc). If you do this in the root of your Apache config, you might need to specify a forward slash.
Options +FollowSymLinks RewriteEngine On RewriteBase / RewriteRule ^(js|css|img|svc)($|/) - [L] RewriteRule ^(.*)$ index.html [L]
rewrite ^(.+)$ /index.html last;
Note that once you have this in place your server no longer reports 400 errors as all requests pull up the index page. To work around this you can create a 404 in a Backbone route:
routes: { // Other routes "*path" : "notFound" }, notFound: function(path) { // Load 404 template, probably of a cute animal. }
If you are using any modern MVC framework, it should be trivial to simply map all requests to the correct controller\action. I’m doing this on a Rails project right now and I like it because it places the logic in the app instead of in the server and leaves less to worry about when deploying.
Great point, Bryan. I guess I didn’t add real context to this post. These kind of Nginx-level rewrites are really for backend-independent JavaScript application frameworks as most the apps I’ve been writing lately have been straight Backbone.js which harness backend as API calls on the same or different server. The Apache version of the rewrites are for local dev environments as most developers are most familiar with that and already have it installed.
Doing it this way enables me to decouple my backend from my front and allows me to put 95% of the entire application compressed and gzipped on the CDN sittin’ cool as a cucumber. The only servers I need to worry about scaling are the API servers, and I don’t require the overhead of loading an entire backend application stack just to load a static html file.
To target those few pages that require SEO (or Facebook sharing), I also use Nginx to serve seo friendly version when those deep links get hit directly (outside of HTML5 pushsate). I use Node Express which I’m working on getting to understand my Backbone routes and templates directly without much extra coding.
How are Ajax requests for dynamic resources handled, won’t they also be redirected to ‘index.html’? Is there a way to ignore requests that are made from Ajax?
Schmulik, I usually put API calls into its own domain served by the same or different web server, for example api.domain.com. I use CORS to support this but also have a work around domain.com/api/ for browsers that don’t support CORS.
Ember component templates have a corresponding component module (Javascript file). So, if you need to pass a variable to the template, set it in the module first like this:
import Ember from 'ember'; export default Ember.Component.extend({ actions: { editTodo: function() { this.set('isEditing', true); }, acceptChanges: function() { this.set('isEditing', false); this.sendAction('acceptChanges', this.get('todo')); }, deleteTodo: function(todo) { this.sendAction('deleteTodo', todo); } } });
Then to use it in your template, reference the variable directly. e.g.
<li class="{{if todo.isCompleted 'completed'}}"> {{#if isEditing}} {{input type="text" class="edit" value=todo.title focus-out="acceptChanges" insert-newline="acceptChanges"}} {{else}} {{input type="checkbox" checked=todo.isCompleted class="toggle"}} <label {{action "editTodo" on="doubleClick"}}>{{todo.title}}</label><button {{action "deleteTodo" todo}} class="destroy"></button> {{/if}} </li>
Normally your data is stored in a database, but it is possible to use a local data store. Ember calls this Fixture Data.
First create an adapter (see above).
Here’s something you might not know about Ember.js: It doesn’t come default with a data library. It may sound a little strange that a model-view-controller framework doesn’t come prepackaged with what a Rails developer would think of as model. In fact, a lot of the early complaints leveled at Ember (in the days when Ember Data was undergoing significant, break-your-application changes, like, all the time) was that if Data wasn’t ready, what was the point of using Ember at all?
App.UserRoute = Ember.Route.extend({ model: function(params) { return this.store.find(user, params.user_id); } });
That code is using Ember Data. But it doesn’t have to be. See, the misunderstanding is that the model hook needs to return a proper Ember Model object. The truth is, it could return a plain JSON object and Ember would be just as happy. I could just do this…
App.UserRoute = Ember.Route.extend({ model: function(params) { return { first_name: "Eric", last_name : "Sipple" }; } });
…and our Controller gets a Model just the same. I use Ember Data because it provides a way to persist that data, and gives access to a really nice REST adapter, but there are a lot of people who are more than happy to deal with the REST parts on their own, and build a data object model that suits them better than what Ember Data does.
Ember needs data, sure, but it doesn’t need Ember Data.
Still trying to figure this out, but I think Components have replaced both Controllers and Views.
I've not talked about controllers becasue they are being depreceated from Ember so why would you want to learn about them? In short controllers were used to decorate a template, show the temporary state of elements (component) on the page. Temporary state is the state of a element on the page that you don't want to be saved permenantly in your model, for example if we are editing a todo, we'd show an <input>box rather than a <label>. We most probably wouldn't want to save the editing state on our model so that it's premanantly available, therefore it's temporary and that decoration is handled by a controller. In the future of Ember, templates should be a collections of components (elements), and components will look after their own decoration/temporary state so a controller becomes redundant in most cases. Please forget all about controllers.
Views have been replaced with Components in most cases now. Keep that in mind when reading on...
Okay, so if Ember has Views, but doesn’t use them to display anything, what exactly do they do? My misunderstanding of Ember Views stuck around longer than almost anything else in the framework, and I’m still untangling things I did wrong because of it. Views are very easy to use for things they aren’t meant for. Specifically, it’s easy to start putting things in the View that really belong in the Controller, and vice versa.
Luckily, the “What are Views?” question is pretty easy to answer. There are two things you might use them for:
If your users had profile pictures, and you wanted to display those pictures on lots of different pages (y’know, like not just on the user profile page), you might want to set that up as a View. Especially if you wanted that profile picture component to have some event logic behind it. Say you wanted people to be able to click the picture to pop up a larger version of the image. Your View could wait for a click event to hit the picture, then execute whatever logic you want as a result.
Hold up, though. This is where I got turned around and put code in all the wrong places. A View is perfect for catching the browser event, but most of what you want to do as a result would go in the Controller.
Basically, your View catches the click, then sends the event up to the Controller.
App.ProfilePhotoView = Ember.View.extend({ click: function(evt) { this.get('controller').send('expandProfilePhoto'); } }); App.UserController = Ember.ObjectController.extend({ actions: { expandProfilePhoto: function() { // Get the expanded picture and display it // We'll talk more about what to do here later } } });
See that actions property in the UserController? Any methods you define in there can be called by the View with send.
Ember won’t stop you from putting a lot of logic into that click event that really belongs elsewhere, but you’ll save yourself a lot of headaches if you let the Controller handle it.
Do not directly update fields on the page; instead you should bind that field to the controller (or model?) and then update that instead.
The relatively new CSP functionality does not want you to put inline styles on the DOM because it can be a security hole. If you can not change the source code to use, say, classes instead, then add a contentSecurityPolicy property to the /config/environment.js
file.
module.exports = function(environment) { var ENV = { modulePrefix: 'logic-model', environment: environment, baseURL: '/', locationType: 'auto', EmberENV: { FEATURES: { // Here you can enable experimental features on an ember canary build // e.g. 'with-controller': true } }, APP: { // Here you can pass flags/options to your application instance // when it is created }, contentSecurityPolicy: { 'style-src': "'self' 'unsafe-inline'" } };