# Modifier tâche

Un des objectifs du développement par composant est de rendre les composants le plus autonomes possible.

Afin de respecter cette bonne pratique, nous allons déléguer la modification d'une tâche au composant `Tache.vue`.

Ainsi `Tache.vue` regroupera toutes les fonctionnalités d'une tâche existante :

* Afficher les données de la tâche (nom, date, heure et état)
* Changer l'état d'une tâche (terminée / à faire)
* Supprimer une tâche
* Modifier les données de la tâche (nom, heure, date)

La page des tâches, `PageTaches.vue`, s'occupe de lister et d'ajouter des tâches.

## Formulaire tâche

Le formulaire de modification d'une tâche étant presque le même que celui d'ajout, nous allons modifier le composant `TaskDialog.vue` pour qu'il puisse être utilisé pour ces deux opérations.

### Titre du formulaire

Nous allons ajouter une **zone de distribution de contenu appelée&#x20;*****Slot**,* `<slot/>` qui nous permettra de personnaliser le titre du formulaire.

On remplace donc le texte `Ajouter une tâche` par `<slot/>` :

{% code title="TaskDialog.vue" %}

```markup
<div class="text-h6"><slot/></div>
```

{% endcode %}

L'élément `<slot/>` sera ensuite remplacé par le contenu saisi lors de l'utilisation du composant `TaskComponent.vue` entre sa balise d'ouverture et de fermeture :

```markup
<TaskDialog>Mon titre custom!</TaskDialog>
```

On peut maintenant l'utiliser dans `PageTaches.vue` pour définir le titre du formulaire :

{% code title="PageTaches.vue" %}

```markup
<TaskDialog @close="() => afficherTaskDialog = false">
  Ajouter une tâche
</TaskDialog>
```

{% endcode %}

### Texte du bouton

Pour le label du bouton, nous allons créer une propriété `bouton` avec `"OK"` comme valeur par défaut.

{% code title="TaskDialog.vue" %}

```javascript
const { bouton } = defineProps({
  bouton: {
    type: String,
    default: 'Ok'
  }
})
```

{% endcode %}

Ne pas oublier de le lier au texte du bouton!

{% code title="TaskDialog.vue" %}

```markup
<q-card-actions align="right">
  <q-btn :label="bouton" color="primary" type="submit" />
</q-card-actions>
```

{% endcode %}

On peut maintenant le configurer dans `PageTaches.vue` :

{% code title="PageTaches.vue" %}

```markup
<TaskDialog
  @close="() => afficherTaskDialog = false"
  bouton="Ajouter"
>
  Ajouter une tâche
</TaskDialog>
```

{% endcode %}

### Intégrer le formulaire à `Tache.vue`

Nous pouvons maintenant ajouter le formulaire au composant `TaskComponent.vue` en l'incluant dans une *dialog*, tout comme nous l'avons fait pour `PageTaches.vue`.

Placer ce code juste avant la fermeture `</q-item>` de `Tache.vue` :

{% code title="TaskComponent.vue" %}

```markup
<q-dialog
  v-model="afficherTaskDialog"
  persistent
>
  <TaskDialog
    @close="() => afficherTaskDialog = false"
    bouton="Enregistrer"
    >
    Modifier la tâche
  </TaskDialog>
</q-dialog>
```

{% endcode %}

#### Ref `afficherTaskDialog`

Il faut encore ajouter la ref `afficherTaskDialog` au composant, pour gérer l'affichage de la *dialog*.

{% code title="src/components/Taches/Tache.vue | Ligne 70" %}

```javascript
import { ref } from 'vue'

//...

const afficherTaskDialog = ref(false)
```

{% endcode %}

## Bouton modifier une tâche

Prochaine étape, ajouter un bouton "Modifier" avant le bouton "Supprimer" de dans le composant `TaskComponent.vue`.

