Vuelidate $each:如何验证嵌套集合?

Vuelidate $each: How can I validate a nested collection?

我很难理解可能的基本概念。我正在传递 location 作为道具。它有一个 json 列来存储 additionalAttributes。它看起来像这样:

    "additionalProperties": [
        {
            "integrations": [
                {
                    "exampleVendor": {
                        "locationId": 123,
                        "positionId": 456
                    }
                }
            ]
        }
    ],
    "createdAt": "",
    "updatedAt": "",
    ...

以上是我硬编码到我的数据库 (Postgres) 中的内容,以尝试模拟数据返回时的样子。

我正在使用 vuelidate 文档的 validate collections 部分。

这是我用来尝试创建验证规则的内容:

validations: {
      location: {
        additionalProperties: {
          $each: {
            integrations: {
              $each: {
                exampleVendor: {
                  locationId: {required},
                  positionId: {required},
                }
              }
            }
          }
        }
      }
  },

在我的模板中,我尝试像这样连接验证:

<select id="my-id" 
    name="my-id" 
    class="py-3 px-3 mt-1 block w-full pl-3 pr-10 py-2 text-base sm:text-sm rounded-md" 
    v-if="locations"
    v-model.trim="$v.location.additionalProperties[0].integrations[0].exampleVendor.locationId.$model" 
    :class="[$v.location.additionalProperties[0].integrations[0].exampleVendor.locationId.$error ? 
    'focus:ring-red-500 focus:border-red-500 border-red-300' : 'focus:ring-gray-400 focus:border-gray-400 border-gray-300',]"
>
...
</select>

我使用这个组件已经有一段时间了, 有一个非常愚蠢的问题。

我也担心设置这么死板的路径additionalProperties[0].integrations[0]真的很糟糕

我担心这个不会落后太多,但是是时候征求一些建议了。感谢您的任何建议!

编辑

@tony19 就为什么仅使用第一个值时使用数组进行了出色的调用。也许有更好的方法来做我正在做的事情;这是我数据库中的数据 可能 的更广泛的视图。除了集成之外,它现在还具有其他属性。不过现在,我只专注于此。


"additionalProperties": [
        {
            "integrations": [
                {
                    "exampleVendor": {
                        "locationId": 123,
                        "positionId": 456
                    },
                    "anotherVendor": {
                        "foo": "abc",
                        "bar": "def"
                    },
                    "someOtherVendor": {
                        "thing": "value"
                    }
                }
            ],
            "anotherAttribute: {
                "one": "two"
            },
            "possibleAttributes": [...]
        }
    ],

我在解决这个问题的过程中学到了很多东西。更重要的问题之一是如何解决 vuelidate 认为它得到的问题。

我创建了一个 change 处理程序来深入了解 $model 值是什么。这是一个例子:

<select @change="onChange"...">...</select>


...

// start with what I know to be true.
onChange() {
    console.log($v.location.additionalProperties);
}

使用上面的对象结构,然后我将移动到对象中,直到我得到这个:

console.log($v.location.additionalProperties.$each[0].integrations.$each[0]. exampleVendor.locationId.$model; // 12345

现在我有了模型的“路径”,我可以更新我的 <select> 元素:

<select id="my-locationId" name="my-locationId" class="py-3 px-3 mt-1 block w-full pl-3 pr-10 py-2 text-base sm:text-sm rounded-md" 
    v-model.trim="$v.location.additionalProperties.$each[0].integrations .$each[0].exampleVendor.locationId.$model"
    :class="[
             $v.location.additionalProperties.$each[0].integrations.$each[0].exampleVendor.locationId.$error
                        ? 'focus:ring-red-500 focus:border-red-500 border-red-300'
                        : 'focus:ring-gray-400 focus:border-gray-400 border-gray-300',
                    ]"
    >
    <option selected="selected" value="">Select</option>
    <option
        v-for="location in myLocations"
        :key="location.id"
        :value="location.id"
    >
        {{ location.name }}
    </option>
</select>

现在嵌套路径是 collecting/setting 数据,我可以设置验证规则:

...

data: () => ({...}),

validations: {
  location: {
      additionalProperties: {
        $each: {
          integrations: {
            $each: {
              exampleVendor: {
                locationId: { required },
                positionId: { required },
              },
            },
          },
        },
     },
  },
},

...

methods: {
  async save() {
      this.$v.$touch();

      if (this.$v.$invalid) {
        this.errors = true;
      } else {
        try {
          const params = {
            location: this.location, // location is passed in as props
            method: this.location.id ? "PATCH" : "POST",
          };

          console.log('params: ', params);  // {...}

          // Save to vuex or ??
        } catch (error) {
          console.log('there was an error:', error);
        }
      }
    },
}

希望这对其他人有所帮助 - 它不是非常简单,我相信有更有效的方法,但这最终对我有用。

编辑 2

请务必同时遵循@tony19 的建议答案。提供的解决方案消除了我在问题中所说的“僵化”。

正如您评论的那样,additionalPropertiesintegrations 中可能有更多的数组值,迭代 这些属性比硬-更有意义仅对第一个元素进行编码访问。

Vuelidate docs for collections you linked 显示用 $each.$iter 迭代数组,所以我会为每个嵌套级别使用 <template v-for="ARRAY.$each.$iter">

<template v-for="(addtlProperty, i) in $v.location.additionalProperties.$each.$iter">
  <template v-for="(integration, j) in addtlProperty.integrations.$each.$iter">
    <select
      :key="`${i}-${j}`"
      v-model.trim="integration.exampleVendor.locationId.$model"
      :class="[
        integration.exampleVendor.locationId.$error
          ? 'focus:ring-red-500 focus:border-red-500 border-red-300'
          : 'focus:ring-gray-400 focus:border-gray-400 border-gray-300',
      ]"
    >
    ...
    </select>
  </template>
</template>

demo