IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VueJS Discussion :

Modification de la propriété "props", comment faire autrement


Sujet :

VueJS

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2007
    Messages
    282
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Gers (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Octobre 2007
    Messages : 282
    Par défaut Modification de la propriété "props", comment faire autrement
    Bonsoir à tous

    Je suis en train de tester VueJs avec un cas concret, la création d'un datepicker et timepicker.
    Je suis face à une erreur lié à une mutation d'une propriété "props". J'ai très bien compris qu'il était complètement déconseillé de faire une telle mutation, mais le soucis c'est que j'ai du mal à voir comment faire autrement.

    Sur la page index.html (je suis en local), j'utilise le code suivant:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    <datepicker value="2015-02-02 22:10:00" format="DD-MM-YYYY HH:mm" name="date"></datepicker>
    Voici mon component qui permet de charger le futur datepicker : Datepicker.vue

    Code vuejs : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    <style>
      .datepicker__agenda{
        position:relative;
      }
    </style>
     
    <template>
      <div class="datepicker__agenda">
        <input type="text" :value="date_formatted">
        <input type="text" :name="name" :value="date_raw">
        <datepicker-agenda :date="date"></datepicker-agenda>
      </div>
    </template>
     
    <script>
    import moment from 'moment'
    import DatepickerAgendaComponent from './DatepickerAgenda'
    moment.locale('fr')
     
    export default {
      components: {
        'datepicker-agenda': DatepickerAgendaComponent
      },
      props: {
        value: {type: String, required: true},
        format: {type: String, default: 'YYYY-MM-DD hh:mm:ss'},
        name: {type: String}
      },
      data: function () {
        return {
          date: moment(this.value, 'YYYY-MM-DD hh:mm:ss')
        }
      },
      computed: {
        date_formatted: function () {
          return this.date.format(this.format)
        },
        date_raw: function () {
          return this.date.format('YYYY-MM-DD HH:mm:ss')
        }
      }
     
    }
    </script>

    Et enfin de code qui pose problème. Fichier DatepickerAgenda.vue
    Code vuejs : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    <style lang="scss">
      $header-height: 60px;
      $day-size: 41px;
      .datepicker{
        font-family: 'Roboto', sans-serif;
        position: absolute;
        top:100%;
        width: 315px;
        z-index:5;
        background-color: #fff;
        box-shadow: 0 14px 45px rgba(0,0,0,0.25), 0 10px 18px rgba(0,0,0,0.22);
      }
      .datepicker__header{
        background-color: #0097a7;
        color: #fff;
        padding: 20px;
        height: $header-height;
      }
      .datepicker__year{
        opacity: 0.7;
        margin-bottom: 10px;
        line-height: 16px;
      }
      .datepicker__date{
        font-size: 32px;
        line-height: 32px;
      }
      .datepicker__week{
        line-height: 12px;
        font-style: 12px;
        color: rgba(0,0,0,0.8);
        padding: 0 14px;
      }
      .datepicker__weekday{
        float: left;
        width: $day-size;
        text-align: center;
      }
      .datepicker__days{
        margin: 14px;
      }
      .datepicker__day{
        position: relative;
        width: $day-size;
        height: $day-size;
        line-height: $day-size;
        cursor: pointer;
        float: left;
        text-align: center;
        transition: all 450ms cubic-bezier(0.23,1,0.32,1);
      }
      .datepicker__day__effect{
        position: absolute;
        top:2px;
        left: 2px;
        background-color: rgb(0,151,167);
        width: 36px;
        height: 36px;
        border-radius: 50%;
        transition: all 450ms cubic-bezier(0.23,1,0.32,1);
        transform: scale(0);
        opacity: 0;
      }
      .datepicker__day__text{
        position: relative;
      }
      .datepicker__day:hover{
        color: #fff;
        .datepicker__day__effect{
          transform: scale(1);;
          opacity: 0.6;
        }
      }
      .datepicker__day.selected{
        color: #fff;
        .datepicker__day__effect{
          transform: scale(1);
          opacity: 1
        }
      }
    </style>
     
    <template>
      <div class="datepicker">
        <div class="datepicker__header">
          <div class="datepicker__year">
            {{ year }}
          </div>
          <div class="datepicker__date">
            {{ date_formatted }}
          </div>
        </div>
        <div class="datepicker__week">
          <div v-for="day in days" track-by="$index" :key="day.id" class="datepicker__weekday">
            {{ day }}
          </div>
        </div>
        <div class="datepicker__days">
          <div class="datepicker__day" v-bind:style="{width: ( month.getWeekStart() * 41 ) + 'px' }"></div>
          <div class="datepicker__day" v-on:click="selectDate(day)" v-for="day in month.getDays()" :key="day.id" :class="{selected: isSelected(day)}">
            <span class="datepicker__day__effect"></span>
            <span class="datepicker__day__text">{{ day.format('D') }}</span>
          </div>
        </div>
      </div>
    </template>
     
    <script>
    import Month from '../modules/month'
    export default {
      props: ['date'],
      data () {
        return {
          days: ['L', 'M', 'M', 'J', 'V', 'S', 'D'],
          month: new Month(this.date.month(), this.date.year())
        }
      },
      methods: {
        isSelected: function (day) {
          return this.date.set({h: 1, m: 1}).unix() === day.set({h: 1, m: 1}).unix()
        },
        selectDate: function (day) {
          this.date = day
        }
      },
      computed: {
        year () {
          return this.date.format('Y')
        },
        date_formatted () {
          return this.date.format('dddd DD MMM')
        }
      }
    }
    </script>

    En fait, ce que je souhaite, c'est que lorsque je clique sur date, cette dernière change dans mon début de datepicker. Même si cela fonctionne, cela va en l'encontre des propriétés "props". Pourriez vous m'aider?

    Merci d'avance.

  2. #2
    Membre Expert
    Avatar de gwyohm
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2007
    Messages
    925
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2007
    Messages : 925
    Par défaut
    Salut,

    J'ai pas tout détaillé dans ton code, mais la réponse globale, c'est $emit
    Si effectivement, un composant ne peux pas modifier ses props, il peut émettre un événement pour notifier le composant parent. Charge au composant parent de modifier une valeur de ses data (ou d'émettre un autre événement, ou de faire un dispatch sur le store, ou...) ce qui éventuellement mettra à jour la valeur de la prop.


    Prenons un exemple simple:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <template>
    <div class="dayPicker">
      <a
       v-for="day in days"
       :key="day"
       @click="selectDay(day)"
       :class="{'isSelected': day === value}"
     >
       {{day}}
      </a>
    <div>
    </template>
    <script>
    export default {
      name: 'DayPicker',
      data() {
        return {
          days: ['Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa', 'Di']
        };
      },
      props: {
        value: {
          type: String,
          default: null,
      },
      methods: {
        selectDay(selectedDay) {
          this.$emit('input', selectedDay);
        }
      }
    }
    </script>
    <style>
    .dayPicker a {
      display: inline-block;
      margin: 5px;
      padding: 5px;
      border: 2px solid transparent;
    }
    .dayPicker a.isSelected {
      border-color: blue;
    }
    </style>

    Et maintenant, utilisons ce composant:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
      <div>
         <day-picker :value="day" @input="day = $event"/>
         <p v-if="day === null">Choisissez un jour</p>
         <p v-else>Vous avez choisi {{day}}</p>
      </div>
    </template>
    <script>
    import DayPicker from './DayPicker';
    export default {
      name: 'SampleUsage',
      components: { DayPicker },
      data() { return { day: null }; },
    };
    </script>
    On peut ensuite simplifier çà en s'appuyant sur la directive v-model, qui va automatiquement transmettre à la prop value la variable donnée et nous abonner à input pour mettre à jour cette variable:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
      <div>
         <day-picker v-model="day"/>
         <p v-if="day === null">Choisissez un jour</p>
         <p v-else>Vous avez choisi {{day}}</p>
      </div>
    </template>
    <script>
    import DayPicker from './DayPicker';
    export default {
      name: 'SampleUsage2',
      components: { DayPicker },
      data() { return { day: null }; },
    };
    </script>

    A noter que v-model s'appuie par défaut sur la prop value et l'événement input pour mettre à jour la data, mais que ca peut être personnalisé si le terme value et l'événement day ne sont pas très parlant.
    Il convient alors d'ajouter une entrée model dans la description du composant:

    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <template>
    <div class="dayPicker">
      <a
       v-for="dayItem in days"
       @click="selectDay(day)"
       :key="dayItem"
       :class="{'isSelected': dayItem === day}"
     >
       {{dayItem}}
      </a>
    <div>
    <script>
    export default {
      name: 'DayPicker2',
      model: {
        prop: 'day',
        event: 'change',
      },
      data() {
        return {
          days: ['Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa', 'Di']
        };
      },
      props: {
        day: {
          type: String,
          default: null,
      },
      methods: {
        selectDay(selectedDay) {
          this.$emit('change', selectedDay);
        }
      }
    }
    </script>
    <style>
    .dayPicker a {
      display: inline-block;
      margin: 5px;
      padding: 5px;
      border: 2px solid transparent;
    }
    .dayPicker a.isSelected {
      border-color: blue;
    }
    </style>
    Ce qui donnerait en utilisation:
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
      <div>
         <day-picker2 :day="day" @change="day = $event"/>
         <p v-if="day === null">Choisissez un jour</p>
         <p v-else>Vous avez choisi {{day}}</p>
      </div>
    </template>
    <script>
    import DayPicker2 from './DayPicker2';
    export default {
      name: 'SampleUsage3',
      components: { DayPicker2 },
      data() { return { day: null }; },
    };
    </script>

    Qui ne changerait rien en utilisant v-model
    Code html : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <template>
      <div>
         <day-picker v-model="day"/>
         <p v-if="day === null">Choisissez un jour</p>
         <p v-else>Vous avez choisi {{day}}</p>
      </div>
    </template>
    <script>
    import DayPicker2 from './DayPicker2';
    export default {
      name: 'SampleUsage3',
      components: { DayPicker2 },
      data() { return { day: null }; },
    };
    </script>

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2007
    Messages
    282
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Gers (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Octobre 2007
    Messages : 282
    Par défaut
    Bonsoir

    Merci pour vos réponses.
    Donc je pense avoir compris le principe.

    J'ai donc modifier dans mon fichier DatepickerAgenda.vue la methods selectDate (Attention, j'ai modifié un peu mon code car je veux gérer l'heure)
    Code vuejs : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        selectDate: function (day) {
          this.newdate = day.clone().set({h: this.hour, m: this.minute})
          this.$emit('change', this.newdate)
        }

    Et ajouter la "methods" suivante dans ma fichier Datepicker.vue
    Code vuejs : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    methods: {
        SelectDate: function (date) {
          this.date = date
        }
      },

    Par contre,je suis tombé dans un forum anglophone sur un $dispatch qui a l'air de faire la même chose. Est-ce la même chose? Sinon, merci en tout cas pour votre idée.

    lemirandais

  4. #4
    Membre Expert
    Avatar de gwyohm
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2007
    Messages
    925
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2007
    Messages : 925
    Par défaut
    $dispatch, c'est pour vuex. c'est effectivement similaire pour déclencher une action du store.

  5. #5
    Membre Expert
    Avatar de gwyohm
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2007
    Messages
    925
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2007
    Messages : 925
    Par défaut
    je dis n'importe quoi.
    dispatch est pour vuex. methode du store
    $dispatch est bien sur vue, mais dépreciée.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 12
    Dernier message: 20/03/2015, 09h52
  2. Réponses: 4
    Dernier message: 02/01/2014, 13h39
  3. Réponses: 1
    Dernier message: 20/07/2009, 10h54
  4. Réponses: 1
    Dernier message: 01/08/2008, 13h53
  5. Réponses: 2
    Dernier message: 15/07/2008, 09h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo