Search code examples
arraysfunctionvue.jsvuejs2v-model

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


I'm working with a portfolio project, where users can upload a picture, provide a description below it, and then click the "add" button to add another image and description.

I'm trying to add a character counter to the description input, which is a textarea field. Usually I can add the name of the v-model into the function, and it works fine, but this textarea is in a for-loop, so I'm not sure how to get this function to work.

Template:

<div class="newPortfolioList">
    <div class="newPortfolioItem" v-for="(item, index) in this.portfolioItems" v-bind:key="index">
....
       <div class="newPortfolioDescription">
          <textarea v-model="item.portfolioDescription" @keyup='remaincharCount()' maxlength="1000" placeholder="Item Description..."></textarea>
       </div>

       <!--  Displaying the remaining characters -->
       <span style="text-align:left; padding: 10px;">{{ remaincharactersText }}</span>
    </div>
...         

Script:

export default {
    data () {
        return {
            portfolioItems:[],
            maxcharacter: 1000,
            remaincharactersText: "1000 characters remaining"            
        }
    },
    methods: {
        createPortfolioItem () {
            this.portfolioItems.push({
                portfolioDescription: ''
            })
        },
        remaincharCount () {
            if (this.foo.length > this.maxcharacter) {
              this.remaincharactersText = "Exceeded "+this.maxcharacter+" characters limit.";
            } else {
              var remainCharacters = this.maxcharacter - this.foo.length;
              this.remaincharactersText = remainCharacters + " characters remaining";
            }
        }
    }
}

Solution

  • 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