Pour ce faire, simplement copier la section `<q-item-section>` contenant le bouton de suppression et la coller juste avant.&#x20;

* Changer l'icône `"delete"` par `"edit"` et utiliser la couleur `"primary"`.
* Au clic, stopper la propagation et afficher le formulaire :  `afficherTaskDialog = true`

{% code title="TaskComponent.vue" %}

```markup
<q-item-section side>
  <q-btn
    @click.stop="() => afficherTaskDialog = true"
    dense
    flat
    round
    color="primary"
    icon="edit"
  />
</q-item-section>

<q-item-section side>
  <q-btn
    @click.stop="() => confirmerSuppression(tache.id)"
    dense
    flat
    round
    color="negative"
    icon="delete"
  />
</q-item-section>
```

{% endcode %}

Cool, maintenant si on clique sur un bouton de modification, le formulaire s'affiche avec le titre "Modifier une tâche" et un bouton "Modifier".

## Passer la propriété au formulaire

Il faut maintenant que notre formulaire récupère les données actuelles de la tâche pour les afficher dans les champs du formulaire.

On va ajouter une propriété `tacheAModifier` au composant `TaskDialog.vue`. Le composant `TaskComponent.vue` pourra ainsi passer la tâche au formulaire via cette propriété.

{% code title="TaskDialog.vue" %}

```javascript
const { tacheAModifier, bouton } = defineProps({
  tacheAModifier: {
    type: Object
  },
  bouton: {
    type: String,
    default: 'Ok'
  }
})
```

{% endcode %}

### Copie des propriétés et valeurs de la tâche à modifier

Après le chargement de `TaskDialog.vue`, si une `tacheAModifier` existe, on copie ses propriétés et valeurs dans la donnée `tache` du composant. Donnée `tache` qui est liée aux éléments du formulaire.

Cela affichera instantanément les valeurs de la tâche à modifier dans les champs de saisie.

{% hint style="danger" %}
On doit utiliser `Object.assign(destination, source)` pour faire une **copie par valeur** !

En faisant une affectation classique `obj1 = obj2`, on fait une **copie par référence**.

Dans notre cas, le formulaire ne modifierait pas les valeurs de son propre objet, mais celui du composant parent, ce qui passerait outre l'action d'enregistrer
{% endhint %}

{% code title="TaskDialog.vue" %}

```javascript
import { onMounted } from 'vue'

onMounted(() => {
  if (tacheAModifier) {
    Object.assign(tache.value, tacheAModifier)
  }
})
```

{% endcode %}

Pour garder de la consistance dans la structure de notre ref tache, on va l'initialiser avec un champ id initialisé à `undefined`:

```javascript
const tache = ref({
  id: undefined, 
  nom: '',
  dateFin: '',
  heureFin: '',
  terminee: false
})
```

Notre formulaire de modification affiche les données de la tâche à modifier :beers:.

## Sauver les modifications

Actuellement, si l’on clique sur le bouton "Modifier", cela ajoute une nouvelle tâche au magasin.

Nous allons modifier la méthode `formSubmit` du composant `Tache.vue` pour qu'il modifie la tâche.

Pour se faire, il suffit de tester si la tâche actuelle possède un id :

* **Avec id :** **Modification** de la tâche dans le magasin.
* **Sans id :** **Ajout** d'une nouvelle tâche dans le magasin.

Pour modifier la tâche dans le magasin, nous allons modifier l'action `modifierTache` que nous avions créée pour changer l'état d'une tâche (terminée, à faire).

Il faut donc modifier pour pouvoir changer n'importe quelle valeur; on change sa signature afin qu'elle prenne en paramètre un `id`, ainsi qu'un `payload` avec la structure suivante:

```typescript
{
    nom: string,
    dateFin: string,
    heureFin: string,
    terminee: boolean
}
```

Toutes les propriétés de notre payload sont optionnelles, afin qu'on puisse ne passer que celles qui nous intéressent. On va itérer sur toutes les données passées, et les affecter à notre tache.

