Introduce "ref" and "for-ref" attributes

Deprecated "w-id" and "w-for"
This commit is contained in:
Charlie Duong 2016-11-13 14:56:46 -05:00
parent 00b52ae5be
commit e1940a7ff0
70 changed files with 207 additions and 158 deletions

View File

@ -685,7 +685,7 @@ Or, since `widget.js` is automatically recognized
</div>
```
### Deprecate `w-id` and `w-for` in favor of `ref` and `ref-for` ([#394](https://github.com/marko-js/marko/issues/394))
### Deprecate `w-id` and `w-for` in favor of `ref` and `for-ref` ([#394](https://github.com/marko-js/marko/issues/394))
The `w-id` attribute was used to obtain references using `this.getEl(refId)`. `w-id` has been replaced with the `ref` attribute:

View File

@ -59,7 +59,7 @@ Marko Widgets starts a batch when handling a bubbling a DOM event that came off
# How do widgets communicate?
Every widget is an [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance and widgets typically communicate by emitting custom events that can then be handled by the direct parent of the widget. The parent widget can choose to handle the event or emit another custom event that bubbles up to its parent. A widget should only communicate directly with nested widgets that it "owns" (i.e., nested widgets that were introduced in the containing widget's template). A widget can get a direct reference to a nested widget using the `this.getWidget(nestedWidgetId)` method (where `nestedWidgetId` is the ID assigned using the `w-id` attribute).
Every widget is an [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance and widgets typically communicate by emitting custom events that can then be handled by the direct parent of the widget. The parent widget can choose to handle the event or emit another custom event that bubbles up to its parent. A widget should only communicate directly with nested widgets that it "owns" (i.e., nested widgets that were introduced in the containing widget's template). A widget can get a direct reference to a nested widget using the `this.getWidget(nestedWidgetId)` method (where `nestedWidgetId` is the ID assigned using the `ref` attribute).
In some situations, it may be helpful to communicate an event on a global pub/sub channel. Pub/sub can be helpful in situations where it would overkill for an event to have to bubble up a complex widget hierarchy in order for it to be handled. The [raptor-pubsub](https://github.com/raptorjs/raptor-pubsub) module provides a very simple pub/sub implementation based on the [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) API.

View File

@ -195,9 +195,9 @@ The following HTML template fragment contains a widget that has three nested [sa
```xml
<div class="my-component" w-bind="./widget">
<div class="btn-group">
<sample-button label="Click Me" variant="primary" w-id="primaryButton"/>
<sample-button label="Click Me" variant="success" w-id="successButton"/>
<sample-button label="Click Me" variant="danger" w-id="dangerButton"/>
<sample-button label="Click Me" variant="primary" ref="primaryButton"/>
<sample-button label="Click Me" variant="success" ref="successButton"/>
<sample-button label="Click Me" variant="danger" ref="dangerButton"/>
</div>
...
</div>
@ -217,7 +217,7 @@ Marko Widgets also supports referencing _repeated_ nested widgets as shown below
<div class="my-component" w-bind="./widget">
<ul>
<li for="todoItem in data.todoItems">
<app-todo-item w-id="todoItems[]" todo-item="todoItem"/>
<app-todo-item ref="todoItems[]" todo-item="todoItem"/>
</li>
</ul>
</div>
@ -234,13 +234,13 @@ To try out and experiment with this code please see the documentation and source
## Referencing Nested DOM Elements
DOM elements nested within a widget can be given unique IDs based on the containing widget's ID. These DOM elements can then be efficiently looked up by the containing widget using methods provided. The `w-id` custom attribute can be used to assign DOM element IDs to HTML elements that are prefixed with the widget's ID. For example, given the following HTML template fragment:
DOM elements nested within a widget can be given unique IDs based on the containing widget's ID. These DOM elements can then be efficiently looked up by the containing widget using methods provided. The `ref` custom attribute can be used to assign DOM element IDs to HTML elements that are prefixed with the widget's ID. For example, given the following HTML template fragment:
```xml
<form w-bind="./widget">
...
<button type="submit" w-id="submitButton">Submit</button>
<button type="button" w-id="cancelButton">Cancel</button>
<button type="submit" ref="submitButton">Submit</button>
<button type="button" ref="cancelButton">Cancel</button>
</form>
```
@ -283,7 +283,7 @@ Marko Widgets also supports referencing _repeated_ nested DOM elements as shown
```xml
<ul>
<li for="color in ['red', 'green', 'blue']"
w-id="colorListItems[]">
ref="colorListItems[]">
$color
</li>
</ul>
@ -347,12 +347,12 @@ NOTE: Event handler methods will be invoked with `this` being the widget instanc
For performance reasons, Marko Widgets only adds one event listener to the root `document.body` element for each event type that bubbles. When Marko Widgets captures an event on `document.body` it will internally delegate the event to the appropriate widgets. For DOM events that do not bubble, Marko Widgets will automatically add DOM event listeners to each of the DOM nodes. If a widget is destroyed, Marko Widgets will automatically do the appropriate cleanup to remove DOM event listeners.
You can also choose to add listeners in JavaScript code by assigning an "element id" to the nested DOM element (only needs to be unique within the scope of the containing widget) so that the nested DOM element can be referenced by the containing widget. The scoped widget element ID should be assigned using the `w-id="<id>"` attribute. For example, in the template:
You can also choose to add listeners in JavaScript code by assigning an "element id" to the nested DOM element (only needs to be unique within the scope of the containing widget) so that the nested DOM element can be referenced by the containing widget. The scoped widget element ID should be assigned using the `ref="<id>"` attribute. For example, in the template:
```xml
<div w-bind>
<form w-id="form">
<input type="text" value="email" w-id="email">
<form ref="form">
<input type="text" value="email" ref="email">
<button>Submit</button>
</form>
</div>
@ -426,12 +426,12 @@ module.exports = require('marko-widgets').defineComponent({
});
```
You can also choose to add listeners in JavaScript code by assigning an "id" to the nested widget (only needs to be unique within the scope of the containing widget) so that the nested widget can be referenced by the containing widget. The scoped widget ID should be assigned using the `w-id="<id>"` attribute. For example, in the template:
You can also choose to add listeners in JavaScript code by assigning an "id" to the nested widget (only needs to be unique within the scope of the containing widget) so that the nested widget can be referenced by the containing widget. The scoped widget ID should be assigned using the `ref="<id>"` attribute. For example, in the template:
```xml
<div w-bind="./widget">
<app-overlay title="My Overlay"
w-id="myOverlay">
ref="myOverlay">
Content for overlay

