web-dev-qa-db-fra.com

Vue JS Regarde un objet imbriqué profond

Disclaimer: Ceci est ma première tentative de création d'une application MVVM pour laquelle je n'ai pas encore travaillé avec vue.js. Il se peut donc que mon problème soit le résultat d'un problème plus fondamental.


À mon avis, j'ai deux types de blocs avec des cases à cocher:

  • Type 1: bloc/cases à cocher
  • Type 2: bloc/en-têtes/cases à cocher

L'objet sous-jacent est structuré comme ceci:

{
  "someTopLevelSetting": "someValue",
  "blocks": [
    {
      "name": "someBlockName",
      "categryLevel": "false",
      "variables": [
        {
          "name": "someVarName",
          "value": "someVarValue",
          "selected": false,
          "disabled": false
        }
      ]
    },
    {
      "name": "someOtherBlockName",
      "categryLevel": "true",
      "variables": [
        {
          "name": "someVarName",
          "value": "someVarValue",
          "categories": [
            {
              "name": "SomeCatName",
              "value": "someCatValue",
              "selected": false,
              "disabled": false
            }
          ]
        }
      ]
    }
  ]
}

Mes objectifs

Sélection des cases à cocher:

  1. L'utilisateur clique sur la case à cocher, la case à cocher est sélectionnée (sélectionné = true)
  2. Une méthode est déclenchée pour vérifier si d'autres cases à cocher doivent être désactivées (disabled = true). (Si cette méthode a effectivement désactivé quelque chose, elle se rappellera également, car d'autres éléments pourraient à leur tour dépendre de l'élément désactivé)
  3. Une autre méthode met à jour d'autres choses, comme des icônes, etc.

Décochez les cases

Un utilisateur peut cliquer sur un bouton "effacer" qui désactive toutes les cases à cocher d'une liste (sélectionné = faux). Cette action doit également déclencher les méthodes qui désactivent éventuellement les cases à cocher et met à jour les icônes, etc.

Ma méthode actuelle (ce qui ne semble pas tout à fait correct)

  • L'attribut sélectionné du modèle de données est lié à l'état vérifié de l'élément checkbox via la directive v-model.
  • L'attribut disabled (du modèle) est lié à la classe et à l'attribut disabled de l'élément. Cet état est défini par le procédé susmentionné.
  • Pour initialiser les méthodes qui désactivent les cases à cocher et modifient certaines icônes, j'utilise une directive v-on="change: checkboxChange(this)". Je pense que je dois faire cette partie différemment
  • La méthode clearList est appelée via v-on="click: clearList(this)"

Le problème avec ma configuration actuelle est que l’événement change ne se déclenche pas lorsque les cases à cocher sont effacées par programme (c’est-à-dire non par interaction de l’utilisateur).

Ce que je voudrais plutôt
Pour moi, la chose la plus logique à faire serait d'utiliser this.$watch Et de suivre les modifications apportées au modèle, au lieu d'écouter les événements DOM.

Une fois qu'il y a un changement, je devrais alors identifier quel élément exact a changé et agir en conséquence. J'ai essayé de créer une fonction $watch Qui observe le tableau blocks. Cela semble bien comprendre les modifications, mais il renvoie l'objet complet, par opposition à l'attribut individuel qui a changé. De plus, il manque à cet objet des attributs d'aide utiles, tels que $parent.

Je peux penser à quelques moyens astucieux de faire fonctionner l'application (par exemple, déclencher manuellement des événements de changement dans ma méthode clearList, etc.), mais mon cas d'utilisation semble plutôt standard. Je m'attends donc à ce qu'il existe un moyen beaucoup plus élégant de gérer cela.

40
Hendrik

Vous pouvez utiliser la méthode 'watch'. Par exemple, si vos données sont:

data: {
    block: {
        checkbox: {
            active:false
        },
        someotherprop: {
            changeme: 0
        }
    }
}

Vous pouvez faire quelque chose comme ça:

data: {...},
watch: {
   'block.checkbox.active': function() {
        // checkbox active state has changed
        this.block.someotherprop.changeme = 5;
    } 
}
69
webkit

Si vous voulez regarder l'objet dans son ensemble avec toutes ses propriétés, et pas seulement une propriété, vous pouvez le faire à la place:

 data() {
    return {
       object: {
          prop1: "a",
          prop2: "b",
       }    
    }
 },
 watch: {
    object: {
        handler(newVal, oldVal) {
            // do something with the object
        },
        deep: true,
    },
},

notez handler et deep: true

22
peerbolte

Étant donné que personne n’a répondu et que j’ai résolu le problème, j’ai pensé qu’il serait peut-être utile d’afficher ma solution. Veuillez noter que je ne suis pas sûr que ma solution est de savoir comment résoudre ce type de problème, cela fonctionne cependant.

Au lieu d'utiliser cet écouteur d'événement v-on="change: checkboxChange(this)", j'utilise maintenant une directive personnalisée qui écoute à la fois les attributs de modèle sélectionnés et désactivés, comme ceci: v-on-filter-change="selected, disabled".

La directive ressemble à ceci:

directives: {
    'on-filter-change': function(newVal, oldVal) {
        // When the input elements are first rendered, the on-filter-change directive is called as well, 
        // but I only want stuff to happen when a user does someting, so I return when there is no valid old value
        if (typeof oldVal === 'undefined') {
            return false;
        }
        // Do stuff here
        // this.vm is a handy attribute that contains some vue instance information as well as the current object
        // this.expression is another useful attribute with which you can assess which event has taken place
    }
},

La clause if semble un peu compliquée, mais je ne pouvais pas trouver un autre moyen. Au moins tout fonctionne.

Cela sera peut-être utile à quelqu'un dans le futur.

7
Hendrik

Autre solution non mentionnée ici: Utilisez l’option deep.

watch:{
  block: {
    handler: function () {console.log("changed") },
    deep: true
  }
}
5
Graham Slick