Modifier `store-taches.js` en conséquence:

```javascript
const modifierTache = (id, payload) => {
  const index = taches.value.findIndex((tache) => tache.id === id)
  for (const key in payload) {
    taches.value[index][key] = payload[key]
  }
}
```

### méthode `formSubmit()`

Nous pouvons maintenant modifier le code de `formSubmit()` pour modifier ou ajouter une tâche.

{% code title="TaskDialog.vue" %}

```javascript
const formSubmit = () => {
  if (tache.value.id) {
    const { value: payload } = tache // destructuration de la tache
    store.modifierTache(tache.value.id, payload);
  } else {
    store.ajouterTache(tache.value)
  }
  emit('close')
}
```

{% endcode %}

## Résultat

{% tabs %}
{% tab title="TaskDialog.vue" %}

```markup
<template>
  <q-card>
    <q-card-section class="row">
      <div class="text-h6"><slot/></div>
      <q-space/>
      <q-btn
        dense
        flat
        round
        icon="close"
        v-close-popup
      />
    </q-card-section>

    <q-form
      @submit="formSubmit"
    >
      <q-card-section class="q-pt-none">
        <q-input
          outlined
          v-model="tache.nom"
          label="Nom"
          :rules="[val => !!val || 'Le nom est obligatoire']"
          autofocus
          clearable
        />

        <q-input
          outlined
          v-model="tache.dateFin"
          label="Date"
          class="q-mt-sm"
          clearable
        >
          <template v-slot:append>
            <q-icon name="event" class="cursor-pointer">
              <q-popup-proxy cover transition-show="scale" transition-hide="scale">
                <q-date
                  v-model="tache.dateFin"
                  mask="D.M.YYYY"
                />
              </q-popup-proxy>
            </q-icon>
          </template>
        </q-input>

        <q-input
          label="Heure"
          outlined
          v-model="tache.heureFin"
          class="q-mt-sm"
          clearable
          v-if="tache.dateFin !== ''"
        >
          <template v-slot:append>
            <q-icon name="access_time" class="cursor-pointer">
              <q-popup-proxy
                cover
                transition-show="scale"
                transition-hide="scale"
                @before-show="() => heureTemp = tache.heureFin"
              >
                <q-time v-model="heureTemp">
                  <div class="row items-center justify-end q-gutter-sm">
                    <q-btn label="Annuler" color="primary" flat v-close-popup />
                    <q-btn
                      label="OK"
                      color="primary"
                      flat
                      @click="() => tache.heureFin = heureTemp"
                      v-close-popup
                    />
                  </div>
                </q-time>
              </q-popup-proxy>
            </q-icon>
          </template>
        </q-input>
      </q-card-section>

      <q-card-actions align="right">
        <q-btn :label="bouton" color="primary" type="submit" />
      </q-card-actions>
    </q-form>
  </q-card>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import { useTachesStore } from 'stores/store-taches'

const store = useTachesStore()

const heureTemp = ref('')

const tache = ref({
  id: undefined,
  nom: '',
  dateFin: '',
  heureFin: '',
  terminee: false
})

const emit = defineEmits(['close'])

const { tacheAModifier, bouton } = defineProps({
  tacheAModifier: {
    type: Object
  },
  bouton: {
    type: String,
    default: 'Ok'
  }
})

const formSubmit = () => {
  if (tache.value.id) {
    const { value: payload } = tache // destructuration de la tache
    store.modifierTache(tache.value.id, payload);
  } else {
    store.ajouterTache(tache.value)
  }
  emit('close')
}

onMounted(() => {
  if (tacheAModifier) {
    Object.assign(tache.value, tacheAModifier)
  }
})
</script>

<style scoped lang="scss">
.q-card {
  max-width: 80vw;
  width: 400px;
}
</style>

```

{% endtab %}

