Vue.js即使没有呈现组件也会出现错误

时间:2020-07-25 13:19:16

标签: javascript vue.js vuetify.js

我有一个仅在有数据要显示时才呈现的组件。

<v-row v-if="openPositions.length !== 0" justify="center" align="end" class="mt-1">

这很好用。

但是,即使未渲染此组件,也会打印错误消息,因为在此组件中未定义某些数据,所以这就是为什么我不首先显示表的原因。

如何停止这些错误消息?

=====编辑=====

在下面您可以看到完整的组件代码,然后在控制台下看到该错误。

<template>
  <v-row v-if="openPositions.length" justify="center" align="end" class="mt-1">
    <v-col sm="12">
      <v-data-table
      
        v-if="this.full"
        dense
        align="center"
        :headers="headers_full"
        :items="openPositions"
        hide-default-footer
      >
        <template v-slot:item.symbol="{ item }">
          <span>{{ item.symbol }}</span>
        </template>
        <template v-slot:item.size="{ item }">
          <span v-if="item.side === 'sell'" class="error--text">{{
            item.size
          }}</span>

          <span v-if="item.side === 'buy'" class="success--text">{{
            item.size
          }}</span>
        </template>

        <template v-slot:item.position_value="{ item }">
          <span>
            {{ item.position_value.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.entry_price="{ item }">
          <span>
            {{ item.entry_price.toFixed(2) }}
          </span>
        </template>

        <template v-slot:item.liq_price="{ item }">
          <span>
            {{ item.liq_price.toFixed(2) }}
          </span>
        </template>

        <template v-slot:item.position_margin="{ item }">
          <span>
            {{ item.position_margin.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.unrealised_pnl_last="{ item }">
          <span v-if="item.unrealised_pnl_last < 0" class="error--text">
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
          <span v-else-if="item.unrealised_pnl_last > 0" class="success--text">
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.realised_pnl="{ item }">
          <span v-if="item.realised_pnl < 0" class="error--text">
            {{ item.realised_pnl.toFixed(6) }}
          </span>
          <span v-else-if="item.realised_pnl > 0" class="success--text">
            {{ item.realised_pnl.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.realised_pnl.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.daily_total="{ item }">
          <span v-if="item.daily_total < 0" class="error--text">
            {{ item.daily_total.toFixed(6) }}
          </span>
          <span v-else-if="item.daily_total > 0" class="success--text">
            {{ item.daily_total.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.daily_total.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.market_close="{ item }">
          <v-btn x-small color="primary" @click="marketClose(item)">
            Close
          </v-btn>
        </template>
      </v-data-table>
      <v-data-table
        v-else
        dense
        align="center"
        :headers="headers_reduced"
        :items="openPositions"
        hide-default-footer
      >
        <template v-slot:item.symbol="{ item }">
          <span>{{ item.symbol }}</span>
        </template>
        <template v-slot:item.size="{ item }">
          <span v-if="item.side === 'sell'" class="error--text">{{
            item.size
          }}</span>

          <span v-if="item.side === 'buy'" class="success--text">{{
            item.size
          }}</span>
        </template>

        <template v-slot:item.position_value="{ item }">
          <span>
            {{ item.position_value.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.entry_price="{ item }">
          <span>
            {{ item.entry_price.toFixed(2) }}
          </span>
        </template>

        <template v-slot:item.liq_price="{ item }">
          <span>
            {{ item.liq_price.toFixed(2) }}
          </span>
        </template>

        <template v-slot:item.position_margin="{ item }">
          <span>
            {{ item.position_margin.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.unrealised_pnl_last="{ item }">
          <span v-if="item.unrealised_pnl_last < 0" class="error--text">
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
          <span v-else-if="item.unrealised_pnl_last > 0" class="success--text">
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.unrealised_pnl_last.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.realised_pnl="{ item }">
          <span v-if="item.realised_pnl < 0" class="error--text">
            {{ item.realised_pnl.toFixed(6) }}
          </span>
          <span v-else-if="item.realised_pnl > 0" class="success--text">
            {{ item.realised_pnl.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.realised_pnl.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.daily_total="{ item }">
          <span v-if="item.daily_total < 0" class="error--text">
            {{ item.daily_total.toFixed(6) }}
          </span>
          <span v-else-if="item.daily_total > 0" class="success--text">
            {{ item.daily_total.toFixed(6) }}
          </span>
          <span v-else>
            {{ item.daily_total.toFixed(6) }}
          </span>
        </template>

        <template v-slot:item.market_close="{ item }">
          <v-btn x-small color="primary" @click="marketClose(item)">
            Close
          </v-btn>
        </template>
      </v-data-table>
    </v-col>
  </v-row>
</template>

<script>
import store from "../store";
export default {
  store,
  name: "OpenPositions",
  components: {},
  props: [],
  data() {
    return {
      dialog: false,
      headers_full: [
        { text: "Open Position", value: "symbol" },
        { text: "Qty", value: "size" },
        { text: "Value", value: "position_value" },
        { text: "Price", value: "entry_price" },
        { text: "Liq. Price", value: "liq_price" },
        { text: "Margin", value: "position_margin" },
        { text: "Leverage", value: "leverage" },
        {
          text: "Unrealized P&L",
          value: "unrealised_pnl_last",
        },
        { text: "Daily Realized P&L", value: "realised_pnl" },
        { text: "Daily Total (% of Account)", value: "daily_total" },
        { text: "SL", value: "stop_loss" },
        { text: "TP", value: "take_profit" },
        { text: "TS", value: "trailing_stop" },
        { text: "Stops", value: "trading_stops" },
        { text: "Market close", value: "market_close" },
      ],
      headers_reduced: [
        { text: "Open Position", value: "symbol" },
        { text: "Qty", value: "size" },
        { text: "Value", value: "position_value" },
        { text: "Price", value: "entry_price" },
        { text: "Liq. Price", value: "liq_price" },
        { text: "Margin", value: "position_margin" },
        { text: "Leverage", value: "leverage" },
        {
          text: "Unrealized P&L",
          value: "unrealised_pnl_last",
        },
        { text: "Daily Realized P&L", value: "realised_pnl" },
        { text: "Daily Total (% of Account)", value: "daily_total" },
        { text: "Market close", value: "market_close" },
      ],
    };
  },
  methods: {
    async marketClose(item) {
      await this.$apiAbstraction.marketOrder(
        item.symbol,
        item.side === "buy" ? "sell" : "buy",
        Math.abs(item.size)
      );
    },
  },
  computed: {
    openPositions() {
      return store.getters.getOpenPositionsByExchange(
        store.getters.getExchange
      );
    },
    full() {
      return store.getters.getExchange !== "deribit";
    },
  },
  mounted() {},
};
</script>

<style scoped></style>

错误:

[Vue warn]: Error in render: "TypeError: item.position_value.toFixed is not a function"

found in

---> <VData>
       <VDataTable>
         <OpenPositions> at src/components/OpenPositions.vue
           <Ladder> at src/views/Ladder.vue
             <VMain>
               <VApp>
                 <App> at src/App.vue
                   <Root>
warn @ vue.runtime.esm.js?2b0e:619
logError @ vue.runtime.esm.js?2b0e:1884
globalHandleError @ vue.runtime.esm.js?2b0e:1879
handleError @ vue.runtime.esm.js?2b0e:1839
Vue._render @ vue.runtime.esm.js?2b0e:3550
updateComponent @ vue.runtime.esm.js?2b0e:4066
get @ vue.runtime.esm.js?2b0e:4479
run @ vue.runtime.esm.js?2b0e:4554
flushSchedulerQueue @ vue.runtime.esm.js?2b0e:4310
eval @ vue.runtime.esm.js?2b0e:1980
flushCallbacks @ vue.runtime.esm.js?2b0e:1906
Promise.then (async)
timerFunc @ vue.runtime.esm.js?2b0e:1933
nextTick @ vue.runtime.esm.js?2b0e:1990
queueWatcher @ vue.runtime.esm.js?2b0e:4402
update @ vue.runtime.esm.js?2b0e:4544
notify @ vue.runtime.esm.js?2b0e:730
reactiveSetter @ vue.runtime.esm.js?2b0e:1055
setOpenPositions @ exchanges.js?d86e:169
wrappedMutationHandler @ vuex.esm.js?2f62:840
commitIterator @ vuex.esm.js?2f62:462
eval @ vuex.esm.js?2f62:461
_withCommit @ vuex.esm.js?2f62:620
commit @ vuex.esm.js?2f62:460
boundCommit @ vuex.esm.js?2f62:405
handleOnMessage @ deribitApi.js?89c3:141
_this.ws.onmessage @ deribitApi.js?89c3:59
ReconnectingWebSocket._handleMessage @ reconnecting-websocket-mjs.js?d096:172
vue.runtime.esm.js?2b0e:1888 TypeError: item.position_value.toFixed is not a function
    at fn (eval at ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"7c63af54-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/OpenPositions.vue?vue&type=template&id=3ad75d40&scoped=true& (app.js:1010), <anonymous>:279:64)
    at normalized (vue.runtime.esm.js?2b0e:2590)
    at eval (Row.ts?6e51:31)
    at Array.map (<anonymous>)
    at render (Row.ts?6e51:22)
    at createFunctionalComponent (vue.runtime.esm.js?2b0e:3058)
    at createComponent (vue.runtime.esm.js?2b0e:3231)
    at _createElement (vue.runtime.esm.js?2b0e:3434)
    at createElement (vue.runtime.esm.js?2b0e:3353)
    at VueComponent.vm.$createElement (vue.runtime.esm.js?2b0e:3494)

1 个答案:

答案 0 :(得分:1)

编辑(看到实际错误后):

问题是您要在非数字的内容上使用.toFixed。为了防止这种情况,您必须将值包装在Number中。

Number(item.daily_total || 0).toFixed(6)

此外,您的item.daily-total模板槽可以显着简化为:

<template v-slot:item.daily_total="{ value }">
  <span :class="{ 'error--text': value < 0, 'success--text': value > 0 }"
        v-text="Number(value || 0).toFixed(6)" />
</template>

显然,您还应该对其他模板使用类似的方法。

由于您在多个地方使用了它,因此可以将代码干燥并使用Vue组件来应用成功/错误类:

<template>
  <span :class="cellClass"
         v-text="Number(value || 0).toFixed(toFixed || 2)" />
</template>
<script>
  export default Vue.extend({
    name: 'CellValue',
    props: ['value', 'item', 'header', 'toFixed'],
    computed: {
      cellClass() {
        return {
          'error--text': this.value < 0,
          'success--text': this.value > 0
        }
      }
    }
  })
</script>

...将其作为Vue组件导入,并在您的表模板中以如下方式使用:

<template v-slot:item.daily_total="cell">
  <cell-value v-bind="cell" :to-fixed="6" />
</template>

您不需要将itemheader声明为道具,但是如果需要它们,它们由Vuetify插槽提供。
注意,我还添加了一个额外的prop,称为to-fixed,默认为2,它确定小数位数。您可以将其重命名为decimals
另请注意,我默认将value设置为0,以避免在非.toFixed()类型的对象上使用number时出现任何错误。
例如,如果您希望在值是number以外的其他任何类型时显示自定义文本,则可以在CellValue的模板中使用更详细的说明:

<template>
  <span :class="cellClass">
    <template v-if="typeof value === 'number'">
      {{ value.toFixed(toFixed || 2) }}
    </template>
    <template v-else>
      <code>{{ value || '--' }}</code>
    </template>
  </span>
</template>

作为参考,我在这里留下初始答案,因为这可能会对其他人有所帮助。

每当您的模板依赖于一个表达式,由于它试图访问undefined实体的属性/方法而在某些情况下会出错时,最简单的解决方法是创建一个computed属性,以避免出现以下异常:< / p>

<template>

<v-row v-if="hasOpenPositions" justify="center" align="end" class="mt-1">

<script>

computed: {
  hasOpenPositions() {
    return this.openPositions && this.openPositions.length !== 0;
  }
}

不过,请注意,当true为真且不具有openPositions属性(当其length为{{1时),上述计算将返回length }};即:undefined-因为openPositions: {})。

我发现自己使用的另一种较短的语法是:

undefined !== 0

(当computed: { hasOpenPositions() { return !!(this.openPositions?.length); } } 有一个真实的true时,这只返回openPositions,这可能是您想要的)。

请注意,至少到目前为止,optional chaininglength)在?.中不起作用。


上面是用于深层对象的常规解决方法,因此当尚未填充/加载对象时,模板不会出错。

但是,如果<template>是一个数组,则应将其实例化为空数组并将检查简化为:

openPositions

<template>

<v-row v-if="openPositions.length" justify="center" align="end" class="mt-1">

<script>

此处的相关位是data: () => ({ openPositions: [] }), mounted() { this.$http.get('some/api').then(r => { this.openPositions = r.data }) } 始终是一个数组(因此,它始终具有openPositions内部方法),并且它是反应性的。无关紧要(用length或其他方法)