Ember
Back to Web-Dev/Ember
Using ember in the context as a front end for a rails API
Ember CLI
- Broccoli compiles the project and serves it with Express.js
- Asynchronous Module Definition (AMD) is a specification of Javascript, can be transpiled to from ES6
- Ember CLI expects ES6 Modules
- code is transpiled to AMD, then loaded with loader.js
- Ember orchestrates all of our files, but we design it in a way that we import Ember from ember, and then export whatever the file represents (a model, route, etc...) as the default export (scopes the export into the importing file)
- Ember's resolver returns whatever is required at each stage (templates, routes, services ...)
- DS.JSONAPIAdapter translates a standard JSON API response into a js object
- lots of other adapters and serializers to choose from
Prerequisite
Promise object (js) is the native way of handling asynchronous calculations, and can return a value that may be available now, later or never
- takes a function parameter that handles the outcome of the Promise (whether it be accepted or rejected)
- acts as a proxy to a value that may not be available yet, but promises for a value at some points
- Promise has 3 states: pending, fulfilled, rejected
- fulfilled transitions to an async action and returned value for the promise
Under the Hood
Every Ember application is an extension of Ember.Application
, configurations and declarations of all objects in the given app
- on boot,
Ember.ApplcationInstance
object is instantiated
Object Model
Classes: Ember.Object.extend({})
Ember.computed('...')
Ember.observed('...')
- Bindings
Ember.computed.alias('...')
Routing
router.js
forwards the user for a given URL to their respective Route objects (in app/routes/
) which access their respective models or controllers
- responsible for everything related to application setup
- each route can then load models by defining the model() method in a route, then using
this.store
in controller
- by default renders the template with the same file path as the route, adjusted to
templates/
- follow the conventional rest-URLs for rails resources
- again,
router.js
forwards the user request to app/routes/*
UI Components & Handlebars
Modular UI elements, from generate component <name>
- also use .hbs (handlebars)
- handlebars expression is denoted by
{{}}
- not usually but can be precompiled to HTML directly
- think rails partials
<script id="entry-template" type="text/x-handlebars-template">
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
</div>
</div>
</script>
// then compile template
var source = $("#entry-template").html();
var template = Handlebars.compile(source);
// for pre-compilation
var context = {title: "My New Post", body: "This is my first post!"};
var html = template(context);
Block Expressions allow us to define helpers {{#list people}}{{firstName}} {{lastName}}{{/list}}
- to get this to work, in JS we do
Handlebars.registerHelper('list', function(items, options) {...});
Actions are defined in routes and controllers and act as event handlers from the UI -> Route -> Controller -> Model
-
{{action "actionName" parameters}}
in hbs
{{outlet}}
is synonomous to yield
in erb
, rendering the application template as the main template, and the specific template associated with the route in the {{outlet}} portion
<button {{action "save"}}>
triggers action cancel
on click
- actions is a list, under Ember.Component.extend
- these actions run in the context of components
Components
Component names must have one dash in their name by convention
- components can be hbs files for visuals, or js for action functionality
{{my-component param1=value}}
- components are referenced from templates with
{{friends/edit-form model=model}}
- isolated in scope, unaware of the parent template
- think of as a rendering function (you can pass some environment to the function
Templating Hierachy
{{outlet}}
is used like rails yield
in nested views, for hierarchal templating
- [ application.hbs [ friends.hbs [ friends/index.hbs ] ] ]
Store
Store
is an ember data class, manages everything related to our model's data
- it knows about all the records we have loaded into the application
- has functions for querying them
- for duration of a life cycle, there is a unique instance of the Store, injected into every route controller serializer and adapter under the key store
- why we call
.store
in Routes, to access this unique object
registry.injection()
{{action}}
Useful helper for binding actions in templates to those in components, routes, and controllers
- an input form is good for a component, if they're the same for new/edit, other than the actions for save and cancel
- actions can be passed as arguments to components, passing down from template's context, much like you'd pass the model