How to use emit and computed / watch

I'm new with VueJS and I started to develop a project to study a worksheet for launching hours and calculating hours worked.

I am using in the Laravel 5.5 project with Vuejs 2 and MomentJS to calculate the differences.

However I could not understand how to use emit to send out of my child component to the parent the updated data and nor how to make the field Trabalhado be updated alone with the calculation of moment(moment(this.entrada_1,"hh:mm").diff(moment(this.saida_1,"hh:mm"))).format("hh:mm"); (initially only the first difference to be able to learn, and then try to calculate the rest.

Example table Above is the table model I am developing and below the blade codes and json

// blade.php
<vue-appointment :timetable="{{ $timetable }}"></vue-appointment>
<!-- timetable é o objeto abaixo -->

// jsonObject
{
   days: [
     { numero: 1, nome: "QUI", entrada_1: "00:00:00", saida_1: "00:00:00", trabalhado: "00:00:00" //...},
     { numero: 2, nome: "SEX", entrada_1: "00:00:00", saida_1: "00:00:00", trabalhado: "00:00:00" //...}
     { numero: 3, nome: "SÁB", entrada_1: "00:00:00", saida_1: "00:00:00", trabalhado: "00:00:00" //...}
   ]
}

Below is the example of my 2 components VueJS.

Apointment.view

<template>
<div class="row">
    <div class="col-xs-12">
        <div class="table-responsive">
            <table class="table table-condensed table-striped">
                <thead>
                    <tr>
                        <th width="20px">#</th>
                        <th width="20px" class="text-center">Dia</th>
                        <th class="text-center">Entrada</th>
                        <th class="text-center">Saída</th>
                        <th class="text-center">Entrada</th>
                        <th class="text-center">Saída</th>
                        <th class="text-center">Entrada</th>
                        <th class="text-center">Saída</th>
                        <th class="text-center">Entrada</th>
                        <th class="text-center">Saída</th>
                        <th class="text-center">Trabalhado</th>
                    </tr>
                </thead>
                <tbody>
                    <vue-appointment-row v-for="day in timetable.days" :key="day.virtual_id" :day=day></vue-appointment-row>
                </tbody>
            </table>
        </div>
    </div>
</div>
</template>

<script>
import {TheMask} from 'vue-the-mask'
import { Moment } from 'moment'

export default {

    props: ['timetable', 'old'],

    components: { TheMask },

    data () {
        return {
            object: {}
        }
    },

    mounted() {
        console.log(this.timetable);
    },

    methods: {

    },

    computed: {
    }

}
</script>

AppointmentRow.view

<template>
<tr>
    <td>{{ day.numero }}</td>
    <td>{{ day.nome }}</td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_1]'" :masked="true" required v-model="day.entrada_1"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_1]'" :masked="true" required v-model="day.saida_1"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_2]'" :masked="true" required v-model="day.entrada_2"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_2]'" :masked="true" required v-model="day.saida_2"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_3]'" :masked="true" required v-model="day.entrada_3"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_3]'" :masked="true" required v-model="day.saida_3"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_4]'" :masked="true" required v-model="day.entrada_4"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_4]'" :masked="true" required v-model="day.saida_4"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][trabalhado]'" :masked="true" required v-model="day.trabalhado"></the-mask></td>
</tr>
</template>

<script>
import {TheMask} from 'vue-the-mask'
import { Moment } from 'moment'

export default {

    props: ['day'],

    components: { TheMask },

    data () {
        return {
            object: {}
        }
    },

    mounted() {
    },

    methods: {
    },

    computed: {}

}
</script>

An explanation of how to use computed / watch in this situation to update The worked as it is editing the line, would help me understand better how this process of computed / watch works.

Author: Tafarel Chicotti, 2018-02-22

1 answers

If I understood correctly, in this case in my opinion the best approach is using sync Modifier.

Basically, for you to pass the value being typed into the "child" component, in the case of AppointmentRow.vue, to the "parent" component, the Appointment.vue, you can simply put the suffix .sync in use prop day.

Then, inside the AppointmentRow.vue component, you create a watch in this prop, where it will output the value being typed to the parent, follows below:

watch: {
  day (newValue) {
    this.$emit('update:day', newValue)
  }
}

On each change of the value day, watch will be executed. Each change of the value can be received per parameter. In turn, the typed value of each attribute changed to the "parent"component is being issued.

I performed your example like this:

Appointment.view

<template>
  <div class="row">
      <div class="col-xs-12">
          <div class="table-responsive">
              <table class="table table-condensed table-striped">
                  <thead>
                      <tr>
                          <th width="20px">#</th>
                          <th width="20px" class="text-center">Dia</th>
                          <th class="text-center">Entrada</th>
                          <th class="text-center">Saída</th>
                          <th class="text-center">Entrada</th>
                          <th class="text-center">Saída</th>
                          <th class="text-center">Entrada</th>
                          <th class="text-center">Saída</th>
                          <th class="text-center">Entrada</th>
                          <th class="text-center">Saída</th>
                          <th class="text-center">Trabalhado</th>
                      </tr>
                  </thead>
                  <tbody>
                    <tr v-for="day in days" :key="day.virtual_id">
                      <appointment-row :day.sync="day">                      
                      </appointment-row>
                      p {{ day }}
                    </tr>
                  </tbody>
              </table>
          </div>
      </div>
  </div>
</template>

<script>
  import AppointmentRow from './AppointmentRow'

  export default {
    props: ['timetable', 'old'],
    components: { AppointmentRow },
    data () {
      return {
        days: [
          { numero: 1, nome: 'QUI', entrada_1: '00:00:00', saida_1: '00:00:00', trabalhado: '00:00:00' },
          { numero: 2, nome: 'SEX', entrada_1: '00:00:00', saida_1: '00:00:00', trabalhado: '00:00:00' },
          { numero: 3, nome: 'SÁB', entrada_1: '00:00:00', saida_1: '00:00:00', trabalhado: '00:00:00' }
        ]
      }
    }
  }
</script>

AppointmentRow.view

<template>
  <div>
    <td>{{ day.numero }}</td>
    <td>{{ day.nome }}</td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_1]'" :masked="true" required v-model="day.entrada_1"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_1]'" :masked="true" required v-model="day.saida_1"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_2]'" :masked="true" required v-model="day.entrada_2"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_2]'" :masked="true" required v-model="day.saida_2"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_3]'" :masked="true" required v-model="day.entrada_3"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_3]'" :masked="true" required v-model="day.saida_3"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][entrada_4]'" :masked="true" required v-model="day.entrada_4"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][saida_4]'" :masked="true" required v-model="day.saida_4"></the-mask></td>
    <td class="text-center"><the-mask class="form-control" :mask="['##:##']" :name="'days['+day.numero+'][trabalhado]'" :masked="true" required v-model="day.trabalhado"></the-mask></td>
  </div>
</template>

<script>
  import { TheMask } from 'vue-the-mask'

  export default {
    props: ['day'],
    components: { TheMask },
    watch: {
      day (newValue) {
        this.$emit('update:day', newValue)
      }
    }
  }
</script>

In paragraph p {{ day }} you can check the change of the value, each time you are being typed inside the child component.

Another way to accomplish this, without the use of sync Modifier, is by using a v-model instead of prop day:

<appointment-row v-model="day"></appointment-row>

Within AppointmentRow.vue you perform the exchange from day to value in the entire code:

props: ['value'],

And change the $emit from watch:

this.$emit('input', newValue)

The operation will be the same, however if in the future you want to pass one more prop, and the value of this prop must be transmitted to the parent, there is no way to use it with the v-model, only with sync Modifier.

 1
Author: guastallaigor, 2018-02-22 03:59:05