View File

@ -257,7 +257,7 @@ module.exports = require('marko-widgets').defineComponent({
```xml
<div w-bind>
<app-overlay title="My Overlay"
w-id="overlay"
ref="overlay"
w-onBeforeHide="handleOverlayBeforeHide">
Body content for overlay.
</app-overlay>

View File

@ -21,18 +21,18 @@ Bind to a JavaScript module named `./widget.js` or `./index.js` (searched for in
<div w-bind>...</div>
```
## w-id
## ref
Used to assign a _scoped_ ID to a nested widget or a nested DOM element. The ID will be a concatenation of the parent widget ID with the provided value of the `w-id`.
Used to assign a _scoped_ ID to a nested widget or a nested DOM element. The ID will be a concatenation of the parent widget ID with the provided value of the `ref`.
### Examples
#### Using `w-id` with an HTML element
#### Using `ref` with an HTML element
```xml
<div w-bind="./widget">
<button w-id="myButton" type="button">My Button</button>
<button ref="myButton" type="button">My Button</button>
</div>
```
@ -51,11 +51,11 @@ The containing widget can reference the nested DOM element using the following c
var myButton = this.getEl('myButton');
```
#### Using `w-id` with a nested widget
#### Using `ref` with a nested widget
```xml
<div w-bind="./widget">
<app-button w-id="myButton" label="My Button" />
<app-button ref="myButton" label="My Button" />
</div>
```
@ -196,10 +196,10 @@ The `w-for` attribute is used to render a `for` attribute that references a scop
```xml
<form>
<label w-for="yes">Yes</label>
<input type="radio" w-id="yes" value="yes">
<input type="radio" ref="yes" value="yes">
<label w-for="no">No</label>
<input type="radio" w-id="no" value="no">
<input type="radio" ref="no" value="no">
</form>
```
@ -208,7 +208,7 @@ This will produce code similar to the following:
```html
<form>
<label for="w0-yes">Yes</label>
<input type="radio" w-id="w0-yes" value="yes">
<input type="radio" ref="w0-yes" value="yes">
<label for="w0-no">No</label>
<input type="radio" id="w0-no" value="no">

View File

@ -15,7 +15,7 @@ Upgrade Guide
- Integrated [morphdom](https://github.com/patrick-steele-idem/morphdom) to more efficiently transform the existing DOM instead of replacing it entirely
- Significant performance improvements
- Code cleanup
- Stable IDs for widgets that are not assigned a `w-id`
- Stable IDs for widgets that are not assigned a `ref`
- New lifecycle event: 'onRender'
## v3 to v4
@ -176,4 +176,4 @@ _New renderer.js:_
exports.renderer = function(input, out) {
// ...
}
```
```

View File

@ -14,6 +14,8 @@
"w-scope",
"w-extend",
"w-config",
"for-ref",
"ref",
"w-for",
"w-id",
"w-body",
@ -23,4 +25,4 @@
"w-preserve-body-if",
"w-preserve-attrs",
"w-on*"
]
]

View File

@ -1,6 +1,6 @@
<div w-bind>
<div.unpreserved-counter>${data.counter}</div>
<span w-id="preserve" data-counter=data.counter w-preserve-body>
<span ref="preserve" data-counter=data.counter w-preserve-body>
<div.preserved-counter>${data.counter}</div>
</span>
</div>

View File

@ -1,6 +1,6 @@
<div w-bind>
<div.unpreserved-counter>${data.counter}</div>
<span w-id="preserve" data-counter=data.counter w-preserve>
<span ref="preserve" data-counter=data.counter w-preserve>
<div.preserved-counter>${data.counter}</div>
</span>
</div>

View File

@ -1,3 +1,3 @@
<div w-bind>
Hello <span w-id="name">${data.name}</span>!
Hello <span ref="name">${data.name}</span>!
</div>

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<div w-bind>
<app-legacy-button size="small" w-id="button1">button1</app-legacy-button>
<app-legacy-button size="normal" w-id="button2">button2</app-legacy-button>
<app-legacy-button size="small" ref="button1">button1</app-legacy-button>
<app-legacy-button size="normal" ref="button2">button2</app-legacy-button>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
<app-legacy-button size="small" w-id="button1">button1</app-legacy-button>
<app-legacy-button size="normal" w-id="button2">button2</app-legacy-button>
<app-legacy-button size="small" ref="button1">button1</app-legacy-button>
<app-legacy-button size="normal" ref="button2">button2</app-legacy-button>
</div>

View File

@ -1,5 +1,5 @@
<div w-bind>
<a href="#foo" w-id="fooLink"
<a href="#foo" ref="fooLink"
w-ondblclick="handleFooLinkDblClick"
w-onmouseout="handleFooLinkMouseOut">
</a>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,4 +1,4 @@
<div class="app-discard" w-bind>
[app-discard]
<app-simple name="John" message-count=50 w-id="simple" if(data.showSimple)/>
<app-simple name="John" message-count=50 ref="simple" if(data.showSimple)/>
</div>

View File

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

View File

@ -1,2 +1,2 @@
div class="app-dom-events-jquery" w-bind="./widget"
button type="button" w-id="button" - Button
button type="button" ref="button" - Button

View File

@ -2,7 +2,7 @@
w-bind="./widget"
w-onmousemove="handleRootMouseMove"
w-onClick="handleRootClick">
<button type="button" w-id="button"
<button type="button" ref="button"
w-onclick="handleButtonClick">
<span w-onmousemove="handleButtonSpanMouseMove">
Button
@ -14,7 +14,7 @@
w-onmouseout="handleFooLinkMouseOut">
</a>
<app-legacy-button w-id="appButton">
<span w-id="helloWorld" w-onmousedown="handleHelloWorldMouseDown">Hello World</span>
<app-legacy-button ref="appButton">
<span ref="helloWorld" w-onmousedown="handleHelloWorldMouseDown">Hello World</span>
</app-legacy-button>
</div>

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
w-bind="./widget">
<if(data.label)>
<span w-id="label">${data.label}</span>
<span ref="label">${data.label}</span>
</if>
<invoke data.renderBody(out, widget)
else-if(data.renderBody)/>

View File

@ -2,6 +2,6 @@
name=data.name
w-extend="./widget">
<span class="app-checkbox-icon"/><span w-id="label">${data.label}</span>
<span class="app-checkbox-icon"/><span ref="label">${data.label}</span>
</app-extend-button>

View File

@ -1,4 +1,4 @@
div w-bind
span.foo w-id="foo"
span.bar w-id="bar"
span.foo-bar w-id="foo-bar"
span.foo ref="foo"
span.bar ref="bar"
span.foo-bar ref="foo-bar"

View File

@ -1,4 +1,4 @@
div w-bind
<ul>
<li for(color in ['red', 'green', 'blue']) w-id="colorListItems[]">${color}</li>
<li for(color in ['red', 'green', 'blue']) ref="colorListItems[]">${color}</li>
</ul>

View File

@ -1,2 +1,2 @@
div w-bind
app-foo w-id="bar"
app-foo ref="bar"

View File

@ -1,3 +1,3 @@
div w-bind
app-foo name="a" w-id="bar[]"
app-foo name="b" w-id="bar[]"
app-foo name="a" ref="bar[]"
app-foo name="b" ref="bar[]"

View File

@ -1,2 +1,2 @@
div w-bind
app-foo w-id="bar[]"
app-foo ref="bar[]"

View File

@ -1,3 +1,3 @@
<div class="app-invoke-widget-id" w-bind>
<invoke data.barRenderer({}, out) w-id=data.barWidgetId />
<invoke data.barRenderer({}, out) ref=data.barWidgetId />
</div>

View File

@ -1,3 +1,3 @@
<div class="app-invoke-widget-id" w-bind>
<invoke data.barRenderer({}, out) w-id="barTest"/>
<invoke data.barRenderer({}, out) ref="barTest"/>
</div>

View File

@ -1,8 +1,8 @@
<div class="app-jquery-proxy" w-bind>
<span w-id="foo">foo</span>
<span w-id="foo-text">foo-text</span>
<span w-id="fooText">fooText</span>
<ul w-id="ul">
<span ref="foo">foo</span>
<span ref="foo-text">foo-text</span>
<span ref="fooText">fooText</span>
<ul ref="ul">
<li>red</li>
<li>green</li>
<li>blue</li>

View File

@ -1,4 +1,4 @@
<div w-bind>
<label w-id="label" w-for="input">foo</label>
<input w-id="input" value="test">
<label ref="label" for-ref="input">foo</label>
<input ref="input" value="test">
</div>

View File

@ -8,5 +8,5 @@ module.exports = function(helpers) {
expect(forElId).to.exist;
expect(inputEl.value).to.equal('test');
expect(label.getAttribute('w-for')).to.equal(null);
};
expect(label.getAttribute('for-ref')).to.equal(null);
};

