创建可以管理外部数据的抽象组件

hrp*_*xQ4 17 javascript vue.js vuetify.js

目前,我将Vuetify用于基本组件,并希望创建可重用的扩展。例如,包含复选框的列表,具有某些功能的数据表列等。

对于这个问题,我将以包含复选框列表为例。我创建了以下名为CheckboxGroup.vue的组件

<template>
  <v-container>
    <v-checkbox
      v-for="(item, index) in items"
      :key="index"
      v-model="item.state"
      :label="item.title"
    ></v-checkbox>
  </v-container>
</template>

<script>
export default {
  props: {
    items: Array,
    required: true
  }
};
</script>
Run Code Online (Sandbox Code Playgroud)

该组件将对象数组作为属性,并为每个条目创建一个复选框。

重要部分是v-model="item.state":label="item.title"。在大多数情况下,该state属性将具有不同的名称,与该title属性相同。

为了进行测试,我创建了一个名为Home.vue的视图文件,其中包含一系列文档。

<template>
  <v-container>
    <CheckboxGroup :items="documents"/>
    <v-btn @click="saveSettings">Save</v-btn>
  </v-container>
</template>

<script>
import CheckboxGroup from "../components/CheckboxGroup";

export default {
  components: {
    CheckboxGroup
  },
  data: function() {
    return {
      documents: [
        {
          id: 1,
          name: "Doc 1",
          deleted: false
        },
        {
          id: 2,
          name: "Doc 2",
          deleted: false
        },
        {
          id: 3,
          name: "Doc 3",
          deleted: true
        }
      ]
    };
  },
  methods: {
    saveSettings: function() {
      console.log(this.documents);
    }
  }
};
</script>
Run Code Online (Sandbox Code Playgroud)

这一次title称为namestate称为deleted。显然CheckboxGroup由于属性名称错误而无法管理文档。

您将如何解决这个问题?您将创建一个计算属性并重命名这些属性吗?我认为这是个坏主意...

顺便说一句,使用v-model一个好主意吗?另一种解决方案是侦听复选框的更改事件并发出带有项目索引的事件。然后,您将不得不侦听父组件中的更改。

我认为没有办法创建类似

<CheckboxGroup :items="documents" titleAttribute="name" stateAttribute="deleted"/> 
Run Code Online (Sandbox Code Playgroud)

因为无论如何这都是不好的设计。我希望这是一个非常琐碎的问题,每个Vue开发人员都面临着这个问题,因为主要目标应该始终是开发可多次重用的抽象组件。

请记住,此复选框问题仅是示例。这个问题的解决方案也可以解决相同或相似的问题:)

Sab*_*bee 7

如果我了解您的要求,那不是那么简单。使用道具是个好主意。您无需管理文档属性名称,只需将属性名称设置为您的组件即可。

注意

像此解决方案一样,重命名属性或使用代理会占用更多资源,因为您需要运行循环来重命名属性名称或将别名应用于数据数组对象。

CheckboxGroup.vue

  <template>
      <v-container fluid>
        <v-checkbox 
          v-for="(item, index) in items"
          :key="index"
          v-model="item[itemModel]" 
          :label="item[itemValue]"
        ></v-checkbox>
        <hr>
        {{items}}
      </v-container>
    </template>
    <script>

    export default {
      name: "CheckboxGroup",
       props: {

        items: {
          type: Array,
          required:true
        },

        itemValue:{
          type:String,
          default: 'title',

           // validate props if you need
          //validator: function (value) {
          //  return ['title', 'name'].indexOf(value) !== -1
          // }
          // or make required
        },

        itemModel:{
          type:String,
          default: 'state',

           // validate props if you need
           //validator: function (value) {
            // validate props if you need
            // return ['state', 'deleted'].indexOf(value) !== -1
           // }
         // or make required
        }

      }
    };
    </script>
Run Code Online (Sandbox Code Playgroud)

主场

<template>

  <div id="app">
    <checkbox-group :items="documents"
      item-value="name"
      item-model="deleted"
    >

    </checkbox-group>
  </div>
</template>

<script>
import CheckboxGroup from "./CheckboxGroup.vue";

export default {
  name: "App",
  components: {
    // HelloWorld,
    CheckboxGroup
  },
  data: function() {
    return {
      documents: [
        {
          id: 1,
          name: "Doc 1",
          deleted: false
        },
        {
          id: 2,
          name: "Doc 2",
          deleted: false
        },
        {
          id: 3,
          name: "Doc 3",
          deleted: true
        }
      ]
    }
}
};
</script>
Run Code Online (Sandbox Code Playgroud)

