ratiw / vuetable-2-tutorial

MIT License
258 stars 67 forks source link

Pass props / parameters to custom component field? #14

Open stevendesu opened 7 years ago

stevendesu commented 7 years ago

I have data being returned from my API in the following format:

{
    "total": 1000,
    "per_page": 5,
    "current_page": 1,
    "last_page": 200,
    "next_page_url": "....",
    "prev_page_url": null,
    "from": 1,
    "to": 5,
    "data": [
        {
            "order_number": 1,
            "departments": ["manufacturing", "packaging", "shipping"]
        },
        {
            "order_number": 2,
            "departments": ["manufacturing", "shipping"]
        },
        {
            "order_number": 3,
            "departments": ["packaging", "shipping"]
        },
        {
            "order_number": 4,
            "departments": ["shipping"]
        },
        {
            "order_number": 5,
            "departments": ["manufacturing"]
        }
    ]
}

For each order, there's a list of departments. I wish to display the following using VueTable:

+---------+---------------+-----------+----------+
| Order # | Manufacturing | Packaging | Shipping |
+---------+---------------+-----------+----------+
|    1    |     true      |   true    |   true   |
+---------+---------------+-----------+----------+
|    2    |     true      |   false   |   true   |
+---------+---------------+-----------+----------+
|    3    |     false     |   true    |   true   |
+---------+---------------+-----------+----------+
|    4    |     false     |   false   |   true   |
+---------+---------------+-----------+----------+
|    5    |     true      |   false   |   false  |
+---------+---------------+-----------+----------+

To make matters more confusing, this list of departments (and therefore fields to be displayed) is subject to change.

Worst-case scenario I can find a work-around by changing my API output to include "hasXXX" fields, however I would prefer to define my table with something like the following:

<template>
    <vuetable :fields="fields" api-url="..."></vuetable>
</template>
<script>
    export default {
        data: function() {
            var fields = allPossibleDepartments.map(dep => { // Populated via API or otherwise
                return {
                    name: '__component:has-department(' + dep + ')',
                    title: dep
                };
            });
            return { fields: fields };
        }
    };
</script>

I could then create a single has-department component with a dep prop which tells it which department to search for.

There may be an easier way to accomplish this that I just haven't thought of yet, but regardless I think there's value in being able to pass props to custom components.

ratiw commented 7 years ago

@stevendesu Maybe you can try using __slot (scoped slot).

The following code might give you an idea, but I haven't tested it so I'm not sure.

<template>
  <vuetable ref="vuetable"
    :fields="fields" api-url="..."
  >
    <template v-for="dep in allPossibleDepartments" :slot="dep" scope="props">
      {{ props.departments.indexOf(dep) > 0 ? 'true' : 'false' }}      
    </template>
  </vuetable>
</template>
<script>
  var fieldsDef = []
  fieldsDef.push({
    name: 'order',
    title: 'Order #'
  })
  fieldsDef.push(
    allPossibleDepartments.map(dep => {
      return {
    name: '__slot:' + dep,
    title: dep
      }
    })
  )

export default {
  data () {
     return {
        fields: fieldsDef
      }
    }
  }
</script>
stevendesu commented 7 years ago

@ratiw When I'm home tonight I'll try this out and see if it works.

stevendesu commented 7 years ago

@ratiw It took a lot of experimentation (stripping away pieces bit by bit to get a minimum test case then making several variations on that test case) but I figured it out

Replace:

<template v-for="dep in allPossibleDepartments" :slot="dep" scope="props">

With:

<div v-for="dep in allPossibleDepartments" :slot="dep" scope="props">

The <template> element apparently does not support property binding. You can use <template> with static properties, but using :slot sets the "slot" property to null (and thus the component is never loaded)

Update: Scratch that... while changing it from <template> to <div> fixed the issue of a dynamic "slot" property, it broke the "scope" property. I wasn't able to access the row data. I guess this is just a limitation of VueJS at the moment

Update 2: I've created the following bug report for VueJS: https://github.com/vuejs/vue/issues/4951