View File

@ -1,3 +1,3 @@
<div w-bind>
You have <span w-id="messageCount">${data.messageCount}</span> new messages.
You have <span ref="messageCount">${data.messageCount}</span> new messages.
</div>

View File

@ -1,6 +1,6 @@
<div w-bind>
Hello ${data.name}!
<app-stateful-lifecycle-events-nested w-id="nestedStateful" message-count=data.messageCount/>
<app-stateful-lifecycle-events-nested ref="nestedStateful" message-count=data.messageCount/>
<app-stateful-lifecycle-events-nested name="foo" message-count=data.messageCount/>
</div>

View File

@ -2,6 +2,6 @@
<a href="http://www.ebay.com" w-preserve-attrs="href">
eBay
</a>
<span w-id="hello">Hello ${data.name}!</span>
<span w-id="hello2" w-preserve-attrs="class" class="foo">Hello ${data.name}!</span>
<span ref="hello">Hello ${data.name}!</span>
<span ref="hello2" w-preserve-attrs="class" class="foo">Hello ${data.name}!</span>
</div>

View File

@ -1,10 +1,10 @@
<div w-bind>
<h1>Preserve Begin</h1>
<span w-id="preserve" w-preserve-if(data.preserveCondition) data-renderId=data.renderId>${data.renderId}</span>
<span ref="preserve" w-preserve-if(data.preserveCondition) data-renderId=data.renderId>${data.renderId}</span>
<span w-id="preserveBody" w-preserve-body-if(data.preserveCondition) data-renderId=data.renderId>${data.renderId}</span>
<span ref="preserveBody" w-preserve-body-if(data.preserveCondition) data-renderId=data.renderId>${data.renderId}</span>
<app-stateful-rerender w-id="widget" name=(data.renderId.toString()) w-preserve-if(data.preserveCondition) />
<app-stateful-rerender ref="widget" name=(data.renderId.toString()) w-preserve-if(data.preserveCondition) />
<!-- Test without an ID -->

