2

I try to append a couple of buttons with click event when the component is mounted:

      data() {
          return {
            show_word: true, 
            game: {
              //type: "memory, working memory",
              type: 1,
              name: "Symbols",
              start:false,
              total_combo:0,
              total_correct:0,
              total_incorrect:0,
              total_correct_in_a_row:0,
              max_correct_in_a_row:0,
              correct_percent:0,
              avg_time_for_ans:0,
              button: '<button @click="checkNumbers">click</button>',
              score_plus:0,
              score_multi:0,          
              d_timer:600,
              finish:false,
              positive: null,
              negative: null,
              numbers_array:[],
              abuse: {
                clicks:5,
                seconds:1,
                max:3,
                flag: false
              }
            },
          }
        },

methods:{
    playGame(){

          let tilesize = 50, tilecount = 6;

          $( '#numbersContainer' ).empty()

          let gRows = Math.floor($("#numbersContainer").innerWidth()/tilesize);
          let gCols = Math.floor($('#numbersContainer').innerHeight()/tilesize);

          let vals = _.shuffle(_.range(tilecount));
          let xpos = _.shuffle(_.range(gRows));
          let ypos = _.shuffle(_.range(gCols));

          console.log(vals);
          console.log(xpos);
          console.log(ypos);
          let $this = this;

          _.each(vals, function(d,i){
              var $newdiv = $('<button @click="checkNumbers('+(d+1)+')"/>').addClass("tile btn btn-info");
              $this.game.numbers_array.push(d+1)
              $newdiv.css({
                  'position':'absolute',
                  'left':(xpos[i] * tilesize)+'px',
                  'top':(ypos[i] * tilesize)+'px'
              }).appendTo( '#numbersContainer' ).html(d+1);  
          });
        },
        checkNumbers($val){
          console.log($val)

        },
}

This is the html that i getting:

<div id="numbersContainer" class="col-ms-3">
<button @click="checkNumbers(6)" class="tile btn btn-info" style="position: absolute; left: 250px; top: 250px;">6</button>
<button @click="checkNumbers(5)" class="tile btn btn-info" style="position: absolute; left: 150px; top: 150px;">5</button>
<button @click="checkNumbers(1)" class="tile btn btn-info" style="position: absolute; left: 100px; top: 0px;">1</button>
<button @click="checkNumbers(3)" class="tile btn btn-info" style="position: absolute; left: 50px; top: 100px;">3</button>
<button @click="checkNumbers(2)" class="tile btn btn-info" style="position: absolute; left: 200px; top: 200px;">2</button>
<button @click="checkNumbers(4)" class="tile btn btn-info" style="position: absolute; left: 0px; top: 50px;">4</button>
</div>

The problems is that the "click" event is not working. Seems like the "checkNumbers" function is not register, although it registered in my methods.

What is the right way to append html element with register events?

Thank !

2
  • 3
    You really don't want to be manipulating the DOM directly. You should change the data and let Vue update the DOM for you. Can you show your data declaration? Commented Apr 18, 2018 at 15:11
  • @acdcjunior - i added the data declaration Commented Apr 18, 2018 at 15:16

1 Answer 1

4

Don't manipulate the DOM directly, that's why you're using Vue in the first place. Use a combination of v-for and data() to manipulate state and have Vue populate the DOM.

Adding the @click="" within the direct DOM manipulation is going to cause the element to not be managed by Vue, in other words it won't have the Vue context.

const Game = {
  props: ["tilecount"],
  data() {
    return {
      tiles: []
    };
  },
  mounted() {
    this.tiles = _.shuffle(_.range(this.tilecount));
  },
  methods: {
    checkNumbers(val) {
      console.log(val);
    }
  }
};

const app = new Vue({
  el: "#app",
  components: {
    game: Game
  },
  data() {
    return {
      tilecount: 50
    };
  }
});
.game .number-container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
}

.game .number-container button {
  flex-basis: 20%;
  height: 50px
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>

<div id="app">
  <game :tilecount="tilecount" inline-template>
    <div class="game">
      <div ref="numberContainer" class="number-container">
        <button v-for="tile in tiles" @click="checkNumbers(tile)">  {{tile}}</button>
      </div>
    </div>
  </game>
</div>

Sign up to request clarification or add additional context in comments.

6 Comments

Ok... I think i got it. I will let you know. Thank you! :)
This is a great example! +1! Just as a side note, you can expand it to add styles as well. For eample: <button v-for="(tile, index) in tiles" @click="checkNumbers(tile)" :style="{position: 'absolute', left: '250px', top: (index * 50) + 'px'}".
@acdcjunior True, but for this, I think I'd keep the style stuff shown in the OP in CSS and leverage flexbox. I can add something to show the style binding if you think it's appropriate.
@zero298 Yes, definitely. I actually didn't look into the specific case, just wanted to show the possibility, but I agree with you.
This is not always feasible. For example when you want to change position of the DOM element or have complete freedom of dynamically created DIVs the user can customize that are impossible to plan ahead because it's always very different. Let alone when you're also getting custom HTML from an API (that is also always with a different structure).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.