north.inhale
north.inhale

Reputation: 511

How to use array map inside v-for loop in Vue?

I'm trying to figure out how to make a loop work inside another loop in Vue. The task seems trivial when working with React, but in View I don't understand how I can use a standard hook when working with arrays inside a template/JSX.

By condition, the input data comes from the server in the given format. At the moment, the code in the attached snippet below does not work due to syntax errors.

Can you help me fix errors in the snippet? This will help me understand how to correctly apply a loop in a loop in a template relative to Vue...

const timeStamp = moment(new Date());
var app = new Vue({
  el: "#app",
  template: "#app-template",
  data: {
    symbols: [
      {
        id: 1,
        name: "EURUSD",
        candles: [
          {
            timeStamp: timeStamp.subtract(1, "days").format("YYYY-MM-DD"), // Yesterday
            open: 1.1,
            close: 1.2,
            high: 1.3,
            low: 1.0,
          },
          {
            timeStamp: timeStamp.format("YYYY-MM-DD"), // Today
            open: 1.2,
            close: 1.5,
            high: 1.6,
            low: 1.2,
          }
        ]
      },
      {
        id: 2,
        name: "USDRUB",
        history: [
          {
            timeStamp: timeStamp.subtract(1, "days").format("YYYY-MM-DD"), // Yesterday
            open: 75,
            close: 76,
            high: 77,
            low: 73,
          },
          {
            timeStamp: timeStamp.subtract(1, "days").format("YYYY-MM-DD"), // Today
            open: 76,
            close: 77,
            high: 79,
            low: 75,
          }
        ]
      }
    ]
  }
});
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/x-template" id="app-template">
  <table>
    <thead>
      <tr>
        <th>Symbol</th>
        <th>Change</th>
        <th>Time stamp</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(symbol, index) in symbols" :key="index">
        {
          symbol.candles.map(candle => {
            const { name } = symbol
            const { open } = candle.history[0]
            const { close, timeStamp } = candle.history[1]
            const change = Number.parseFloat((100 / (open / (close - open)))).toFixed(2)

            return (
              <td>{{ name }}</td>
              <td>{{ change }} %</td>
              <td>{{ timeStamp }}</td>
            )
          })
        }
      </tr>
    </tbody>
  </tabel>
</script>

Upvotes: 4

Views: 19638

Answers (2)

Hannah
Hannah

Reputation: 1143

You are missing the closing </table> tag, first of all (you have a typo: </tabel>).

Also, I would recommend an approach like this:

    <tr v-for="(symbol, index) in symbols" :key="index">
      <template v-for="candle in symbol.candles">
        <td>{{ symbol.name }}</td>
        <td>{{ getChange(candle) }} %</td>
        <td>{{ candle.history[1].timeStamp }}</td>
      </template>
    </tr>

    ...

    methods: {
        getChange(candle) {
            const { open } = candle.history[0];
            const { close } = candle.history[1];
            return Number.parseFloat((100 / (open / (close - open)))).toFixed(2);
        }
    }

The <template> tag can be used in cases like this, where you want to repeat a block of code without adding an extra parent tag around it (as <template> does not render as an actual HTML tag).

The reason I suggest moving the logic outside of the template and into a method is that Vue only supports one expression per data binding (source: https://v2.vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions).

Upvotes: 4

north.inhale
north.inhale

Reputation: 511

Thank you for your answers, I have corrected the code in the snippet according to your recommendations. The code now works as expected. The main thing I learned is that the Vue <template> component is analogous to the React component of the <Fragment>

document.addEventListener('DOMContentLoaded', function() {
    const timeStamp = moment(new Date());
    var app = new Vue({
        el: "#app",
        template: "#app-template",
        data: {
            symbols: [
                {
                    id: 1,
                    name: "EURUSD",
                    candles: [
                        {
                            timeStamp: timeStamp
                                .subtract(1, "days")
                                .format("YYYY-MM-DD"), // Yesterday
                            open: 1.1,
                            close: 1.2,
                            high: 1.3,
                            low: 1.0,
                        },
                        {
                            timeStamp: timeStamp.format("YYYY-MM-DD"),
                            open: 1.2,
                            close: 1.5,
                            high: 1.6,
                            low: 1.2,
                        },
                    ],
                },
                {
                    id: 1,
                    name: "USDRUB",
                    candles: [
                        {
                            timeStamp: timeStamp
                                .subtract(1, "days")
                                .format("YYYY-MM-DD"),
                            open: 75,
                            close: 76,
                            high: 77,
                            low: 73,
                        },
                        {
                            timeStamp: timeStamp.format("YYYY-MM-DD"),
                            open: 76,
                            close: 77,
                            high: 79,
                            low: 75,
                        },
                    ],
                },
            ],
        },
        methods: {
            moment: moment,
            getChange(candle) {
                const { open, close } = candle;
                return Number.parseFloat((100 / (open / (close - open)))).toFixed(2);
            }
        }
    });
});
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/x-template" id="app-template">
    <table>
        <thead>
            <tr>
                <th>Symbol</th>
                <th>Change</th>
                <th>Time stamp</th>
            </tr>
        </thead>
        <tbody>
            <template v-for="symbol in symbols">
                <tr v-for="(candle, index) in symbol.candles" :key="symbol.name + '-' + index">
                    <td>{{ symbol.name }}</td>
                    <td>{{ getChange(candle) }} %</td>
                    <td>{{ moment(candle.timeStamp).format("YYYY-MM-DD") }}</td>
                </tr>
            </template>
        </tbody>
    </table>
</script>
<div id="app"></div>

Upvotes: 0

Related Questions