View File

@ -1,5 +1,5 @@
<div w-bind>
<input w-id="input" type="text" value="test" />
<button w-id="button" />
<app-iframe-more-content w-id="more" />
<input ref="input" type="text" value="test" />
<button ref="button" />
<app-iframe-more-content ref="more" />
</div>

View File

@ -1,3 +1,3 @@
<div w-bind>
<input w-id="input" type="text" value="hello" />
<input ref="input" type="text" value="hello" />
</div>

View File

@ -1,3 +1,3 @@
<div class="foo" w-bind>
<iframe w-id="frame"></iframe>
<iframe ref="frame"></iframe>
</div>

View File

@ -1,3 +1,3 @@
<div w-bind>
Hello <span w-id="name">${data.name}</span>!
Hello <span ref="name">${data.name}</span>!
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,5 +1,5 @@
<div class="app-repeated-id-widgetless" w-bind>
<for(renderer in data.renderers)>
<invoke renderer({}, out) w-id="childWidget[]" />
<invoke renderer({}, out) ref="childWidget[]" />
</for>
</div>

View File

@ -1,5 +1,5 @@
<div w-bind>
[app-rerender-init-order]
<app-rerender-init-order-child w-id="childA" id="childA" version=data.version/>
<app-rerender-init-order-child w-id="childB" id="childB" version=data.version/>
<app-rerender-init-order-child ref="childA" id="childA" version=data.version/>
<app-rerender-init-order-child ref="childB" id="childB" version=data.version/>
</div>

