Repository: DevExpress/devextreme-vue (ts)

Issue:
dxTabPanels not render async components
<!--
  Note: if the issue is a general Vue question, please check Vue Docs first: https://vuejs.org/v2/guide/
  If your question is about  DevExtreme widgets' APIs, use the DevExpress Support Center: https://www.devexpress.com/Support/Center
-->

### Issue Type
'bug report'

### Description
If tab template contains async component, this component not rendered. If component place out tab panel work fine.


#### Steps to Reproduce
[sample](https://codesandbox.io/s/03w63pw4rw)


<!--
  If you provide a minimal demo of the problem\proposal, it helps us to solve it much faster.
  Paste the link to your online code editor (like https://stackblitz.com) below:
-->

### Environment Details
<!--
Specify DevExtreme version, DevExtreme Vue Components version, Vue version and other environment details or notes you consider important.
-->
- devextreme version: 18.1.7
- devextreme-vue version: 18.1.7-alpha.10
- vue version: 2.5.17


Pull request:
Update template components on base component update (#208)
fix #190 

Patch:
diff --git a/example/app.vue b/example/app.vue
index a27797a..a505de0 100644
--- a/example/app.vue
+++ b/example/app.vue
@@ -11,6 +11,8 @@
         <br/>
         <scroll-view-example/>
         <br/>
+        <tab-panel-example />
+        <br/>
         <list-example/>
         <br/>
         <grid-example/>
@@ -39,6 +41,8 @@ import ValidationExample from "./components/validation-example";
 import SchedulerExample from "./components/scheduler-example";
 import ScrollViewExample from "./components/scroll-view-example";
 import ChartExample from "./components/chart-example";
+import TabPanelExample from "./components/tab-panel-example";
+
 
 export default {
     components: {
@@ -53,7 +57,8 @@ export default {
         GridExample,
         SchedulerExample,
         ScrollViewExample,
-        ChartExample
+        ChartExample,
+        TabPanelExample
     },
     name: "app"
 }
diff --git a/example/components/grid-example.vue b/example/components/grid-example.vue
index b45fd4d..1d15ea5 100644
--- a/example/components/grid-example.vue
+++ b/example/components/grid-example.vue
@@ -79,8 +79,6 @@
 </template>
 
 <script>
-import Vue from "vue";
-
 import ExampleBlock from "./example-block";
 import { DxButton, DxCheckBox, DxDataGrid, DxDateBox, DxSwitch } from "../../src";
 import {
diff --git a/example/components/tab-panel-example.vue b/example/components/tab-panel-example.vue
new file mode 100644
index 0000000..d507ff9
--- /dev/null
+++ b/example/components/tab-panel-example.vue
@@ -0,0 +1,38 @@
+<template>
+    <example-block title="dxTabPanel">
+        <dx-tab-panel :items="tabs">
+            <div slot="tab1" slot-scope="_">
+                <h3>First Tab Content</h3>
+            </div>
+            <div slot="tab2" slot-scope="_">
+                <async-component/>
+            </div>
+        </dx-tab-panel>
+    </example-block>
+</template>
+
+<script>
+import Vue from "vue";
+import ExampleBlock from "./example-block";
+import { DxTabPanel } from "../../src";
+
+const SecondTabComponent = Vue.extend({
+    template: "<h3>Second Tab Content</h3>"
+});
+
+export default {
+  components: {
+    ExampleBlock,
+    DxTabPanel,
+    AsyncComponent: () => Promise.resolve(SecondTabComponent)
+  },
+  data: function() {
+    return {
+        tabs: [
+          { title:"Sync Tab", template:"tab1" },
+          { title:"Async Tab", template:"tab2" }
+        ]
+    };
+  }
+};
+</script>
\ No newline at end of file
diff --git a/example/components/validation-example.vue b/example/components/validation-example.vue
index db66765..1c40872 100644
--- a/example/components/validation-example.vue
+++ b/example/components/validation-example.vue
@@ -18,7 +18,6 @@
       <dx-button text="Submit" @click="validate"/>
     </dx-validation-group>
     <br/>
-    <br/>
     <h3>Custom value validation</h3>
     <br/>
     <div>
diff --git a/src/core/component.ts b/src/core/component.ts
index 70dcf51..6724339 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -1,5 +1,5 @@
 import * as VueType from "vue";
-import { VNode, VueConstructor } from "vue";
+import IVue, { VNode, VueConstructor } from "vue";
 
 import * as events from "devextreme/events";
 
@@ -14,20 +14,47 @@ interface IWidgetComponent extends IConfigurable {
     $_WidgetClass: any;
 }
 
+interface IEventBusHolder {
+    eventBus: IVue;
+}
+
+interface IBaseComponent extends IVue, IWidgetComponent, IEventBusHolder {
+    $_isExtension: boolean;
+    $_createWidget: (element: any) => void;
+    $_getIntegrationOptions: () => void;
+    $_getExtraIntegrationOptions: () => void;
+    $_getWatchMethod: () => void;
+    $_createEmitters: () => void;
+    $_fillTemplate: () => void;
+    $_processChildren: () => void;
+}
+
 const Vue = VueType.default || VueType;
 
 const DX_TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper";
 const DX_REMOVE_EVENT = "dxremove";
 
-const BaseComponent: VueConstructor = Vue.extend({
+const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
 
     inheritAttrs: false,
 
+    data() {
+        return {
+            eventBus: new Vue()
+        };
+    },
+
+    provide() {
+        return {
+            eventBus: this.eventBus
+        };
+    },
+
     render(createElement: (...args) => VNode): VNode {
         const children: VNode[] = [];
-        pullAllChildren(this.$slots.default, children, (this as any as IWidgetComponent).$_config);
-        this.$_processChildren(children);
+        pullAllChildren(this.$slots.default, children, this.$_config);
 
+        this.$_processChildren(children);
         return createElement(
             "div",
             {
@@ -37,8 +64,12 @@ const BaseComponent: VueConstructor = Vue.extend({
         );
     },
 
+    updated() {
+        this.eventBus.$emit("updated");
+    },
+
     beforeDestroy(): void {
-        const instance = (this as any as IWidgetComponent).$_instance;
+        const instance = this.$_instance;
         if (instance) {
             events.triggerHandler(this.$el, DX_REMOVE_EVENT);
             instance.dispose();
@@ -46,27 +77,26 @@ const BaseComponent: VueConstructor = Vue.extend({
     },
 
     created(): void {
-
-        (this as any as IWidgetComponent).$_config = new Configuration(
-            (n: string, v: any) => (this as any as IWidgetComponent).$_instance.option(n, v),
+        (this as IBaseComponent).$_config = new Configuration(
+            (n: string, v: any) => this.$_instance.option(n, v),
             null,
             this.$options.propsData && { ...this.$options.propsData },
-            (this as any as IWidgetComponent).$_expectedChildren
+            this.$_expectedChildren
         );
 
-        (this as any as IWidgetComponent).$_config.init(this.$props && Object.keys(this.$props));
+        this.$_config.init(this.$props && Object.keys(this.$props));
     },
 
     methods: {
         $_createWidget(element: any): void {
-            const config = (this as any as IWidgetComponent).$_config;
+            const config = this.$_config;
             const options: object = {
                 ...this.$_getIntegrationOptions(),
                 ...this.$options.propsData,
                 ...config.getInitialValues()
             };
-            const instance = new (this as any as IWidgetComponent).$_WidgetClass(element, options);
-            (this as any as IWidgetComponent).$_instance = instance;
+            const instance = new this.$_WidgetClass(element, options);
+            (this as IBaseComponent).$_instance = instance;
 
             instance.on("optionChanged", (args) => config.onOptionChanged(args));
             subscribeOnUpdates(config, this);
@@ -128,7 +158,13 @@ const BaseComponent: VueConstructor = Vue.extend({
                 render: (data: any) => {
                     const vm = new Vue({
                         name,
+                        inject: ["eventBus"],
                         parent: this,
+                        created() {
+                            (this as IEventBusHolder).eventBus.$on("updated", () => {
+                                this.$forceUpdate();
+                            });
+                        },
                         render: () => template(data.model)
                     }).$mount();
 
@@ -176,8 +212,8 @@ const DxComponent: VueConstructor = BaseComponent.extend({
     },
 
     mounted(): void {
-        (this as any).$_createWidget(this.$el);
-        (this as any as IWidgetComponent).$_instance.endUpdate();
+        this.$_createWidget(this.$el);
+        this.$_instance.endUpdate();
         this.$children.forEach((child: IExtension) => {
             if (child.$_isExtension) {
                 child.attachTo(this.$el);
diff --git a/src/core/configuration-component.ts b/src/core/configuration-component.ts
index e09c1f2..dc73966 100644
--- a/src/core/configuration-component.ts
+++ b/src/core/configuration-component.ts
@@ -22,7 +22,7 @@ interface IConfigurable extends IConfigurationOwner {
 const DxConfiguration: VueConstructor = Vue.extend({
 
     beforeMount() {
-        const config = (this.$vnode.componentOptions as any as IConfigurable).$_config;
+        const config = (this.$vnode.componentOptions as IConfigurable).$_config;
 
         config.init(Object.keys(this.$props));
         bindOptionWatchers(config, this);
diff --git a/src/core/extension-component.ts b/src/core/extension-component.ts
index 719afae..20c22cc 100644
--- a/src/core/extension-component.ts
+++ b/src/core/extension-component.ts
@@ -13,18 +13,18 @@ interface IExtensionComponentNode {
 const DxExtensionComponent: VueConstructor = BaseComponent.extend({
 
     created(): void {
-        (this as any).$_isExtension = true;
+        this.$_isExtension = true;
     },
 
     mounted() {
-        if (this.$vnode && (this.$vnode.componentOptions as any as IExtensionComponentNode).$_hasOwner) { return; }
+        if (this.$vnode && (this.$vnode.componentOptions as IExtensionComponentNode).$_hasOwner) { return; }
 
         this.attachTo(this.$el);
     },
 
     methods: {
         attachTo(element: any) {
-            (this as any).$_createWidget(element);
+            this.$_createWidget(element);
         }
     }
 });


Test patch:
diff --git a/src/core/component.test.ts b/src/core/component.test.ts
index 18bc3d1..c12cdda 100644
--- a/src/core/component.test.ts
+++ b/src/core/component.test.ts
@@ -912,6 +912,30 @@ describe("template", () => {
         expect(component.$children.length).toBe(1);
     });
 
+    it("updates templates on component updating (check via functional component inside)", () => {
+        expect.assertions(2);
+        const FunctionalComponent = Vue.extend({
+            functional: true,
+            render(h) {
+                expect(true).toBeTruthy();
+                return h("div");
+            }
+        });
+        const vm = new Vue({
+            template: `<test-component ref="component">
+                            <div slot='item' slot-scope='props'><functional-component/></div>
+                        </test-component>`,
+            components: {
+                TestComponent,
+                FunctionalComponent
+            }
+        }).$mount();
+        renderItemTemplate({});
+
+        const component: any = vm.$refs.component;
+        component.$forceUpdate();
+    });
+
     it("unwraps container", () => {
         new Vue({
             template: `<test-component>