Repository: DevExpress/devextreme-vue
Pull request #265 (2019-04-27 14:00:40)

Fix the problem of processing config-components as default templates


Patch:
diff --git a/example/components/menu-example.vue b/example/components/menu-example.vue
index 7348937..4e75c28 100644
--- a/example/components/menu-example.vue
+++ b/example/components/menu-example.vue
@@ -2,12 +2,16 @@
     <example-block title="dxMenu">
         <h4>Menu with simple items</h4>
         <dx-menu>
-            <dx-item text="Item 1">
+            <dx-item text="Item 1" icon="user">
                 <dx-item text="Item 1-1"/>
                 <dx-item text="Item 1-2"/>
-                <dx-item text="Item 1-3">
+                <dx-item text="Item 1-3" icon="edit">
                     <dx-item text="Item 1-3-1"/>
                 </dx-item>
+                <dx-item>
+                    <div slot-scope="_">Inline template</div>
+                    <dx-item text="Item 1-4-1"/>
+                </dx-item>
             </dx-item>
             <dx-item text="Item 2"/>
             <dx-item text="Item 3"/>
diff --git a/src/core/component.ts b/src/core/component.ts
index 3bb2c1f..1206112 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -8,7 +8,7 @@ import { getOption } from "./config";
 import Configuration, { bindOptionWatchers, subscribeOnUpdates } from "./configuration";
 import { IConfigurable } from "./configuration-component";
 import { IExtension, IExtensionComponentNode } from "./extension-component";
-import { camelize, extractScopedSlots, toComparable } from "./helpers";
+import { camelize, extractScopedSlots, forEachChildNode, toComparable } from "./helpers";
 
 interface IWidgetComponent extends IConfigurable {
     $_instance: any;
@@ -231,6 +231,14 @@ const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
     }
 });
 
+function cleanWidgetNode(node: Node) {
+    forEachChildNode(node, (childNode) => {
+        if (childNode.nodeName === "#comment") {
+            childNode.remove();
+        }
+    });
+}
+
 const DxComponent: VueConstructor = BaseComponent.extend({
     methods: {
         $_getExtraIntegrationOptions(): object {
@@ -251,6 +259,8 @@ const DxComponent: VueConstructor = BaseComponent.extend({
     },
 
     mounted(): void {
+        cleanWidgetNode(this.$el);
+
         this.$_createWidget(this.$el);
         this.$_instance.endUpdate();
         this.$children.forEach((child: IExtension) => {
diff --git a/src/core/helpers.ts b/src/core/helpers.ts
index 0a4bcc4..6f7d194 100644
--- a/src/core/helpers.ts
+++ b/src/core/helpers.ts
@@ -46,3 +46,10 @@ export function extractScopedSlots(
 
     return result;
 }
+
+export function forEachChildNode(
+    el: Node,
+    callback: (child: ReturnType<Node["childNodes"]["item"]>) => void
+) {
+    Array.prototype.slice.call(el.childNodes).forEach(callback);
+}


Test patch:
diff --git a/src/core/component.test.ts b/src/core/component.test.ts
index b4ad0e5..7c37d44 100644
--- a/src/core/component.test.ts
+++ b/src/core/component.test.ts
@@ -827,6 +827,33 @@ describe("nested option", () => {
         });
     });
 
+    it("removes obstructive nodes before widget creation (T711311)", () => {
+        const subNestedCollectionItem = buildTestConfigCtor();
+        (subNestedCollectionItem as any as IConfigurationComponent).$_optionName = "subNestedOption";
+        (subNestedCollectionItem as any as IConfigurationComponent).$_isCollectionItem = true;
+
+        let innerHtml;
+        WidgetClass.mockImplementationOnce((element: HTMLElement, options: any) => {
+            innerHtml = element.innerHTML;
+            return createWidget(element, options);
+        });
+
+        new Vue({
+            template:
+                `<test-component>` +
+                `  <nested>` +
+                `    <sub-nested-collection-item/>` +
+                `  </nested>` +
+                `</test-component>`,
+            components: {
+                TestComponent,
+                Nested,
+                subNestedCollectionItem
+            },
+        }).$mount();
+
+        expect(innerHtml).toBe("");
+    });
 });
 
 function renderTemplate(name: string, model?: object, container?: any, index?: number): Element {