View File

@ -1,4 +1,4 @@
<div class="app-stateful-reuse-widgets" w-bind>
<app-stateful-button size=data.buttonSize w-id="button1">${data.buttonSize}</app-stateful-button>
<app-stateful-button size="large" w-id="button2">button2</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button1">${data.buttonSize}</app-stateful-button>
<app-stateful-button size="large" ref="button2">button2</app-stateful-button>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,2 +1,2 @@
<app-stateful-button label="Submit" w-id="submitButton"/>
<app-stateful-button label="Cancel" w-id="cancelButton"/>
<app-stateful-button label="Submit" ref="submitButton"/>
<app-stateful-button label="Cancel" ref="cancelButton"/>

View File

@ -1,3 +1,3 @@
<div class="app-stateful-preserve-body" w-bind>
<app-stateful-button size="large" w-id="button1">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size="large" ref="button1">${data.buttonLabel}</app-stateful-button>
</div>

View File

@ -1,4 +1,4 @@
<div class="app-stateful-reuse-widgets" w-bind>
<app-stateful-button size=data.buttonSize w-id="button1">${data.buttonSize}</app-stateful-button>
<app-stateful-button size="large" w-id="button2">button2</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button1">${data.buttonSize}</app-stateful-button>
<app-stateful-button size="large" ref="button2">button2</app-stateful-button>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
<app-stateful-button size=data.buttonSize w-id="button1">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize w-id="button2">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button1">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button2">${data.buttonLabel}</app-stateful-button>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
<app-stateful-button size=data.buttonSize w-id="button1">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize w-id="button2">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button1">${data.buttonLabel}</app-stateful-button>
<app-stateful-button size=data.buttonSize ref="button2">${data.buttonLabel}</app-stateful-button>
</div>

