runningmark
runningmark

Reputation: 760

All dynamically generated components are changing to the same value in VUEJS

we are building a chat application in Vuejs, now every chat message is component in our application, now whenever we are changing the value of one chat message, the value of all chat messages changes

What is happening

enter image description here

source code

App Component

const App = new Vue({
      el: '#myApp',
      data: {
        children: [
          MyCmp
        ],
        m1: '',
        m2: '',
        m3: 'Hello world',
        m4: 'How are you'
      },
      methods: {
        sendMessage (event) {
          if(event.key == "Enter") {
            this.m2= this.m3;
            this.children.push(MyCmp);
          }
        },
      }
    });

component code

let MyCmp = {
      props: ['myMessage'],
      template: `
      <li class="self">
          <div class="avatar"><img src="" draggable="false"/></div>
          <div class="msg">
            <p>{{ myMessage }}</p>
          </div>
      </li>
      `
    };

** view where components are generating **

<ol class="chat">
        <template v-for="(child, index) in children">
          <component :is="child" :key="child.name" v-bind="{myMessage: m3}"></component>
        </template>
    </ol>

Upvotes: 0

Views: 458

Answers (2)

ParaBolt
ParaBolt

Reputation: 1863

Even though you are creating new components by pushing them into the children array, they are still getting bound to the same data via the line v-bind="{myMessage: m3}". Whenever you change m3, it will be passed down to all the child components and they will all render the same message.

This is an odd way of creating custom components since you could easily do so using the templating syntax or render function provided by Vue.

Solution 1 - Recommended

Change your approach - push message strings instead of card component definitions into children and use MyCmp inside v-for to render the message cards.

So the new template could be refactored as :-

<ol class="chat">
    <template v-for="(message, index) in children">
        <my-cmp :key="index" :my-message="message"></my-cmp>
    </template>
</ol>

And inside App component, you can replace this.children.push(MyCmp) with this.children.push(messageVariable); where messageVariable contains the message that you receive from the input box.

Why is the recommended? This is a standard approach where component lists are rendered based on an array of data. It will be easier to scale and maintain.

Solution 2

You can use the v-once directive to bind the message one-time as static text. The binding won't be updated even if m3 changes on the parent.

Then MyCmp template will become :-

<li class="self">
    <div class="avatar"><img src="" draggable="false"/></div>
        <div class="msg">
        <p v-once>{{ myMessage }}</p>
    </div>
</li>

Upvotes: 1

Lana
Lana

Reputation: 1267

You bind myMessage of all your components instances with one variable m3. So, when m3 is changed myMessage in all instances changes respectively. Use another variable (e.g. msg) for rendering the message and then use myMessage property only for the initialisation of msg, like this:

let MyCmp = {
  props: ['myMessage'],
  data: function () {
    return {
      msg: this.myMessage
    }
  },
  template: `
    <li class="self">
      <div class="avatar"><img src="" draggable="false"/></div>
        <div class="msg">
          <p>{{ msg }}</p>
        </div>
    </li>
   `
};

Upvotes: 0

Related Questions