根据您的示例,我尝试演示如何创建组件来管理子组件中的对象属性。如果您需要更多信息,请告诉我。


Ric*_*sen 5

您可以在访问期间使用代理来映射文档属性名称。

注意
在我的原始答案中,我为和使用了代理处理程序,这对于普通的javascript对象就足够了,但是与Vue 属性一起使用时会失败,因为Vue应用了观察器包装。getsetdata

通过捕捉也has代理,这是可以克服的。我在下面将原始答案留给了对此问题感兴趣的人。

这是一个演示如何使用代理将Vue反应性属性“别名”为不同名称 的演示

  • 不影响原始数据结构
  • 无需复制数据

console.clear()
Vue.config.productionTip = false
Vue.config.devtools = false

Vue.component('checkboxgroup', {
  template: '#checkboxGroup',
  props: { items: Array, required: true },
});

const aliasProps = (obj, aliasMap) => {
  const handler = {
    has(target, key) {
      if (key in aliasMap) {
        return true;  // prevent Vue adding aliased props
      }
      return key in target;
    },
    get(target, prop, receiver) {
      const propToGet = aliasMap[prop] || prop;
      return Reflect.get(target, propToGet);
    },
    set(target, prop, value, receiver) {
      const propToSet = aliasMap[prop] || prop;
      return Reflect.set(target, propToSet, value)
    }
  };
  return new Proxy(obj, handler);
}

new Vue({
  el: '#app',
  data: {
    documents: [
      { id: 1, name: "Doc 1", deleted: false },
      { id: 2, name: "Doc 2", deleted: false },
      { id: 3, name: "Doc 3", deleted: true },
    ]
  },
  computed: {
    checkBoxItems() {
      const aliases = {
        title: 'name',
        state: 'deleted'
      }
      return this.documents.map(doc => aliasProps(doc, aliases));
    }
  },
  methods: {
    saveSettings: function() {
      console.log(this.documents);
    }
  },
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet"/>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>

<div id="app">
  <v-app id="theapp">
    <v-container>
      <checkboxgroup :items="checkBoxItems"></checkboxgroup>
      <v-btn color="info" 
             @click="saveSettings">Save</v-btn>
    </v-container>
  </v-app>
</div>

<template id="checkboxGroup">
  <v-container style="display: flex">
    <v-checkbox
      v-for="(item, index) in items"
      :key="index"
      v-model="item.state" 
      :label="item.title"
    ></v-checkbox>
  </v-container>
</template>
Run Code Online (Sandbox Code Playgroud)


原始答案

您可以在访问期间使用代理来映射文档属性名称。

<template>
  ...
  <CheckboxGroup :items="checkBoxItems"/>
  ...
</template>

<script>
  export default {
    ...
    computed: {
      checkBoxItems() {
        const handler = {
          get: function(target, prop) {
            return prop === 'title' ? target.name :
              prop === 'state' ? target.deleted :
              target[prop];
          },
          set(obj, prop, value) {
            const propToSet = 
              prop === 'title' ? 'name' :
              prop === 'state' ? 'deleted' :
              prop;
            obj[propToSet] = value;
          }
        };
        return documents.map(doc => new Proxy(doc, handler))
      },
    },
    ...
  }
</script>
Run Code Online (Sandbox Code Playgroud)

演示版

const documents = [
  { id: 1, name: "Doc 1", deleted: false },
  { id: 2, name: "Doc 2", deleted: false },
  { id: 3, name: "Doc 3", deleted: true },
]

const handler = {
  get: function(target, prop) {
    return prop === 'title' ? target.name :
      prop === 'state' ? target.deleted :
      target[prop];
  },
  set(obj, prop, value) {
    const propToSet = 
      prop === 'title' ? 'name' :
      prop === 'state' ? 'deleted' :
      prop;
     obj[propToSet] = value;
  }
};
const checkItems = documents.map(doc => new Proxy(doc, handler))

console.log('Accessing new property names via checkItems')
checkItems.forEach(ci => console.log(ci.id, ci.title, ci.state))

console.log('After update, values of documents')
checkItems.forEach(ci => ci.state = !ci.state )
documents.forEach(doc => console.log(doc.id, doc.name, doc.deleted))
Run Code Online (Sandbox Code Playgroud)