View File

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

View File

@ -1,5 +1,5 @@
<div w-bind="./widget">
[app-async] ${data.name}
<app-hello name="Async" w-id="hello"/>
<app-hello name="Async" ref="hello"/>
</div>

View File

@ -1,7 +1,7 @@
<div w-bind="./widget">
<app-hello name="Frank" w-id="helloFrank"/>
<app-async name="async1" w-id="async1"/>
<app-hello name="John" w-id="helloJohn"/>
<app-async name="async2" w-id="async2"/>
<app-hello name="Jane" w-id="helloJane"/>
<app-hello name="Frank" ref="helloFrank"/>
<app-async name="async1" ref="async1"/>
<app-hello name="John" ref="helloJohn"/>
<app-async name="async2" ref="async2"/>
<app-hello name="Jane" ref="helloJane"/>
</div>

View File

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

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,6 +1,6 @@
<div id="appFixedId" class="app-fixed-id" w-bind="./widget">
<h1>app-fixed-id</h1>
<div w-id="wrapper">
<app-hello w-id="hello" name="hello"/>
<div ref="wrapper">
<app-hello ref="hello" name="hello"/>
</div>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,4 +1,4 @@
<div w-bind>
Hello ${data.name}! You have ${data.messageCount} new messages.
<div w-id="foo">foo</div>
<div ref="foo">foo</div>
</div>

View File

@ -1,2 +1,2 @@
<div w-bind w-id="foo">
<div w-bind ref="foo">
</div>

View File

@ -1,2 +1,2 @@
<div w-bind w-id="foo">
<div w-bind ref="foo">
</div>

View File

@ -3,5 +3,5 @@ var expect = require('chai').expect;
module.exports = function(helpers) {
expect(function() {
require('./template.marko');
}).to.throw(/The "w-id" attribute cannot be used in conjuntion with the "w-bind" attribute/);
};
}).to.throw(/The "ref" attribute cannot be used in conjunction with the "w-bind" attribute./);
};

View File

