diff --git a/docs/widgets/overview.md b/docs/widgets/overview.md index c54e972c5..9fa2ba2a9 100644 --- a/docs/widgets/overview.md +++ b/docs/widgets/overview.md @@ -373,12 +373,12 @@ Sometimes it is important to _not_ re-render a DOM subtree. This may due to eith - DOM nodes contains externally provided content - DOM nodes have internal state that needs to be maintained -Marko Widgets allows DOM nodes to be preserved by putting a special `w-preserve`, `w-preserve-if()`, `w-preserve-body` or `w-preserve-body-if()` attribute on the HTML tags that should be preserved. Preserved DOM nodes will be reused and re-inserted into a widget's newly rendered DOM automatically. +Marko Widgets allows DOM nodes to be preserved by putting a special `no-update`, `no-update-if()`, `no-update-body` or `no-update-body-if()` attribute on the HTML tags that should be preserved. Preserved DOM nodes will be reused and re-inserted into a widget's newly rendered DOM automatically. ```xml
- +

The root span and all its children will never be re-rendered. @@ -387,7 +387,7 @@ Marko Widgets allows DOM nodes to be preserved by putting a special `w-preserve` Rendered at ${Date.now()}.

-
+
Only the children of the div will preserved and the outer HTML div tag will be re-rendered.
@@ -395,7 +395,7 @@ Marko Widgets allows DOM nodes to be preserved by putting a special `w-preserve` Don't rerender the search results if no search results are provided. + no-update-if(data.searchResults == null)/>
``` diff --git a/docs/widgets/taglib-api.md b/docs/widgets/taglib-api.md index c89b66565..47620f56c 100644 --- a/docs/widgets/taglib-api.md +++ b/docs/widgets/taglib-api.md @@ -123,9 +123,9 @@ For the example above it is assumed that the nested widget will emit the custom this.emit('handleSomeCustomEvent', { foo: bar }); ``` - + -## w-preserve +## no-update Preserves the DOM subtree associated with the DOM element or widget such that it won't be modified or rerendered when rerendering the UI component. @@ -133,7 +133,7 @@ Example: ```xml
- +
...
@@ -141,39 +141,39 @@ Example: ```xml
- +
``` -## w-preserve-if +## no-update-if -Similar to [w-preserve](#w-preserve) except that the DOM subtree is conditionally preserved: +Similar to [no-update](#no-update) except that the DOM subtree is conditionally preserved: ```xml
- +
...
``` -## w-preserve-body +## no-update-body -Similar to [w-preserve](#w-preserve) except that only the child DOM nodes are preserved: +Similar to [no-update](#no-update) except that only the child DOM nodes are preserved: ```xml -
+
...
``` -## w-preserve-body-if +## no-update-body-if -Similar to [w-preserve-if](#w-preserve) except that only the child DOM nodes are preserved: +Similar to [no-update-if](#no-update) except that only the child DOM nodes are preserved: ```xml
- +
...
diff --git a/test/autotests/taglib-lookup/forEachAttribute/expected.json b/test/autotests/taglib-lookup/forEachAttribute/expected.json index 9fcce72de..73645491e 100644 --- a/test/autotests/taglib-lookup/forEachAttribute/expected.json +++ b/test/autotests/taglib-lookup/forEachAttribute/expected.json @@ -23,6 +23,10 @@ "w-preserve-body", "w-preserve-if", "w-preserve-body-if", + "no-update", + "no-update-body", + "no-update-if", + "no-update-body-if", "w-preserve-attrs", "w-on*" ] diff --git a/test/autotests/taglib-lookup/forEachTag/expected.json b/test/autotests/taglib-lookup/forEachTag/expected.json index e1f7c89b1..b2bca5d2a 100644 --- a/test/autotests/taglib-lookup/forEachTag/expected.json +++ b/test/autotests/taglib-lookup/forEachTag/expected.json @@ -47,6 +47,7 @@ "w-widget", "init-widgets", "w-preserve", + "no-update", "widget-types", "body" ] \ No newline at end of file diff --git a/test/autotests/taglib-lookup/getTagsSorted/expected.json b/test/autotests/taglib-lookup/getTagsSorted/expected.json index aa715d5f0..71053ea93 100644 --- a/test/autotests/taglib-lookup/getTagsSorted/expected.json +++ b/test/autotests/taglib-lookup/getTagsSorted/expected.json @@ -40,6 +40,7 @@ "macro", "macro-body", "marko-preserve-whitespace", + "no-update", "pre", "script", "style", diff --git a/test/autotests/widgets-browser/preserve-dom-body-no-id/template.marko b/test/autotests/widgets-browser/preserve-dom-body-no-id/template.marko index e41a39497..85574c6d6 100644 --- a/test/autotests/widgets-browser/preserve-dom-body-no-id/template.marko +++ b/test/autotests/widgets-browser/preserve-dom-body-no-id/template.marko @@ -1,6 +1,6 @@
${data.counter}
- + ${data.counter}
\ No newline at end of file diff --git a/test/autotests/widgets-browser/preserve-dom-body/template.marko b/test/autotests/widgets-browser/preserve-dom-body/template.marko index 97b661086..fdc5a9b8b 100644 --- a/test/autotests/widgets-browser/preserve-dom-body/template.marko +++ b/test/autotests/widgets-browser/preserve-dom-body/template.marko @@ -1,6 +1,6 @@
${data.counter}
- + ${data.counter} \ No newline at end of file diff --git a/test/autotests/widgets-browser/preserve-dom-no-id/template.marko b/test/autotests/widgets-browser/preserve-dom-no-id/template.marko index a300090f7..cf3116db6 100644 --- a/test/autotests/widgets-browser/preserve-dom-no-id/template.marko +++ b/test/autotests/widgets-browser/preserve-dom-no-id/template.marko @@ -1,6 +1,6 @@
${data.counter}
- + ${data.counter} \ No newline at end of file diff --git a/test/autotests/widgets-browser/preserve-dom/template.marko b/test/autotests/widgets-browser/preserve-dom/template.marko index 1a4019037..9df6a71d6 100644 --- a/test/autotests/widgets-browser/preserve-dom/template.marko +++ b/test/autotests/widgets-browser/preserve-dom/template.marko @@ -1,6 +1,6 @@
${data.counter}
- + ${data.counter} \ No newline at end of file diff --git a/test/autotests/widgets-browser/widget-preserve-dom-if/template.marko b/test/autotests/widgets-browser/widget-preserve-dom-if/template.marko index f636793fc..018bc2306 100644 --- a/test/autotests/widgets-browser/widget-preserve-dom-if/template.marko +++ b/test/autotests/widgets-browser/widget-preserve-dom-if/template.marko @@ -1,18 +1,18 @@

