Deprecated w-on* attributes (#434)

Added support for on*() attributes.
This commit is contained in:
Charlie Duong 2016-11-16 13:53:51 -05:00 committed by Michael Rawlings
parent 3eb3c286e8
commit fc82ef0cf0
38 changed files with 122 additions and 81 deletions

View File

@ -264,7 +264,7 @@ And, here is the corresponding Marko template for the UI component:
<div> <div>
You clicked the button ${data.clickCount} ${data.timesMessage}. You clicked the button ${data.clickCount} ${data.timesMessage}.
</div> </div>
<button type="button" w-onclick="handleButtonClick"> <button type="button" onClick("handleButtonClick")>
Click Me Click Me
</button> </button>
</div> </div>

View File

@ -51,6 +51,7 @@ var SequenceExpression = require('./ast/SequenceExpression');
var parseExpression = require('./util/parseExpression'); var parseExpression = require('./util/parseExpression');
var parseStatement = require('./util/parseStatement'); var parseStatement = require('./util/parseStatement');
var parseJavaScriptArgs = require('./util/parseJavaScriptArgs'); var parseJavaScriptArgs = require('./util/parseJavaScriptArgs');
var replacePlaceholderEscapeFuncs = require('./util/replacePlaceholderEscapeFuncs');
var isValidJavaScriptIdentifier = require('./util/isValidJavaScriptIdentifier'); var isValidJavaScriptIdentifier = require('./util/isValidJavaScriptIdentifier');
var DEFAULT_BUILDER; var DEFAULT_BUILDER;
@ -420,6 +421,10 @@ class Builder {
return parsed; return parsed;
} }
replacePlaceholderEscapeFuncs(node, context) {
return replacePlaceholderEscapeFuncs(node, context);
}
program(body) { program(body) {
return new Program({body}); return new Program({body});
} }

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
var ok = require('assert').ok; var ok = require('assert').ok;
var AttributePlaceholder = require('./ast/AttributePlaceholder'); var replacePlaceholderEscapeFuncs = require('./util/replacePlaceholderEscapeFuncs');
var COMPILER_ATTRIBUTE_HANDLERS = { var COMPILER_ATTRIBUTE_HANDLERS = {
'preserve-whitespace': function(attr, context) { 'preserve-whitespace': function(attr, context) {
@ -17,24 +17,6 @@ function isIEConditionalComment(comment) {
return ieConditionalCommentRegExp.test(comment); return ieConditionalCommentRegExp.test(comment);
} }
function replacePlaceholderEscapeFuncs(node, context) {
var walker = context.createWalker({
exit: function(node, parent) {
if (node.type === 'FunctionCall' &&
node.callee.type === 'Identifier') {
if (node.callee.name === '$noEscapeXml') {
return new AttributePlaceholder({escape: false, value: node.args[0]});
} else if (node.callee.name === '$escapeXml') {
return new AttributePlaceholder({escape: true, value: node.args[0]});
}
}
}
});
return walker.walk(node);
}
function mergeShorthandClassNames(el, shorthandClassNames, context) { function mergeShorthandClassNames(el, shorthandClassNames, context) {
var builder = context.builder; var builder = context.builder;
let classNames = shorthandClassNames.map((className) => { let classNames = shorthandClassNames.map((className) => {

View File

@ -0,0 +1,19 @@
var AttributePlaceholder = require('../ast/AttributePlaceholder');
module.exports = function replacePlaceholderEscapeFuncs(node, context) {
var walker = context.createWalker({
exit: function(node, parent) {
if (node.type === 'FunctionCall' &&
node.callee.type === 'Identifier') {
if (node.callee.name === '$noEscapeXml') {
return new AttributePlaceholder({escape: false, value: node.args[0]});
} else if (node.callee.name === '$escapeXml') {
return new AttributePlaceholder({escape: true, value: node.args[0]});
}
}
}
});
return walker.walk(node);
};

View File

@ -308,8 +308,8 @@ Listeners can be attached declaratively as shown in the following sample code:
```xml ```xml
<div w-bind> <div w-bind>
<form w-onsubmit="handleFormSubmit"> <form onsubmit("handleFormSubmit")>
<input type="text" value="email" w-onchange="handleEmailChange"> <input type="text" value="email" onchange("handleEmailChange")>
<button>Submit</button> <button>Submit</button>
</form> </form>
</div> </div>
@ -406,7 +406,7 @@ Listeners can be attached declaratively as shown in the following sample code:
```xml ```xml
<div w-bind="./widget"> <div w-bind="./widget">
<app-overlay title="My Overlay" <app-overlay title="My Overlay"
w-onBeforeHide="handleOverlayBeforeHide"> onBeforeHide("handleOverlayBeforeHide")>
Content for overlay Content for overlay
@ -699,7 +699,7 @@ ___src/components/app-hello/template.marko:___
```xml ```xml
<div w-bind <div w-bind
w-on-click="handleClick"> on-click("handleClick")>
Hello ${data.name}! Hello ${data.name}!
</div> </div>
``` ```
@ -736,7 +736,7 @@ ___src/components/app-hello/template.marko:___
```xml ```xml
<div w-bind="./widget" <div w-bind="./widget"
w-on-click="handleClick"> on-click("handleClick")>
Hello ${data.name}! Hello ${data.name}!
</div> </div>
``` ```

View File

@ -113,7 +113,7 @@ __src/components/app-hello/template.marko__
```xml ```xml
<div w-bind <div w-bind
w-onClick="handleClick"> onClick("handleClick")>
Hello ${data.name}! Hello ${data.name}!
@ -154,7 +154,7 @@ __src/components/app-hello/template.marko__
```xml ```xml
<div w-bind <div w-bind
w-onClick="handleClick" onClick("handleClick")
style="background-color: ${data.color}"> style="background-color: ${data.color}">
Hello ${data.name}! Hello ${data.name}!
@ -202,7 +202,7 @@ __src/components/app-hello/template.marko__
```xml ```xml
<div w-bind <div w-bind
w-onClick="handleClick" onClick("handleClick")
style="background-color: ${data.color}"> style="background-color: ${data.color}">
Hello ${data.name}! Hello ${data.name}!
@ -258,17 +258,17 @@ module.exports = require('marko-widgets').defineComponent({
<div w-bind> <div w-bind>
<app-overlay title="My Overlay" <app-overlay title="My Overlay"
ref="overlay" ref="overlay"
w-onBeforeHide="handleOverlayBeforeHide"> onBeforeHide("handleOverlayBeforeHide")>
Body content for overlay. Body content for overlay.
</app-overlay> </app-overlay>
<button type="button" <button type="button"
w-onClick="handleShowButtonClick"> onClick("handleShowButtonClick")>
Show Overlay Show Overlay
</button> </button>
<button type="button" <button type="button"
w-onClick="handleHideButtonClick"> onClick("handleHideButtonClick")>
Hide Overlay Hide Overlay
</button> </button>
</div> </div>

View File

@ -85,7 +85,7 @@ NOTE: For DOM events that bubble, efficient DOM event delegation will automatica
```xml ```xml
<div w-bind="./widget"> <div w-bind="./widget">
<button w-onclick="handleMyButtonClick" type="button">My Button</button> <button onClick("handleMyButtonClick") type="button">My Button</button>
</div> </div>
``` ```
@ -113,7 +113,7 @@ var myButton = this.getEl('myButton');
```xml ```xml
<div w-bind="./widget"> <div w-bind="./widget">
<app-button w-onSomeCustomEvent="handleSomeCustomEvent" label="My Button" /> <app-button onSomeCustomEvent("handleSomeCustomEvent") label="My Button" />
</div> </div>
``` ```

View File

@ -28,5 +28,6 @@
"no-update-if", "no-update-if",
"no-update-body-if", "no-update-body-if",
"w-preserve-attrs", "w-preserve-attrs",
"on*",
"w-on*" "w-on*"
] ]

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -5,8 +5,8 @@
<app-bar ref="barArray[]" label="1"/> <app-bar ref="barArray[]" label="1"/>
<app-bar ref="barArray[]" label="2"/> <app-bar ref="barArray[]" label="2"/>
<app-custom-events w-onTestEvent="handleTestEvent1" ref="customEvents" /> <app-custom-events onTestEvent("handleTestEvent1") ref="customEvents" />
<app-custom-events w-onTestEvent="handleTestEvent2" channel="customEvents-${widget.id}" /> <app-custom-events onTestEvent("handleTestEvent2") channel="customEvents-${widget.id}" />
<ul> <ul>
<li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li> <li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li>

View File

@ -5,8 +5,8 @@
<app-bar ref="barArray[]" label="1"/> <app-bar ref="barArray[]" label="1"/>
<app-bar ref="barArray[]" label="2"/> <app-bar ref="barArray[]" label="2"/>
<app-custom-events w-onTestEvent="handleTestEvent1" ref="customEvents" /> <app-custom-events onTestEvent("handleTestEvent1") ref="customEvents" />
<app-custom-events w-onTestEvent="handleTestEvent2" channel="customEvents-${widget.id}" /> <app-custom-events onTestEvent("handleTestEvent2") channel="customEvents-${widget.id}" />
<ul> <ul>
<li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li> <li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li>

View File

@ -5,8 +5,8 @@
<app-bar ref="barArray[]" label="1"/> <app-bar ref="barArray[]" label="1"/>
<app-bar ref="barArray[]" label="2"/> <app-bar ref="barArray[]" label="2"/>
<app-custom-events w-onTestEvent="handleTestEvent1" ref="customEvents" /> <app-custom-events onTestEvent("handleTestEvent1") ref="customEvents" />
<app-custom-events w-onTestEvent="handleTestEvent2" channel="customEvents-${widget.id}" /> <app-custom-events onTestEvent("handleTestEvent2") channel="customEvents-${widget.id}" />
<ul> <ul>
<li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li> <li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li>

View File

@ -1,5 +1,5 @@
<button type="button" class="app-legacy-button" <button type="button" class="app-legacy-button"
w-bind="./widget" w-bind="./widget"
w-onmousedown="handleRootMouseDown"> onMousedown("handleRootMouseDown")>
<invoke data.renderBody(out)/> <invoke data.renderBody(out)/>
</button> </button>

View File

@ -1,6 +1,6 @@
<div w-bind> <div w-bind>
<a href="#foo" ref="fooLink" <a href="#foo" ref="fooLink"
w-ondblclick="handleFooLinkDblClick" onDblclick("handleFooLinkDblClick")
w-onmouseout="handleFooLinkMouseOut"> onMouseout("handleFooLinkMouseOut")>
</a> </a>
</div> </div>

View File

@ -1,6 +1,6 @@
<div w-bind> <div w-bind>
<ul> <ul>
<li w-onMouseMove="handleMouseMove" ref="foo-${loop.getIndex()}" <li onMouseMove("handleMouseMove") ref="foo-${loop.getIndex()}"
for(color in ['red', 'green', 'blue'] | status-var=loop)>${color}</li> for(color in ['red', 'green', 'blue'] | status-var=loop)>${color}</li>
</ul> </ul>
</div> </div>

View File

@ -1,5 +1,5 @@
<button type="button" class="app-legacy-button" <button type="button" class="app-legacy-button"
w-bind="./widget" w-bind="./widget"
w-onmousedown="handleRootMouseDown"> onMousedown("handleRootMouseDown")>
<invoke data.renderBody(out)/> <invoke data.renderBody(out)/>
</button> </button>

View File

@ -1,20 +1,20 @@
<div class="app-dom-events" <div class="app-dom-events"
w-bind="./widget" w-bind="./widget"
w-onmousemove="handleRootMouseMove" onMousemove("handleRootMouseMove")
w-onClick="handleRootClick"> onClick("handleRootClick")>
<button type="button" ref="button" <button type="button" ref="button"
w-onclick="handleButtonClick"> onClick("handleButtonClick")>
<span w-onmousemove="handleButtonSpanMouseMove"> <span onMousemove("handleButtonSpanMouseMove")>
Button Button
</span> </span>
</button> </button>
<a href="#foo" id="fooLink" <a href="#foo" id="fooLink"
w-ondblclick="handleFooLinkDblClick" onDblclick("handleFooLinkDblClick")
w-onmouseout="handleFooLinkMouseOut"> onMouseout("handleFooLinkMouseOut")>
</a> </a>
<app-legacy-button ref="appButton"> <app-legacy-button ref="appButton">
<span ref="helloWorld" w-onmousedown="handleHelloWorldMouseDown">Hello World</span> <span ref="helloWorld" onMousedown("handleHelloWorldMouseDown")>Hello World</span>
</app-legacy-button> </app-legacy-button>
</div> </div>

View File

@ -1,5 +1,5 @@
<div w-bind> <div w-bind>
<input w-onclick=(false && 'handleButtonClick') ref="inputWithoutHandler" type="button"> <input onClick(false && 'handleButtonClick') ref="inputWithoutHandler" type="button">
<input w-onclick=(true && 'handleButtonClick') ref="inputWithHandler" type="button"> <input onClick(true && 'handleButtonClick') ref="inputWithHandler" type="button">
<input w-onclick='handleButtonClick' ref="inputWithLiteralHandler" type="button"> <input onClick('handleButtonClick') ref="inputWithLiteralHandler" type="button">
</div> </div>

View File

@ -1,5 +1,5 @@
<div w-bind> <div w-bind>
<input w-onMouseMove=(false && "handleMouseMove") ref="inputWithoutHandler" type="button"> <input onMouseMove(false && "handleMouseMove") ref="inputWithoutHandler" type="button">
<input w-onMouseMove=(true && 'handleMouseMove') ref="inputWithHandler" type="button"> <input onMouseMove(true && 'handleMouseMove') ref="inputWithHandler" type="button">
<input w-onMouseMove='handleMouseMove' ref="inputWithLiteralHandler" type="button"> <input onMouseMove('handleMouseMove') ref="inputWithLiteralHandler" type="button">
</div> </div>

View File

@ -1,7 +1,7 @@
<div w-bind> <div w-bind>
<h1>app-macro-events</h1> <h1>app-macro-events</h1>
<macro renderButton(id, label, handler)> <macro renderButton(id, label, handler)>
<button ref="${id}" type="button" w-onClick="${handler}"> <button ref="${id}" type="button" onClick("${handler}")>
$label $label
</button> </button>
</macro> </macro>

View File

@ -1,5 +1,5 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind="./widget"> w-bind="./widget">
<if(data.label)> <if(data.label)>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include></button> include></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
include()></button> include()></button>

View File

@ -1,7 +1,7 @@
<div w-bind> <div w-bind>
<div w-onclick="handleDivClick"> <div onClick("handleDivClick")>
Hello Hello
<button ref="button" type="button" w-onclick="handleButtonClick"> <button ref="button" type="button" onClick("handleButtonClick")>
Button Button
</button> </button>
</div> </div>

View File

@ -1,2 +1,2 @@
div w-bind div w-bind
button ref="button" w-onMouseMove='handleButtonMouseMove' w-onClick="handleButtonClick" button ref="button" onMouseMove('handleButtonMouseMove') onClick("handleButtonClick")

View File

@ -1,4 +1,4 @@
<button ${data.rootAttrs} <button ${data.rootAttrs}
w-onclick="handleClick" onClick("handleClick")
w-bind w-bind
w-body></button> w-body></button>

View File

@ -48,7 +48,7 @@ var attachBubbleEventListeners = function() {
var target; var target;
// Attributes will have the following form: // Attributes will have the following form:
// w-on<event_type>="<target_method>|<widget_id>" // on<event_type>("<target_method>|<widget_id>")
do { do {
if ((target = getEventAttribute(curNode, attrName))) { if ((target = getEventAttribute(curNode, attrName))) {

View File

@ -89,6 +89,7 @@ function addCustomEventListener(transformHelper, eventType, targetMethod) {
module.exports = function handleWidgetEvents() { module.exports = function handleWidgetEvents() {
var el = this.el; var el = this.el;
var builder = this.builder; var builder = this.builder;
var context = this.context;
var isCustomTag = el.type !== 'HtmlElement'; var isCustomTag = el.type !== 'HtmlElement';
// We configured the Marko compiler to attach a flag to nodes that // We configured the Marko compiler to attach a flag to nodes that
// have one or more attributes that match the "w-on*" pattern. // have one or more attributes that match the "w-on*" pattern.
@ -100,16 +101,35 @@ module.exports = function handleWidgetEvents() {
var attrs = el.getAttributes().concat([]); var attrs = el.getAttributes().concat([]);
attrs.forEach((attr) => { attrs.forEach((attr) => {
var eventType;
var targetMethod;
var attrName = attr.name; var attrName = attr.name;
if (!attrName || !attrName.startsWith('w-on')) { var args = attr.argument;
if (!attrName) {
return;
}
if (attrName.startsWith('on') && args) {
eventType = attrName.substring(2); // Chop off "on"
try {
var parsedArgs = builder.parseExpression(args);
targetMethod = builder.replacePlaceholderEscapeFuncs(parsedArgs, context);
} catch (err) {
this.addError('Invalid Javascript Expression for "' + attrName + '": ' + err);
}
} else if (attrName.startsWith('w-on')) {
console.warn('"w-on*" attributes are deprecated. Please use "on*()" instead. (' + (el.pos ? context.getPosInfo(el.pos) : context.filename) + ')');
eventType = attrName.substring(4); // Chop off "w-on"
targetMethod = attr.value;
}
if (!eventType || !targetMethod) {
return; return;
} }
el.removeAttribute(attrName); el.removeAttribute(attrName);
var eventType = attrName.substring(4); // Chop off "w-on"
var targetMethod = attr.value;
if (isCustomTag) { if (isCustomTag) {
var widgetArgs = this.getWidgetArgs(); var widgetArgs = this.getWidgetArgs();
if (widgetArgs.getId() == null) { if (widgetArgs.getId() == null) {

View File

@ -64,6 +64,20 @@
} }
] ]
}, },
"@on*": {
"pattern": true,
"type": "statement",
"allow-expressions": true,
"preserve-name": true,
"set-flag": "hasWidgetEvents",
"autocomplete": [
{
"displayText": "on<event>(\"<method>\")",
"snippet": "on${1:Click}(\"handle${2:Button}${1:Click}\")",
"descriptionMoreURL": "http://markojs.com/docs/marko-widgets/get-started/#adding-dom-event-listeners"
}
]
},
"@w-on*": { "@w-on*": {
"pattern": true, "pattern": true,
"type": "string", "type": "string",