@ -27,6 +27,7 @@ module.exports = function assignWidgetId(isRepeated) {
var context = this.context;
var builder = this.builder;
let widgetRef;
var nestedIdExpression;
var idExpression;
@ -52,29 +53,43 @@ module.exports = function assignWidgetId(isRepeated) {
// We need to handle the following scenarios:
//
// 1) The HTML element already has an "id" attribute
// 2) The HTML element has a "w-id" attribute (we already converted this
// 2) The HTML element has a "ref" or "w-id" attribute (we already converted this
// to an "id" attribute above)
// 3) The HTML does not have an "id" or "w-el-id" attribute. We must add
// 3) The HTML does not have an "id" or "ref" attribute. We must add
// an "id" attribute with a unique ID.
var isCustomTag = el.type !== 'HtmlElement';
if (el.hasAttribute('ref')) {
widgetRef = el.getAttributeValue('ref');
el.removeAttribute('ref');
}
if (el.hasAttribute('w-id')) {
let widgetId = el.getAttributeValue('w-id');
console.warn('The "w-id" attribute is deprecated. Please use "ref" instead.');
if (widgetRef) {
this.addError('The "w-id attribute cannot be used in conjuction with the "ref" attribute.');
return;
}
widgetRef = el.getAttributeValue('w-id');
el.removeAttribute('w-id');
}
idExpression = this.buildWidgetElIdFunctionCall(widgetId);
nestedIdExpression = widgetId;
if (widgetRef) {
idExpression = this.buildWidgetElIdFunctionCall(widgetRef);
nestedIdExpression = widgetRef;
if (isCustomTag) {
// The element is a custom tag
this.getWidgetArgs().setId(nestedIdExpression);
} else {
if (el.hasAttribute('id')) {
this.addError('The "w-id" attribute cannot be used in conjuction with the "id" attribute');
this.addError('The "ref" and "w-id" attributes cannot be used in conjuction with the "id" attribute.');
return;
}
el.setAttributeValue('id', idExpression);
@ -140,7 +155,6 @@ module.exports = function assignWidgetId(isRepeated) {
if (isCustomTag) {
transformHelper.getWidgetArgs().setId(nestedIdExpression);
} else {
el.setAttributeValue('id', idExpression);
}
@ -149,4 +163,4 @@ module.exports = function assignWidgetId(isRepeated) {
};
return this.widgetIdInfo;
};
};

View File

@ -16,21 +16,33 @@
module.exports = function handleWidgetFor() {
var el = this.el;
var widgetFor;
if (el.hasAttribute('for-ref')) {
widgetFor = el.getAttributeValue('for-ref');
el.removeAttribute('for-ref');
}
var widgetFor = el.getAttributeValue('w-for');
el.removeAttribute('w-for');
if (el.hasAttribute('w-for')) {
console.warn('The "w-for" tag is deprecated. Please use "for-ref" instead.');
if (widgetFor) {
this.addError('The "w-for" tag cannot be used with "for-ref".');
return;
} else {
widgetFor = el.getAttributeValue('w-for');
}
el.removeAttribute('w-for');
}
if (widgetFor == null) {
return;
}
// Handle the "w-for" attribute
// Handle the "for-ref" attribute
if (el.hasAttribute('for')) {
this.addError('The "w-for" attribute cannot be used in conjuction with the "for" attribute');
this.addError('The "for-ref" and "w-for" attribute cannot be used in conjuction with the "for" attribute.');
} else {
el.setAttributeValue(
'for',
this.buildWidgetElIdFunctionCall(widgetFor));
}
};
};

View File

@ -28,6 +28,24 @@
"type": "expression",
"preserve-name": true
},
"@for-ref": {
"type": "string",
"preserve-name": true
},
"@ref": {
"type": "string",
"preserve-name": true,
"autocomplete": [
{
"displayText": "ref=\"<method>\"",
"snippet": "ref=\"${1:method}\"",
"descriptionMoreURL": "http://markojs.com/docs/marko-widgets/get-started/#referencing-nested-widgets"
},
{
"descriptionMoreURL": "http://markojs.com/docs/marko-widgets/get-started/#referencing-nested-widgets"
}
]
},
"@w-for": {
"type": "string",
"preserve-name": true
@ -37,7 +55,7 @@
"preserve-name": true,
"autocomplete": [
{
"displayText": "w-id=\"<method>\"",
"displayText": "w-id=\"<method>\" (Deprecated)",
"snippet": "w-id=\"${1:method}\"",
"descriptionMoreURL": "http://markojs.com/docs/marko-widgets/get-started/#referencing-nested-widgets"
},
@ -163,4 +181,4 @@
}
}
}

View File

@ -41,6 +41,9 @@ module.exports = function transform(el, context) {
if (el.hasAttribute('w-bind')) {
el.setFlag('hasWidgetBind');
if (el.hasAttribute('ref')) {
transformHelper.addError('The "ref" attribute cannot be used in conjunction with the "w-bind" attribute.');
}
if (el.hasAttribute('w-id')) {
transformHelper.addError('The "w-id" attribute cannot be used in conjuntion with the "w-bind" attribute.');
}
@ -57,11 +60,11 @@ module.exports = function transform(el, context) {
transformHelper.handleWidgetPreserve();
}
if (el.hasAttribute('w-id')) {
if (el.hasAttribute('ref') || el.hasAttribute('w-id')) {
transformHelper.assignWidgetId();
}
if (el.hasAttribute('w-for')) {
if (el.hasAttribute('for-ref') || el.hasAttribute('w-for')) {
transformHelper.handleWidgetFor();
}
@ -85,4 +88,4 @@ module.exports = function transform(el, context) {
if (el.type !== 'HtmlElement') { // Only custom tags can have nested widgets
transformHelper.getWidgetArgs().compile(transformHelper);
}
};
};