Preserve Begin

- ${data.renderId} + ${data.renderId} - ${data.renderId} + ${data.renderId} - + - ${data.renderId} + ${data.renderId} - ${data.renderId} + ${data.renderId} - +

Preserve End

\ No newline at end of file diff --git a/test/autotests/widgets-browser/widget-preserve-dom-repeated/template.marko b/test/autotests/widgets-browser/widget-preserve-dom-repeated/template.marko index 7d268cac4..a3a445b09 100644 --- a/test/autotests/widgets-browser/widget-preserve-dom-repeated/template.marko +++ b/test/autotests/widgets-browser/widget-preserve-dom-repeated/template.marko @@ -1 +1 @@ -
${color}
\ No newline at end of file +
${color}
\ No newline at end of file diff --git a/test/autotests/widgets-browser/widget-preserve-dom-root/template.marko b/test/autotests/widgets-browser/widget-preserve-dom-root/template.marko index 9f5e9b50f..71fe53fe6 100644 --- a/test/autotests/widgets-browser/widget-preserve-dom-root/template.marko +++ b/test/autotests/widgets-browser/widget-preserve-dom-root/template.marko @@ -1,3 +1,3 @@ -
+
Hello ${data.name}! You have ${data.messageCount} new messages.
\ No newline at end of file diff --git a/widgets/taglib/TransformHelper/handleWidgetPreserve.js b/widgets/taglib/TransformHelper/handleWidgetPreserve.js index 41a942c3b..b24b5dd98 100644 --- a/widgets/taglib/TransformHelper/handleWidgetPreserve.js +++ b/widgets/taglib/TransformHelper/handleWidgetPreserve.js @@ -16,12 +16,11 @@ 'use strict'; function addPreserve(transformHelper, bodyOnly, condition) { + let el = transformHelper.el; + let context = transformHelper.context; + let builder = transformHelper.builder; - var el = transformHelper.el; - var context = transformHelper.context; - var builder = transformHelper.builder; - - var preserveAttrs = {}; + let preserveAttrs = {}; if (bodyOnly) { preserveAttrs['body-only'] = builder.literal(bodyOnly); @@ -31,13 +30,13 @@ function addPreserve(transformHelper, bodyOnly, condition) { preserveAttrs['if'] = condition; } - var widgetIdInfo = transformHelper.assignWidgetId(true /* repeated */); - var idVarNode = widgetIdInfo.idVarNode ? null : widgetIdInfo.createIdVarNode(); + let widgetIdInfo = transformHelper.assignWidgetId(true /* repeated */); + let idVarNode = widgetIdInfo.idVarNode ? null : widgetIdInfo.createIdVarNode(); preserveAttrs.id = transformHelper.getIdExpression(); - var preserveNode = context.createNodeForEl('w-preserve', preserveAttrs); - var idVarNodeTarget; + let preserveNode = context.createNodeForEl('w-preserve', preserveAttrs); + let idVarNodeTarget; if (bodyOnly) { el.moveChildrenTo(preserveNode); @@ -57,33 +56,127 @@ function addPreserve(transformHelper, bodyOnly, condition) { return preserveNode; } +function deprecatedWarning(preserveType, transformHelper, el) { + let attribute = preserveType.attribute; + let suffix = preserveType.suffix; + let context = transformHelper.getCompileContext(); + + let newAttributeName = 'no-update'; + if (suffix) { + newAttributeName += suffix; + } + + console.warn(`The '${attribute}' attribute is deprecated. Please use '${newAttributeName}' instead. (${el.pos ? context.getPosInfo(el.pos) : context.filename})`); +} + +function preserveHandler(transformHelper, preserveType, el) { + if (preserveType.deprecated) { + deprecatedWarning(preserveType, transformHelper, el); + } + + el.removeAttribute(preserveType.attribute); + addPreserve(transformHelper, false); +} + +function preserveIfHandler(transformHelper, preserveType, el) { + if (preserveType.deprecated) { + deprecatedWarning(preserveType, transformHelper, el); + } + + let attribute = preserveType.attribute; + let preserveIfAttr = el.getAttribute(attribute); + let preserveIfCondition = preserveIfAttr.argument; + + if (!preserveIfCondition) { + transformHelper.addError(`The '${attribute}' attribute should have an argument. For example:
`); + return; + } + + addPreserve(transformHelper, false, transformHelper.builder.expression(preserveIfCondition)); + el.removeAttribute(attribute); +} + +function preserveBodyHandler(transformHelper, preserveType, el) { + if (preserveType.deprecated) { + deprecatedWarning(preserveType, transformHelper, el); + } + + el.removeAttribute(preserveType.attribute); + addPreserve(transformHelper, true); +} + +function preserveBodyIfHandler(transformHelper, preserveType, el) { + if (preserveType.deprecated) { + deprecatedWarning(preserveType, transformHelper, el); + } + + let attribute = preserveType.attribute; + let preserveBodyIfAttr = el.getAttribute(attribute); + let preserveBodyIfCondition = preserveBodyIfAttr.argument; + + if (!preserveBodyIfCondition) { + transformHelper.addError(`The '${attribute}' attribute should have an argument. For example:
`); + return; + } + + addPreserve(transformHelper, true, transformHelper.builder.expression(preserveBodyIfCondition)); + el.removeAttribute('w-preserve-body-if'); +} + +const preserveTypes = [ + // The new preserve types + { + attribute: 'no-update', + handler: preserveHandler + }, + { + attribute: 'no-update-if', + handler: preserveIfHandler + }, + { + attribute: 'no-update-body', + handler: preserveBodyHandler + }, + { + attribute: 'no-update-body-if', + handler: preserveBodyIfHandler + }, + + // The deprecated preserve types + { + attribute: 'w-preserve', + handler: preserveHandler, + deprecated: true + }, + { + attribute: 'w-preserve-if', + suffix: '-if', + handler: preserveIfHandler, + deprecated: true + }, + { + attribute: 'w-preserve-body', + suffix: '-body', + handler: preserveBodyHandler, + deprecated: true + }, + { + attribute: 'w-preserve-body-if', + suffix: '-body-if', + handler: preserveBodyIfHandler, + deprecated: true + } +]; + module.exports = function handleWidgetPreserve() { - var el = this.el; + let el = this.el; - if (el.hasAttribute('w-preserve')) { - el.removeAttribute('w-preserve'); - addPreserve(this, false); - } else if (el.hasAttribute('w-preserve-if')) { - let preserveIfAttr = el.getAttribute('w-preserve-if'); - var preserveIfCondition = preserveIfAttr.argument; - if (!preserveIfCondition) { - this.addError('The `w-preserve-if` attribute should have an argument. For example:
'); + for (let i = 0; i < preserveTypes.length; i++) { + let preserveType = preserveTypes[i]; + + if (el.hasAttribute(preserveType.attribute)) { + preserveType.handler(this, preserveType, el); return; } - addPreserve(this, false, this.builder.expression(preserveIfCondition)); - el.removeAttribute('w-preserve-if'); - } else if (el.hasAttribute('w-preserve-body')) { - el.removeAttribute('w-preserve-body'); - addPreserve(this, true); - } else if (el.hasAttribute('w-preserve-body-if')) { - let preserveBodyIfAttr = el.getAttribute('w-preserve-body-if'); - var preserveBodyIfCondition = preserveBodyIfAttr.argument; - if (!preserveBodyIfCondition) { - this.addError('The `w-preserve-body-if` attribute should have an argument. For example:
'); - return; - } - - addPreserve(this, true, this.builder.expression(preserveBodyIfCondition)); - el.removeAttribute('w-preserve-body-if'); } }; \ No newline at end of file diff --git a/widgets/taglib/TransformHelper/index.js b/widgets/taglib/TransformHelper/index.js index 94ef5f737..89250981e 100644 --- a/widgets/taglib/TransformHelper/index.js +++ b/widgets/taglib/TransformHelper/index.js @@ -57,6 +57,10 @@ class TransformHelper { return this.widgetIdInfo; } + getCompileContext() { + return this.context; + } + getDefaultWidgetModule() { var dirname = this.dirname; if (resolveFrom(dirname, './component')) { diff --git a/widgets/taglib/marko.json b/widgets/taglib/marko.json index cd0137613..a21b46f12 100644 --- a/widgets/taglib/marko.json +++ b/widgets/taglib/marko.json @@ -97,6 +97,7 @@ "preserve-name": true, "autocomplete": [ { + "displayText": "w-preserve (Deprecated)", "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" } ] @@ -106,6 +107,7 @@ "preserve-name": true, "autocomplete": [ { + "displayText": "w-preserve-body (Deprecated)", "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" } ] @@ -114,6 +116,7 @@ "preserve-name": true, "autocomplete": [ { + "displayText": "w-preserve-if (Deprecated)", "snippet": "w-preserve-if(${1:condition})", "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" } @@ -123,11 +126,48 @@ "preserve-name": true, "autocomplete": [ { + "displayText": "w-preserve-body-if (Deprecated)", "snippet": "w-preserve-body-if(${1:condition})", "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" } ] }, + "@no-update": { + "type": "flag", + "preserve-name": true, + "autocomplete": [ + { + "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" + } + ] + }, + "@no-update-body": { + "type": "flag", + "preserve-name": true, + "autocomplete": [ + { + "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" + } + ] + }, + "@no-update-if": { + "preserve-name": true, + "autocomplete": [ + { + "snippet": "no-update-if(${1:condition})", + "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" + } + ] + }, + "@no-update-body-if": { + "preserve-name": true, + "autocomplete": [ + { + "snippet": "no-update-body-if(${1:condition})", + "descriptionMoreURL": "http://markojs.com/docs/marko-widgets/#preserving-dom-nodes-during-re-render" + } + ] + }, "@w-preserve-attrs": { "type": "string", "preserve-name": true, @@ -168,6 +208,13 @@ "@body-only": "expression", "autocomplete": [] }, + "": { + "renderer": "./preserve-tag.js", + "@id": "string", + "@if": "expression", + "@body-only": "expression", + "autocomplete": [] + }, "": { "code-generator": "./widget-types-tag.js", "@*": "string", diff --git a/widgets/taglib/widgets-transformer.js b/widgets/taglib/widgets-transformer.js index 6f9274592..efafa5b06 100644 --- a/widgets/taglib/widgets-transformer.js +++ b/widgets/taglib/widgets-transformer.js @@ -53,7 +53,13 @@ module.exports = function transform(el, context) { transformHelper.handleWidgetExtend(); } - if (el.hasAttribute('w-preserve') || + if (/* New preserve attributes */ + el.hasAttribute('no-update') || + el.hasAttribute('no-update-body') || + el.hasAttribute('no-update-if') || + el.hasAttribute('no-update-body-if') || + /* Old preserve attributes */ + el.hasAttribute('w-preserve') || el.hasAttribute('w-preserve-body') || el.hasAttribute('w-preserve-if') || el.hasAttribute('w-preserve-body-if')) {