ratiw / vuetable-2

data table simplify! -- datatable component for Vue 2.x. See documentation at
https://vuetable.com
MIT License
2.16k stars 400 forks source link

Add second param "item" to callback function #97

Open yariksav opened 7 years ago

yariksav commented 7 years ago

Somtime I need calculate cell from two fields from data. For example: some total value. Make every time component or slot is a not best solution, becouse I try put all settings to 1 object code.

Why would not add second parameter "item" to callback? I wil make work simplier

I suggest to add "item" as second param to rows of code (in function callCalback)

534: return field.callback(this.getObjectValue(item, field.name), item)

545: : this.$parent[func].call(this.$parent, valuem, item)

As I understand we cannot add item to row below, because it might broke old code where use logic to put additional params like callback: 'formatDate|DD-MM-YYYY'

544: ? this.$parent[func].apply(this.$parent, [value].concat(args))

Thank You

ratiw commented 7 years ago

@yariksav Sorry, it is not clear to me what is your use case. Can you please explain in more detail or show me the code that you would like to use?

yariksav commented 7 years ago

example code:

  data () {
    return {
      fields: [
        //...
        {
            name: 'total',
            titleClass: 'center aligned',
            dataClass: 'right aligned',
            callback: function(value, item) {
                return item.quantity * item.price
            }
        },
        //...
      ]
    }
  },
ratiw commented 7 years ago

@yariksav I see. The callback option is never meant to do the computational work. It is, however, intended mostly for manipulating the value of a particular column for display.

For you case, this can easily be achieved using __slot.

cristijora commented 7 years ago
<template slot="actions" scope="props">
    <div>Total price: {{props.rowData.quantity * props.rowData.price}}</div>
</template>

You have more control with slots for this case. You can style and put whatever your need in your slot rather than having control over the text only.

msinansahin commented 7 years ago

Maybe this can be used as temporary solution

```
  Vuetable.default.methods.callCallback = function (field, item) {
    if (!this.hasCallback(field)) return;

    var args = field.callback.split('|');
    var func = args.shift();

    if (typeof this.$parent[func] == 'function') {
        //sinan changed, send item data to callback as last parameter
        return args.length > 0 
                ? this.$parent[func].apply(this.$parent, [this.getObjectValue(item, field.name)].concat(args), item) 
                : this.$parent[func].call(this.$parent, this.getObjectValue(item, field.name), item);
    }

    return null;
};  
chrislandeza commented 7 years ago

I think having access to rowData inside the callback is a good addition. another use case for this is when you try to combine and format two dates.

  fields: [
    //...
    {
        name: 'Period',
        callback: function(value, rowData) {
            return `${moment(rowData.date_from).format('MMM DD, YYYY')} - ${moment(rowData.date_to).format('MMM DD, YYYY')}
        }
    },
    //...
  ]

I know we can achieve the same thing using __slot + methodor custom filter but I think for quick formatting, doing it inside a callback is much simpler.

ratiw commented 7 years ago

@chrislandeza __slot is already easy enough. If this is implemented, there is no use having to define any field definition at all. And when your project's getting more complex, this will become very messy as you can not easily determine which is doing what. To reinforce that, I'm planning to rename callbacke to format or formatter in the future.

Todmy commented 6 years ago

@ratiw I support this idea, since more my case I need just to concat text from two fields. So it will be conveniently to write:

...
callback: (value, item) => `Mr. ${value} (${country})`
...
cristijora commented 6 years ago

@Todmy Although this might be shorter/convenient, slots are way more flexible. For 2 extra lines, you could add styling and get the benefits of templates. Here is an example: http://jsfiddle.net/z11fe07p/2612/

fields: ['__slot:slotname']

And with ES6

<template slot="slotname" scope="{rowData}">
         {{`Mr. ${rowData.name} ${rowData.country}`}}
</template>

Or with vue > 2.5

<span slot-scope="slotname" scope="{rowData}">
         {{`Mr. ${rowData.name} ${rowData.country}`}}
</span>

I really believe this is a more flexible option and would encourage it instead of callbacks simply because:

  1. It's declarative in the template. You get rendered based on what's in the template rather than some js logic which it's a bit of magic tbh.
  2. It's more flexible. You could add classes, template logic, tags, components and so on.
Todmy commented 6 years ago

@cristijora nice solution. For my case, I found the option to pass a nested object to vuetable. And then in the callback, I transform my output as I want. But it will work for nested objects in responses. So maybe are some possibility to pass all request data to vuetable name? like

...
name: '*'
...
cristijora commented 6 years ago

@Todmy That's another reason to use slots :) In the fiddle above, you could easily access nested data because you have access to the whole row. {{props.rowData.group.description}}

Todmy commented 6 years ago

@cristijora yep, I got it. But we should tend to write the simplest code we can. Once again, your solution is quite good, but I am trying to find more simple and not overloaded by additional code. Let's think about the case when a user creates own custom wrapper for vuetable-2(actually I am currently working on the project with such wrapper). This wrapper has own interface which accepts an array of fields

<grid :fields="gridFields" url="/users" :opts="opts"></grid>

So I don't see how to force slots to work together with this wrapper. That's why I am seeking alternatives for slots.

cristijora commented 6 years ago

You could simply propagate the field slots inside your grid component http://jsfiddle.net/z11fe07p/2656/

  1. Filter your slot names and save them in some computed property
  2. Declare them in your grid
    <template v-for="fieldSlot in fieldSlots" :slot="fieldSlot" slot-scope="props">
    <slot :row-data="props.rowData" :name="fieldSlot"></slot>
    </template>
  3. Use your grid with slots just as vuetable itself
    <grid :fields="fields">
      <template slot="description" scope="props">
           <span style ="color: green;">{{props.rowData.group.description}}</span>
      </template>
    </grid>

    It's a bit more code but again you have a nice interface in the end which is very flexible in terms of what you put inside the table. This will allow pretty much anything inside the table rather than limit to some callback based composed strings