-
-
Save lancejpollard/3314813 to your computer and use it in GitHub Desktop.
# Random helper method to force Ember to render (I think this is it, comes in handy) | |
Ember.fast = -> | |
Ember.run.autorun() | |
Ember.run.currentRunLoop.flush('render') |
# app/client/views/users/new.coffee | |
App.UserNewView = Ember.View.extend(templateName: 'new') |
# config/routes.coffee | |
Tower.Route.draw -> | |
@resources 'users' | |
@resources 'posts' | |
# The `resources` method creates 7 routes mapping to 7 controller actions. | |
# It creates those routes with the `match` method. | |
# The `match` method on the client is overridden to include this: | |
# `Tower.router.insertRoute(route)` | |
# That then builds the state tree on the router here: | |
# https://github.com/viatropos/tower/blob/402f19595f0d2fe1f85c973fead7d76fd682e820/packages/tower-net/client/states.coffee#L68 | |
# Then see the `Ember.Route.create` in states.coffee. | |
# It delegates `enter`, `exit`, and `connectOutlets` to the controller. | |
# The controller calls `enter` when it first enters, | |
# and `enterAction` when it switches between actions. Same with exit/exitAction. | |
# The `route.connectOutlets` calls `controller.call`, which actually calls | |
# the action on the controller (and runs the before/after callbacks like normal). | |
# The last part is calling `@render 'someAction'` from controller action method, | |
# which gets expanded into the `viewClass` and figures out the outlet to render this | |
# action view into. That's the part I'm finishing now. | |
# Currently it's hacked in partly here: | |
# https://github.com/viatropos/tower/blob/402f19595f0d2fe1f85c973fead7d76fd682e820/packages/tower-view/shared/rendering.coffee#L15 | |
# and here: | |
# https://github.com/viatropos/tower/blob/402f19595f0d2fe1f85c973fead7d76fd682e820/packages/tower-view/client/emberHelper.coffee#L22 | |
# The other last part is the Watchfile task that builds the templates.js | |
# | |
# From the developers perspective, for a resource like "users", | |
# all you do is write templates, single-line routes, | |
# and a controller with the actions that call `@render action`. | |
# You can pass in an `@render 'show', outlet: 'popup'` too, but the simple case is 1 outlet. | |
# All of this will be implemented as defaults so you don't have to write any actions on the controller. | |
# | |
# Then in the templates, you'd have an {{action "destroy"}} do destroy a record, | |
# but this hasn't been implemented yet. |
# app/client/views/users/show.coffee | |
App.UserShowView = Ember.View.extend(templateName: 'show') |
# See the Watchfile method below, which generates the templates.js that basically looks like this: | |
Ember.TEMPLATES['new'] = Ember.computed(-> Ember.Handlebars.compile('new user!')) | |
Ember.TEMPLATES['show'] = Ember.computed(-> Ember.Handlebars.compile('show user!')) | |
# The ideal/optimized way of compiling the ember templates is not fully fleshed out yet, but using computed properties like this basically lazily compiles them, which is good enough for now. |
// this is what's actually generated, the templates.coffee is for human readability :) | |
// will clean this up over time | |
Tower.View.cache = { | |
'app/views/users/show': Ember.computed(function() { return Ember.Handlebars.compile('show!'); }) | |
}; | |
_.extend(Ember.TEMPLATES, Tower.View.cache); |
class App.UsersController extends Tower.Controller | |
new: -> | |
@render 'new' | |
show: -> | |
@render 'show' # Ember.TEMPLATES['app/views/users/show'] |
coffeecup = require('coffeecup') | |
watch /app\/views.*\.coffee$/ | |
update: (path, callback) -> | |
try | |
nodes = path.replace("app/views/", "").split("/") | |
name = {} | |
data = File.read(path) | |
id = nodes.join("/") | |
selector = id | |
name = "" | |
#@broadcast body: data, id: id, selector: selector, path: "/#{name}" | |
files = File.files("app/views") | |
result = [] | |
for file in files | |
continue unless file.match(/app\/views\/admin\/(index|tiles.*|categories.*).coffee$/) | |
continue unless file.match(/\.coffee$/) | |
result.push [file.replace(/\.coffee$/, ""), File.read(file)] | |
template = "Tower.View.cache =\n" | |
# this is different from before | |
iterator = (item, next) => | |
template += " '#{item[0]}': Ember.Handlebars.compile('" | |
# make it render to HTML for ember | |
template += "#{coffeecup.render(item[1])}')\n" | |
next() | |
async.forEachSeries result, iterator, (error) => | |
template += '_.extend(Ember.TEMPLATES, Tower.View.cache)\n' | |
mint.coffee template, bare: true, (error, string) => | |
if error | |
console.log error | |
return callback(error) | |
else | |
File.write "public/javascripts/templates.js", string | |
callback() | |
catch error | |
callback(error) | |
client: | |
update: (data) -> | |
Tower.View.cache ||= {} | |
Tower.View.cache[data.id] = Ember.TEMPLATES[data.id] = data | |
### | |
if data.reload | |
window.location = data.path | |
else | |
Tower.get data.path | |
### |
Tower.router.transitionTo('users.new', optionalParams)
For menu navigation, you want to link to controller actions, and maybe have the menu items be highlighted when active. There's booleans on the controller for when the controller is active, controller.isActive
, and when each action is active, isIndexActive
, isXActive
(maybe useful in sidebars):
ul class: 'nav', ->
li ->
a '{{action index target="App.usersController"}} {{bindAttr class="isActive" target="App.usersController"}}', 'Users'
li ->
a '{{action index target="App.postsController"}} {{bindAttr class="isActive" target="App.postsController"}}', 'Posts'
# you may be able to use `with` here to make it easier to read, haven't tried though.
ul class: 'nav', ->
li ->
text '{{with App.usersController}}'
a '{{action index}} {{bindAttr class="isActive"}}', 'Users'
text '{{/with}}'
# ...or the `with` helper
ul class: 'nav', ->
li ->
hWith 'App.usersController', ->
a '{{action index}} {{bindAttr class="isActive"}}', 'Users'
I wonder what would be the relation between controller and router. In ember -- these are conceptually different things. Here in tower you seems to "merge" controller and router in a more traditional MVC approach. Is that correct observation or you still plan to introduce a separate routing concept? (as I understand, Tower.router is wrapper around ember router, and supposed to be used only internally -- or it can be extended, e.g. by handling "enter" event in a custom way?).
To change the url, use:
I haven't dug in further yet to make a better way.