Browse Source

[editor] Switch to using the composition API for the ResultTable component

Johan Åhlén 4 years ago
parent
commit
e3fa585ea1

+ 31 - 0
desktop/core/src/desktop/js/apps/editor/components/result/ResultTable.scss

@@ -0,0 +1,31 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+.result-grid {
+  position: relative;
+  height: 100%;
+  width: 100%;
+
+  &.grayed-out {
+    opacity: 0.5;
+
+    ::v-deep(.hue-table-container) {
+      overflow: hidden !important;
+    }
+  }
+}

+ 122 - 154
desktop/core/src/desktop/js/apps/editor/components/result/ResultTable.vue

@@ -20,7 +20,7 @@
   <div class="result-grid" :class="{ 'grayed-out': grayedOut }">
     <HueTable
       v-if="rows.length"
-      :columns="tableColumns"
+      :columns="columns"
       :rows="rows"
       :sticky-header="true"
       :sticky-first-column="true"
@@ -48,15 +48,15 @@
 </template>
 
 <script lang="ts">
+  import { computed, defineComponent, PropType, ref, toRefs, watch } from 'vue';
+
+  import './ResultTable.scss';
   import {
     EXECUTABLE_RESULT_UPDATED_TOPIC,
     EXECUTABLE_UPDATED_TOPIC,
     ExecutableResultUpdatedEvent,
     ExecutableUpdatedEvent
   } from 'apps/editor/execution/events';
-  import { defineComponent, PropType } from 'vue';
-
-  import { ResultMeta } from 'apps/editor/execution/api';
   import Executable, { ExecutionStatus } from 'apps/editor/execution/executable';
   import ExecutionResult, { ResultRow, ResultType } from 'apps/editor/execution/executionResult';
   import { Column } from 'components/HueTable';
@@ -70,192 +70,160 @@
     components: {
       HueTable
     },
-
     props: {
       executable: {
         type: Object as PropType<Executable>,
         required: true
       }
     },
