Repository: DevExpress/devextreme-vue
Pull request #251 (2019-03-12 15:44:21)

Render template of a nested item only if the item has scoped slots


Patch:
diff --git a/src/core/component.ts b/src/core/component.ts
index 447b559..532fd5e 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -29,6 +29,19 @@ interface IBaseComponent extends IVue, IWidgetComponent, IEventBusHolder {
     $_processChildren: () => void;
 }
 
+function asConfigurable(vueComponent: IVue): IConfigurable | undefined {
+    if (!vueComponent.$vnode) {
+        return undefined;
+    }
+
+    const configurable = vueComponent.$vnode.componentOptions as any as IConfigurable;
+    if (!configurable.$_config || !configurable.$_config.name) {
+        return undefined;
+    }
+
+    return configurable;
+}
+
 const Vue = VueType.default || VueType;
 
 const DX_TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper";
@@ -106,13 +119,12 @@ const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
 
         $_getIntegrationOptions(): object {
             const TEMPLATE_PROP = "template";
-            function shouldAddTemplate(child) {
-                return child.$vnode
-                && child.$vnode.componentOptions.$_config
-                && child.$vnode.componentOptions.$_config.name
-                && TEMPLATE_PROP in child.$props
-                && child.$scopedSlots.default;
+
+            function shouldAddTemplate(child: IVue) {
+                return TEMPLATE_PROP in child.$props
+                && (child.$vnode.data && child.$vnode.data.scopedSlots);
             }
+
             const result: Record<string, any> = {
                 integrationOptions:  {
                     watchMethod: this.$_getWatchMethod(),
@@ -122,9 +134,14 @@ const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
 
             const templates = extractScopedSlots(this.$scopedSlots, Object.keys(this.$slots));
 
-            this.$children.forEach((child: any) => {
+            this.$children.forEach((child: IVue) => {
+                const configurable = asConfigurable(child);
+                if (!configurable) {
+                    return;
+                }
+
                 if (shouldAddTemplate(child)) {
-                    const templateName = `${child.$vnode.componentOptions.$_config.fullPath}.${TEMPLATE_PROP}`;
+                    const templateName = `${configurable.$_config.fullPath}.${TEMPLATE_PROP}`;
                     templates[templateName] = child.$scopedSlots.default;
                     result[templateName] = templateName;
                 }


Test patch:
diff --git a/src/core/component.test.ts b/src/core/component.test.ts
index 5ae1d52..379916c 100644
--- a/src/core/component.test.ts
+++ b/src/core/component.test.ts
@@ -1089,6 +1089,36 @@ describe("static items", () => {
 
         expect(renderedTemplate.innerHTML).toBe("1");
     });
+
+    it("doesn't pass integrationOptions to widget if nestd item has sub nested item", () => {
+        const NestedItem = Vue.extend({
+            extends: DxConfiguration,
+            props: {
+                prop1: Number,
+                template: String
+            }
+        });
+        (NestedItem as any as IConfigurationComponent).$_optionName = "items";
+        (NestedItem as any as IConfigurationComponent).$_isCollectionItem = true;
+
+        const subNested = buildTestConfigCtor();
+        (subNested as any as IConfigurationComponent).$_optionName = "subNestedOption";
+
+        new Vue({
+            template: `<test-component>
+                         <nested-item>
+                            <sub-nested prop2="abc"/>
+                         </nested-item>
+                       </test-component>`,
+            components: {
+                TestComponent,
+                subNested,
+                NestedItem
+            }
+        }).$mount();
+
+        expect(WidgetClass.mock.calls[0][1].integrationOptions.templates).toBeUndefined();
+    });
 });
 
 describe("events emitting", () => {