Permalink
Browse files

Change {{action}} API for more explicit contexts

Previously, the action helper automatically sent
the current context as the context to the event
it was triggering.

This meant that the router could receive
superfluous contexts that it would try to apply
to a parent state.

Instead of:

  {{action edit context="post"}}

Do:

  {{action edit post}}

If you were relying on the default context,
change:

  {{action edit}}

to:

  {{action edit this}}

This also paves the way for support for multiple
contexts.
  • Loading branch information...
wycats committed Jul 22, 2012
1 parent 396c08b commit 83b7a61a892e55423cf1e66f606b13435bcab8f0
@@ -1,26 +1,35 @@
require('ember-handlebars/ext');
var EmberHandlebars = Ember.Handlebars, getPath = EmberHandlebars.getPath, get = Ember.get;
var EmberHandlebars = Ember.Handlebars,
getPath = EmberHandlebars.getPath,
get = Ember.get,
a_slice = Array.prototype.slice;
var ActionHelper = EmberHandlebars.ActionHelper = {
registeredActions: {}
};
ActionHelper.registerAction = function(actionName, eventName, target, view, context, link) {
ActionHelper.registerAction = function(actionName, options) {
var actionId = (++Ember.$.uuid).toString();
ActionHelper.registeredActions[actionId] = {
eventName: eventName,
eventName: options.eventName,
handler: function(event) {
if (link && (event.button !== 0 || event.shiftKey || event.metaKey || event.altKey || event.ctrlKey)) {
var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey,
rightClick = event.button !== 0,
nonStandard = modifier || rightClick;
if (options.link && nonStandard) {
// Allow the browser to handle special link clicks normally
return;
}
event.preventDefault();
event.view = view;
event.context = context;
event.view = options.view;
event.context = options.context;
var target = options.target;
// Check for StateManager (or compatible object)
if (target.isState && typeof target.send === 'function') {
@@ -32,7 +41,7 @@ ActionHelper.registerAction = function(actionName, eventName, target, view, cont
}
};
view.on('willRerender', function() {
options.view.on('willRerender', function() {
delete ActionHelper.registeredActions[actionId];
});
@@ -169,43 +178,54 @@ ActionHelper.registerAction = function(actionName, eventName, target, view, cont
<script type="text/x-handlebars" data-template-name='a-template'>
{{#each person in people}}
<div {{action "edit" context="person"}}>
<div {{action edit person}}>
click me
</div>
{{/each}}
</script>
@name Handlebars.helpers.action
@param {String} actionName
@param {Object...} contexts
@param {Hash} options
*/
EmberHandlebars.registerHelper('action', function(actionName, options) {
EmberHandlebars.registerHelper('action', function(actionName) {
var options = arguments[arguments.length - 1],
contexts = a_slice.call(arguments, 1, -1);
var hash = options.hash,
eventName = hash.on || "click",
view = options.data.view,
target, context, controller, link;
view = get(view, 'concreteView');
// create a hash to pass along to registerAction
var action = {
eventName: hash.on || "click"
};
action.view = view = get(view, 'concreteView');
if (hash.target) {
target = getPath(this, hash.target, options);
} else if (controller = options.data.keywords.controller) {
target = get(controller, 'target');
}
target = target || view;
action.target = target = target || view;
context = hash.context ? getPath(this, hash.context, options) : options.contexts[0];
// TODO: Support multiple contexts
if (contexts.length) {
action.context = context = getPath(this, contexts[0], options);
}
var output = [], url;
if (hash.href && target.urlForEvent) {
url = target.urlForEvent(actionName, context);
output.push('href="' + url + '"');
link = true;
action.link = true;
}
var actionId = ActionHelper.registerAction(actionName, eventName, target, view, context, link);
var actionId = ActionHelper.registerAction(actionName, action);
output.push('data-ember-action="' + actionId + '"');
return new EmberHandlebars.SafeString(output.join(" "));
@@ -33,8 +33,8 @@ test("should output a data attribute with a guid", function() {
test("should by default register a click event", function() {
var registeredEventName;
ActionHelper.registerAction = function(actionName, eventName) {
registeredEventName = eventName;
ActionHelper.registerAction = function(actionName, options) {
registeredEventName = options.eventName;
};
view = Ember.View.create({
@@ -51,8 +51,8 @@ test("should by default register a click event", function() {
test("should allow alternative events to be handled", function() {
var registeredEventName;
ActionHelper.registerAction = function(actionName, eventName) {
registeredEventName = eventName;
ActionHelper.registerAction = function(actionName, options) {
registeredEventName = options.eventName;
};
view = Ember.View.create({
@@ -69,8 +69,8 @@ test("should allow alternative events to be handled", function() {
test("should by default target the parent view", function() {
var registeredTarget;
ActionHelper.registerAction = function(actionName, eventName, target) {
registeredTarget = target;
ActionHelper.registerAction = function(actionName, options) {
registeredTarget = options.target;
};
view = Ember.View.create({
@@ -110,8 +110,8 @@ test("should by default target the state manager on the controller if it exists"
test("should allow a target to be specified", function() {
var registeredTarget;
ActionHelper.registerAction = function(actionName, eventName, target) {
registeredTarget = target;
ActionHelper.registerAction = function(actionName, options) {
registeredTarget = options.target;
};
var anotherTarget = Ember.View.create();
@@ -341,7 +341,7 @@ test("should send the view, event and current Handlebars context to the action",
view = Ember.View.create({
aContext: aContext,
template: Ember.Handlebars.compile('{{#with aContext}}<a id="edit" href="#" {{action "edit" target="aTarget"}}>edit</a>{{/with}}')
template: Ember.Handlebars.compile('{{#with aContext}}<a id="edit" href="#" {{action edit this target="aTarget"}}>edit</a>{{/with}}')
});
appendView();
@@ -350,7 +350,7 @@ test("should send the view, event and current Handlebars context to the action",
strictEqual(passedTarget, aTarget, "the action is called with the target as this");
strictEqual(passedEvent.view, view, "the view passed is the view containing the action helper");
deepEqual(passedEvent.context, aContext, "the context passed is the context surrounding the action helper");
deepEqual(passedEvent.context, aContext, "the context is passed");
equal(passedEvent.type, 'click', "the event passed is the event triggered for the action helper");
});
@@ -375,7 +375,7 @@ test("should allow a context to be specified", function() {
view = Ember.View.create({
people: Ember.A([model]),
template: Ember.Handlebars.compile('{{#each person in people}}<button {{action "edit" context="person"}}>edit</button>{{/each}}'),
template: Ember.Handlebars.compile('{{#each person in people}}<button {{action edit person}}>edit</button>{{/each}}'),
edit: function(event) {
passedContext = event.context;
}

1 comment on commit 83b7a61

@sly7-7

This comment has been minimized.

Show comment
Hide comment
@sly7-7

sly7-7 Jul 22, 2012

Contributor

There is a PR for for multiple contexts support. #1191

Contributor

sly7-7 commented on 83b7a61 Jul 22, 2012

There is a PR for for multiple contexts support. #1191

Please sign in to comment.