-
-    setup() {
+    setup(props) {
+      const { executable } = toRefs(props);
       const subTracker = new SubscriptionTracker();
-      return { subTracker };
-    },
-
-    data() {
-      return {
-        grayedOut: false,
-        fetchedOnce: false,
-        hasResultSet: false,
-        streaming: false,
-        hasMore: false,
-        rows: [] as ResultRow[],
-        meta: [] as ResultMeta[],
+      const grayedOut = ref(false);
+      const fetchedOnce = ref(false);
+      const hasResultSet = ref(false);
+      const streaming = ref(false);
+      const hasMore = ref(false);
+
+      const rows = ref<ResultRow[]>([]);
+      const columns = ref<Column<ResultRow>[]>([]);
+
+      const lastRenderedResult = ref<ExecutionResult | null>(null);
+      const status = ref<ExecutionStatus | null>(null);
+      const type = ref(ResultType.Table);
+
+      const hasEmptyResult = computed<boolean>(
+        () =>
+          !rows.value.length &&
+          hasResultSet.value &&
+          status.value === ExecutionStatus.available &&
+          fetchedOnce.value
+      );
 
-        status: null as ExecutionStatus | null,
-        type: ResultType.Table,
-        images: [] as string[],
-        lastFetchedRows: [] as ResultRow[],
-        lastRenderedResult: null as ExecutionResult | null
-      };
-    },
+      const hasEmptySuccessResult = computed<boolean>(
+        () =>
+          !rows.value.length &&
+          !hasResultSet.value &&
+          status.value === ExecutionStatus.available &&
+          fetchedOnce.value
+      );
 
-    computed: {
-      hasEmptyResult(): boolean {
-        return (
-          !this.rows.length &&
-          this.hasResultSet &&
-          this.status === ExecutionStatus.available &&
-          this.fetchedOnce
-        );
-      },
+      const isExecuting = computed<boolean>(() => status.value === ExecutionStatus.running);
 
-      hasEmptySuccessResult(): boolean {
-        return (
-          !this.rows.length &&
-          !this.hasResultSet &&
-          this.status === ExecutionStatus.available &&
-          this.fetchedOnce
-        );
-      },
+      const isExpired = computed<boolean>(
+        () => !rows.value.length && status.value === ExecutionStatus.expired
+      );
 
-      isExecuting(): boolean {
-        return this.status === ExecutionStatus.running;
-      },
+      const isStreaming = computed<boolean>(
+        () => !rows.value.length && streaming.value && status.value !== ExecutionStatus.running
+      );
 
-      isExpired(): boolean {
-        return this.status === ExecutionStatus.expired && !this.rows.length;
-      },
+      const resetResultData = (): void => {
+        type.value = ResultType.Table;
 
-      isStreaming(): boolean {
-        return this.streaming && !this.rows.length && this.status !== ExecutionStatus.running;
-      },
+        fetchedOnce.value = false;
+        streaming.value = false;
+        hasMore.value = false;
 
-      tableColumns(): Column<ResultRow>[] {
-        return this.meta.map(({ name }, index) => ({
-          label: name,
-          key: index,
-          htmlValue: true
-        }));
-      }
-    },
+        rows.value = [];
+        columns.value = [];
+      };
 
-    watch: {
-      executable(): void {
-        this.handleResultChange();
-      }
-    },
+      const updateFromExecutable = (executable: Executable): void => {
+        status.value = executable.status;
+        hasResultSet.value = !!(executable.handle && executable.handle.has_result_set);
+        if (!hasResultSet.value) {
+          resetResultData();
+        }
+      };
 
-    mounted(): void {
-      this.subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, executable => {
-        if (this.executable && this.executable.id === executable.id) {
-          this.updateFromExecutable(executable);
+      const updateFromExecutionResult = (
+        executionResult: ExecutionResult,
+        refresh?: boolean
+      ): void => {
+        if (refresh) {
+          resetResultData();
         }
-      });
 
-      this.subTracker.subscribe<ExecutableResultUpdatedEvent>(
-        EXECUTABLE_RESULT_UPDATED_TOPIC,
-        (executionResult: ExecutionResult) => {
-          if (this.executable && this.executable.id === executionResult.executable.id) {
-            this.handleResultChange();
+        if (executionResult) {
+          fetchedOnce.value = executionResult.fetchedOnce;
+          hasMore.value = executionResult.hasMore;
+          type.value = executionResult.type || ResultType.Table;
+          streaming.value = executionResult.streaming;
+
+          if (!columns.value.length && executionResult.meta.length) {
+            columns.value = executionResult.meta.map(({ name }, index) => ({
+              label: name,
+              key: index,
+              htmlValue: true
+            }));
           }
-        }
-      );
-    },
 
-    unmounted(): void {
-      this.subTracker.dispose();
-    },
+          if (refresh) {
+            rows.value = executionResult.rows;
+          } else if (
+            executionResult.lastRows.length &&
+            rows.value.length !== executionResult.rows.length
+          ) {
+            rows.value.push(...executionResult.lastRows);
+          }
+        }
+      };
 
-    methods: {
-      I18n,
-      handleResultChange(): void {
-        if (this.executable && this.executable.result) {
-          const refresh = this.lastRenderedResult !== this.executable.result;
-          this.updateFromExecutionResult(this.executable.result, refresh);
-          this.lastRenderedResult = this.executable.result;
+      const handleResultChange = (): void => {
+        const result = executable.value?.result;
+        if (result) {
+          const refresh = lastRenderedResult.value !== executable.value.result;
+          updateFromExecutionResult(result as ExecutionResult, refresh);
+          lastRenderedResult.value = result;
         } else {
-          this.resetResultData();
+          resetResultData();
         }
-      },
+      };
 
-      async onScrollToEnd(): Promise<void> {
-        if (this.hasMore && !this.grayedOut && this.executable && this.executable.result) {
-          this.grayedOut = true;
+      const onScrollToEnd = async (): Promise<void> => {
+        if (hasMore.value && !grayedOut.value && executable.value && executable.value.result) {
+          grayedOut.value = true;
           try {
-            await this.executable.result.fetchRows({ rows: 100 });
+            await executable.value.result.fetchRows({ rows: 100 });
           } catch (e) {}
           defer(() => {
             // Allow executable events to finish before enabling the result scroll again
-            this.grayedOut = false;
+            grayedOut.value = false;
           });
         }
-      },
-
-      resetResultData(): void {
-        this.type = ResultType.Table;
-
-        this.fetchedOnce = false;
-        this.streaming = false;
-        this.hasMore = false;
-
-        this.images = [];
-        this.lastFetchedRows = [];
-        this.rows = [];
-        this.meta = [];
-      },
+      };
 
-      updateFromExecutable(executable: Executable): void {
-        this.status = executable.status;
-        this.hasResultSet = !!(executable.handle && executable.handle.has_result_set);
-        if (!this.hasResultSet) {
-          this.resetResultData();
-        }
-      },
+      watch(executable, handleResultChange);
 
-      updateFromExecutionResult(executionResult: ExecutionResult, refresh?: boolean): void {
-        if (refresh) {
-          this.resetResultData();
+      subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, updatedExecutable => {
+        if (executable.value?.id === updatedExecutable.id) {
+          updateFromExecutable(updatedExecutable);
         }
+      });
 
-        if (executionResult) {
-          this.fetchedOnce = executionResult.fetchedOnce;
-          this.hasMore = executionResult.hasMore;
-          this.type = executionResult.type || ResultType.Table;
-          this.streaming = executionResult.streaming;
-
-          if (!this.meta.length && executionResult.meta.length) {
-            this.meta = executionResult.meta;
-          }
-
-          if (refresh) {
-            this.rows = [...executionResult.rows];
-          } else if (
-            executionResult.lastRows.length &&
-            this.rows.length !== executionResult.rows.length
-          ) {
-            this.rows.push(...executionResult.lastRows);
+      subTracker.subscribe<ExecutableResultUpdatedEvent>(
+        EXECUTABLE_RESULT_UPDATED_TOPIC,
+        (executionResult: ExecutionResult) => {
+          if (executable.value?.id === executionResult.executable.id) {
+            handleResultChange();
           }
-          this.lastFetchedRows = executionResult.lastRows;
         }
-      }
+      );
+
+      return {
+        I18n,
+        columns,
+        grayedOut,
+        hasEmptyResult,
+        hasEmptySuccessResult,
+        isExecuting,
+        isExpired,
+        isStreaming,
+        onScrollToEnd,
+        rows,
+        subTracker
+      };
     }
   });
 </script>
-
-<style lang="scss" scoped>
-  .result-grid {
-    position: relative;
-    height: 100%;
-    width: 100%;
-
-    &.grayed-out {
-      opacity: 0.5;
-
-      ::v-deep(.hue-table-container) {
-        overflow: hidden !important;
-      }
-    }
-  }
-</style>