{% tab title="TaskComponent.vue" %}

```markup
<template>
  <q-item
    @click="() => store.modifierTache(tache.id)"
    clickable
    v-ripple
    :class="tache.terminee ? 'bg-green-1' : 'bg-orange-1'"
  >
    <q-item-section side>
      <q-checkbox
        :modelValue="tache.terminee"
        @update:modelValue="() => store.modifierTache(tache.id)"
      />
    </q-item-section>

    <q-item-section>
      <q-item-label
        :class="{ 'text-barre': tache.terminee }"
      >{{ tache.nom }}</q-item-label>
    </q-item-section>

    <q-item-section
      v-if="tache.dateFin !== ''"
      side
    >
      <!-- Création d'une ligne Flex -->
      <div class="row">
        <!-- Création d'une colonne Flex -->
        <div class="column justify-center">
          <q-icon
            size="18px"
            name="event"
            class="q-mr-xs"
          />
        </div>
        <!-- Création d'une colonne Flex -->
        <div class="column">
          <q-item-label
            class="text-right"
            caption
          >
            {{ tache.dateFin }}
          </q-item-label>
          <q-item-label
            class="text-right"
            caption
          >
            <small>{{ tache.heureFin }}</small>
          </q-item-label>
        </div>
      </div>
    </q-item-section>

    <q-item-section side>
      <q-btn
        @click.stop="() => afficherFormTache = true"
        dense
        flat
        round
        color="primary"
        icon="edit"
      />
    </q-item-section>

    <q-item-section side>
      <q-btn
        @click.stop="() => confirmerSuppression(tache.id)"
        dense
        flat
        round
        color="negative"
        icon="delete"
      />
    </q-item-section>

    <q-dialog
      v-model="afficherFormTache"
      persistent
    >
      <TaskDialog
        @close="() => afficherFormTache = false"
        bouton="Enregistrer"
        :tacheAModifier="tache"
        >
        Modifier la tâche
      </TaskDialog>
    </q-dialog>
  </q-item>
</template>

<script setup>
import { useTachesStore } from 'stores/store-taches'
import { Dialog } from 'quasar'
import TaskDialog from 'components/TaskDialog.vue'
import { ref } from 'vue'

const { tache } = defineProps({ tache: Object })

const store = useTachesStore()

const afficherFormTache = ref(false)

const confirmerSuppression = (id) => {
  Dialog.create({
    title: 'Suppression',
    message: 'Êtes-vous sûr de vouloir supprimer cette tâche ?',
    cancel: true,
    persistent: true
  }).onOk(() => {
    store.supprimerTache(id)
  })
}
</script>

<style scoped lang="scss">

</style>

```

{% endtab %}

{% tab title="PageTaches.vue" %}

```markup
<template>
  <q-page padding>
    <q-list
      v-if="store.taches.length > 0"
      separator
      bordered
    >
      <Task
        v-for="(tache, index) in store.taches"
        :key="tache.id"
        :tache="tache"
        @toggle="() => store.taches[index].terminee = !store.taches[index].terminee"
      />
    </q-list>
    <q-page-sticky position="bottom" class="q-mb-lg">
      <q-btn
        @click="() => afficherTaskDialog = true"
        fab
        icon="add"
        color="primary" />
    </q-page-sticky>
    <q-dialog v-model="afficherTaskDialog">
      <TaskDialog
        @close="() => afficherTaskDialog = false"
        bouton="Ajouter"
      >
        Ajouter une tâche
      </TaskDialog>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { useTachesStore } from 'stores/store-taches'
import Task from 'components/TaskComponent.vue'
import { ref } from 'vue'
import TaskDialog from 'components/TaskDialog.vue'

const store = useTachesStore()

const afficherTaskDialog = ref(false)
</script>

<style>
.text-barre {
  text-decoration: line-through;
}
</style>

```

{% endtab %}
{% endtabs %}
