[Vuejs]-(Vue.js) V-model array. Having difficulty running function

1👍

You should be aware that the textarea already has its maxlength set to 1000, so the label Exceeded N characters limit isn’t possible (unless you check for fewer than 1000). Currently, the label always would display N reminaing characters.

Option 1: Display count calculation inline

Instead of storing the character count (unnecessarily taking up extra memory), you could display the calculation inline with string interpolation:

<template>
  <div>
    <textarea v-model="item.portfolioDescription" maxlength="1000"></textarea>
    <span>{{ 1000 - item.portfolioDescription.length }} remaining characters</span>
  </div>
</template>

demo 1

Option 2: Display count from item variable

If you prefer storing the character count (e.g., for some internal processing), you could add a new property to the item data:

<script>
const MAXLEN = 1000

export default {
  methods: {
    createPortfolioItem() {
      this.portfolioItems.push({
        remainChars: MAXLEN, // <--
      })
    },
  }
}
</script>

Then, update item.remainChars upon the textarea‘s input-event, and display item.remainChars inline.

<template>
  <div>
    <textarea v-model="item.portfolioDescription" maxlength="1000"
              @input="item.remainChars = 1000 - item.portfolioDescription.length">
    </textarea>
    <span>{{ item.remainChars }} remaining characters</span>
  </div>
</template>

demo 2

Option 3: Display computed text

You could compute the character-count labels in a separate array that corresponds to portfolioItems:

<script>
const MAXLEN = 1000

export default {
  computed: {
    remainingCharsText() {
      return this.portfolioItems.map(item => `${MAXLEN - item.portfolioDescription.length} remaining characters`)
    },
  }
}
</script>

Then, update your template to reference this computed array by index:

<template>
  <div>
    <textarea v-model="item.portfolioDescription" maxlength="1000">
    </textarea>
    <span>{{ remainingCharsText[index] }}</span>
  </div>
</template>

demo 3

👤tony19

2👍

There are a number of ways you could do this.

In this case I think your best option is to introduce a new component to represent a single portfolio item. Each of these components can manage their own message. From their perspective there is no loop to consider.

So you’d have something like this for your list template:

<div class="newPortfolioList">
    <my-new-portfolio-item
        v-for="(item, index) in portfolioItems"
        :key="index"
        :portfolio-item="item"
    />
</div>

Two side notes. I’ve dropped the v-bind prefix on your key, a : will suffice. I’ve also removed the this. before portfolioItems as that’s also unnecessary. The Vue linting rules can be used to help keep this stuff in check.

There are alternatives to introducing a new component. You could generate the value of remaincharactersText within the template rather than keeping it in state. It could still be a method call but it wouldn’t be precalculated. Something like this:

<span style="text-align:left; padding: 10px;">{{ remaincharCount(item) }}</span>

A further (even more painful) alternative would be to make remaincharactersText an array of values and then grab the relevant one by index:

<span style="text-align:left; padding: 10px;">{{ remaincharactersText[index] }}</span>

But, to reiterate, introducing a separate component for the items within the v-for is probably the best way to go here.

Leave a comment