Dynamic Column in a ColumnGroup

UI Components for Vue
riverman83
Posts: 3
Joined: 21 Dec 2020, 13:26

21 Dec 2020, 13:50

Hey there,
first of all: PrimeVue is great. Thanks for your work.

I'm experiencing a problem and can't find a solution googling, checking the docs or this forum.
I try to create columns inside a row of a columngroup dynamically.
Here's how I tried to realize this.

Code: Select all

<DataTable :value="calcValues">
        <template #header>
         {{ someCalculatedHeaderValue }}       
        </template>
        <ColumnGroup type="header">
          <Row>
            <Column header="Values / Years" />
            <!-- Here starts the problem -->
            <Column v-for="n of numberOfYears" :key="n">
             <template #body>{{ calcHeadingPerYear(n) }}</template> 
            </Column>
            <!-- Here ends the problem -->
          </Row>
        </ColumnGroup>
        <Column field="rowHeading" />
        <Column v-for="n of getNumberOfColumns" :key="n">          
          <template #body="slotProps">
            {{ slotProps.data.values[n].value}}
          </template>
        </Column>
      </DataTable>
Whilst within the "body" of the DataTable everything works fine, I cannot find a solution to dynamically create the "head" of the DataTable.
The browser console tells me: "Uncaught (in promise) TypeError: Array.prototype.filter called on null or undefined"

I also tried to wrap the <Column> inside a <div v-for="n of numberOfYears" :key="n"></div>, but with the same result.

Could you please give me a hint how to solve this?

Kind regards,
riverman

netsrac
Posts: 1
Joined: 07 Jan 2021, 13:31

07 Jan 2021, 15:11

Hi there,

I'm currently experiencing the same problem.

Dynamically defining columns inside of a column group using the v-for directive doesn't seem to work for me.

Code: Select all

<DataTable v-bind:value="data">
     <ColumnGroup type="header">
            <Row>
                <Column header="Nr" v-bind:rowspan="2" />
                <Column v-for="n in 4" v-bind:key="n" v-bind:header="n" v-bind:colspan="7"/>
            </Row>
            <Row>
                <Column v-for="n in 28" v-bind:key="n" v-bind:header="n" />
           </Row>
      </ColumnGroup>                
</DataTable>      
Exception:

Code: Select all

Uncaught (in promise) TypeError: Array.prototype.filter called on null or undefined
    at filter (<anonymous>)
    at renderComponentRoot (runtime-core.esm-bundler.js:717)
    at componentEffect (runtime-core.esm-bundler.js:4035)
    at reactiveEffect (reactivity.esm-bundler.js:42)
    at effect (reactivity.esm-bundler.js:17)
    at setupRenderEffect (runtime-core.esm-bundler.js:4018)
    at mountComponent (runtime-core.esm-bundler.js:3976)
    at processComponent (runtime-core.esm-bundler.js:3936)
    at patch (runtime-core.esm-bundler.js:3547)
    at mountChildren (runtime-core.esm-bundler.js:3736)
edit: I posted the wrong error message in my initial post

Kind regards
netsrac

javierrodrigogomez
Posts: 7
Joined: 27 Jan 2021, 19:11

01 Feb 2021, 14:34

Same problem here... I try to creat a group of columns over a dynamic group of columns and nothing happens

javierrodrigogomez
Posts: 7
Joined: 27 Jan 2021, 19:11

02 Feb 2021, 13:02

Another thing that apparently is not working with dynamic columns is the scrolling. If I activate it (flex or with a fixed value) (:scrollable="true" scrollHeight="flex" or :scrollable="true" scrollHeight="800px" ), the header and the columns do not match one below the other. Only the frozen row is correctly aligned with the hearder but the rest of the rows are not

Code: Select all

<template>
    <DataTable :value="ciclos" :frozenValue="frozenValue" :scrollable="true" scrollHeight="800px" class="p-datatable-gridlines" selectionMode="single">
        <template #empty>Cargando datos, por favor espere...</template>
        <template #header>
          <div class="table-header">
            Elección de ciclos
          </div>
        </template>
 
        <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field">
          <template #body="slotProps">
              <div :class="estiloCelda(slotProps.data, col.field)">
                {{ slotProps.data[col.field] }}
              </div>
          </template>        
        </Column>
    </DataTable>
</template>

javierrodrigogomez
Posts: 7
Joined: 27 Jan 2021, 19:11

04 Feb 2021, 21:52

I am not sure if this is just not working or I am doing something wrong. I want my column headers and just above a group column to group some of them. Do I have to create my table header in the group as well as I am trying to do in this code or is there any other way to do it. I am getting this error :"Uncaught (in promise) TypeError: Array.prototype.filter called on null or undefined"

Code: Select all

<template>
  <div v-if="columns.length > 10" style="height: 100vh">
    <DataTable ref="dt" :value="ciclos" :scrollable="true" scrollHeight="flex" class="p-datatable-responsive-demo p-datatable-gridlines" 
      v-model:selection="selectedProducts1" selectionMode="multiple" dataKey="Fecha">
        <template #header>
          <div class="p-d-flex p-jc-between">
            <div class="p-text-bold">Elección de ciclos</div>
            <Button icon="pi pi-external-link" label="Export" class="p-button-sm" @click="exportCSV($event)" />       
          </div>
        </template>
      <ColumnGroup type="header">
          <Row>
              <Column header="H" :colspan="4" />
              <Column header="A" :colspan="4" />
              <Column header="B" :colspan="4" />
          </Row> 
          <Row>
            <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" :headerStyle="estiloHeader(col.field)"></Column>
          </Row> 
      </ColumnGroup>
      <Column v-for="col of columns" :field="col.field" :key="col.field">
        <template #body="slotProps">
            <div :class="estiloCelda(slotProps.data, col.field)">
              {{ slotProps.data[col.field] }}
            </div>
        </template>        
      </Column>


      <!-- <Column v-for="col of columns" :field="col.field" :header="col.header" :key="col.field" :headerStyle="estiloHeader(col.field)">
        <template #body="slotProps">
            <div :class="estiloCelda(slotProps.data, col.field)">
              {{ slotProps.data[col.field] }}
            </div>
        </template>        
      </Column> -->
    </DataTable>
  </div>
  <div v-else class="Loading">
    <p>Cargando tabla de ciclos...</p>
    <br>
    <i class="pi pi-spin pi-spinner" style="fontSize: 2rem"></i>
  </div>
</template>

<script>
import DataTable from 'primevue/datatable'
import Column from 'primevue/column'
import Row from 'primevue/row'
import ColumnGroup from 'primevue/columngroup'
import Button from 'primevue/button'
import getCollection from '../composables/getCollection'
import { computed, ref } from 'vue'

export default {
    name: 'DetalleCiclos',
    components: { DataTable, Column, Button, Row, ColumnGroup },
    setup() {
      const { error: err1, documents: ciclos } = getCollection('Vs')
      const { error: err2, documents: plantilla } = getCollection('Plantilla')
      let columns = [null] //no hace falta que sea un ref para mandarlo como parámetro a Column en DataTable
      let ordenColumnas = [] //tiene los objectos de plantilla ordenados según la tabla que vamos a mostrar
      const camposFijos = ['Ciclo', 'Dia', 'Fecha', 'CupoRRHH', 'Id', 'createdAt', 'V+W'] 
      const selectedProducts1 = ref(null)
      const dt = ref(null)
      
      columns = computed(() => {
          ordenColumnas = plantilla.value.sort(function(a,b){ //array con las columnas ordenadas según OrdenTabla
            return a.OrdenTabla - b.OrdenTabla
          })
          const columnas = [
            {field: 'Ciclo', header: 'Ciclo'},
            {field: 'Dia', header: 'Dia'},
            {field: 'Fecha', header: 'Fecha'}
          ]
          ordenColumnas.forEach(cta => {
            columnas.push({field: cta.Cta, header: cta.Cta})
          })
          columnas.push(
            {field: 'V+W', header: 'V+W'},
            {field: 'CupoRRHH', header: 'CupoRRHH'})
        return columnas
      })

      const estiloHeader = (columna) => {
        //binding headerStyle a esta función para que el campo fecha sea un poco más ancho
        let estilo = ''
        if (columna === 'Fecha')
          estilo = 'width: 30px'
        else
          estilo = 'width: 25px'
        
        return estilo  
      }

      const estiloCelda = (data, columna) => {
        const estilo = [null]
        let cicloPar = true
        let ordenCtaTabla = 0
        let diaPrimerCiclo = 0

        if (!camposFijos.includes(columna)) { //es un campo de cta
          //calculo del ciclo al que pertenece el día según grupo del cta
          plantilla.value.find((elem) => {
            if (elem.Cta == columna){    
              diaPrimerCiclo = elem.DiaPrimerCiclo        
              cicloPar = ((Math.floor((data['Id']-diaPrimerCiclo)/8) + 1) % 2) //par o impar
              ordenCtaTabla = elem.OrdenTabla //orden del cta en las columnas
            }
          })        
        
          // bordes de las celdas
          let bordeDcho = false
          if (ordenCtaTabla != ordenColumnas.length) {
            if (ordenColumnas[ordenCtaTabla-1]['Grupo'] != ordenColumnas[ordenCtaTabla]['Grupo']) {
              bordeDcho = true
            }            
          }

          if ( data[columna] === '') //por alguna razón a las casillas vacías no les deja adjuntar ninguna clase
          data[columna] = '.'

          // w del año anterior, para darle otro color uso w20
          let w20 = false
          if ( data[columna] === 'w' && diaPrimerCiclo - data['Id'] > 0)
            w20 = true

          estilo.push([{
            'xp': ((data[columna] === 'x') || data[columna] === '.') && cicloPar,
            'xi': (data[columna] === 'x' || data[columna] === '.') && !cicloPar,
            'V': data[columna] === 'V' || data[columna] === 'w' ,
            'V20': data[columna] === 'V20' || w20,
            'Va': data[columna] === 'Va',
            'Bdcho': bordeDcho
          }])          
        } else {
          estilo.push([{
            'Fecha': columna === 'Fecha', //clase por defecto para no ctas
            'Bdcha': columna === 'Fecha',
            'Bizda': columna === 'V+W',
            'xp': data['Ciclo'] % 2 === 1,
            'xi': data['Ciclo'] % 2 === 0,
            'CupoRRHHmm1': columna == 'V+W' && data['CupoRRHH'] - data['V+W'] < -1, //para colorear V+W en funcion de la diferencia con CupoRRHH
            'CupoRRHHm1': columna == 'V+W' && data['CupoRRHH'] - data['V+W'] == -1,
            'CupoRRHH0': columna == 'V+W' && data['CupoRRHH'] - data['V+W'] == 0,
            'CupoRRHH1': columna == 'V+W' && data['CupoRRHH'] - data['V+W'] == 1,
            'CupoRRHH2': columna == 'V+W' && data['CupoRRHH'] - data['V+W'] > 1
          }])
        }
      return estilo //devuelve la clase a aplicar a la celda correspondiente
      }

      const exportCSV = () => {
        dt.value.exportCSV()
      }        
      return { ciclos, columns, estiloCelda, estiloHeader, selectedProducts1, exportCSV, dt }
    },
}

</script>

<style>
@media screen and (max-width: 600px) {
  div.p-datatable-header {
    display:none !important;
  }

  th,td {
    font-size: xx-small !important;
  }
}

td {
    padding: 0 !important;
    text-align: center;
    font-size: small;
    border-color: rgba(104, 152, 224, 0.521) !important;
}
th {
    padding: 0 !important;
    text-align: center !important;
    font-size: small;
    font-weight: bold !important;
    border-color: rgba(104, 152, 224, 0.521) !important;
}
.table-header {
    text-align: center;
} 
.p-highlight { /* cambia la manera de mostrar una selección de fila */
  filter: brightness(80%);
}
.xp {
    background: #e3ecac;
    text-align: center;
}
.xi {
    background: #c9ffc4d5;
    text-align: center;
    border-spacing: 0px !important;
}
.V {
    background: #1f720a;
    color:rgb(255, 255, 255);
    text-align: center;
}
.V20 {
    background: #b36dbd;
    color:rgb(10, 10, 10);
    text-align: center;
}
.Va {
    background: #000000;
    color:rgb(255, 255, 255);
    text-align: center;
}
.Fecha {
    white-space: nowrap;
}
.CupoRRHHmm1{
  background: blueviolet;
}
.CupoRRHHm1{
  background: rgb(226, 43, 43);
}
.CupoRRHH0{
  background: rgb(8, 214, 77);
}
.CupoRRHH1{
  background: rgb(241, 204, 240);
}
.CupoRRHH2{
  background: rgb(207, 100, 207);
}
.Bdcha{
  border-right: solid;
  border-color: black;
}
.Bizda{
  border-left: solid;
  border-color: black;
}
.Loading{
  margin: auto;
  text-align: center;
  width: 50%;
}
</style>

anniepyim
Posts: 1
Joined: 05 May 2021, 10:19

05 May 2021, 10:43

Hello I have the same issue here. The code for <Column> is not returning anything if it's put inside a <ColumnGroup>. Is this a limitation of the library or is there some other way to do that? Thanks!

Code: Select all

<template>
    <div>
        <DataTable :value="products" :paginator="true" class="p-datatable-customers" :rows="10" 
                dataKey="id" v-model:filters="filters2" filterDisplay="row" responsiveLayout="scroll">

            <template #header>
                <div class="p-d-flex p-jc-end">
                    <span class="p-input-icon-left ">
                        <i class="pi pi-search" />
                        <InputText v-model="filters2['global'].value" placeholder="Keyword Search" />
                    </span>
                </div>
            </template>

            <!-- <Column header="" :colspan="1" /> -->
            <ColumnGroup type="header">
                <Row>
                     <Column header="" :colspan="1" />
                </Row>
                <Row>
                    <Column header="" :colspan="1" />
                </Row>
                <Row>
                    <Column v-for="col of columns" :header="col.header" :field="col.field" :key="col.field" :sortable="col.sortable">

                        <template #filter="{filterModel,filterCallback}">
                            <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" :placeholder="`Search by name - `" v-tooltip.top.focus="'Hit enter key to filter'"/>
                        </template>
                        
                    </Column>
                </Row>
            </ColumnGroup>

            <Column v-for="col of columns" :header="col.header" :field="col.field" :key="col.field" :sortable="col.sortable">

                <template #filter="{filterModel,filterCallback}">
                    <InputText type="text" v-model="filterModel.value" @keydown.enter="filterCallback()" class="p-column-filter" :placeholder="`Search by name - `" v-tooltip.top.focus="'Hit enter key to filter'"/>
                </template>
                
            </Column>

        </DataTable>

    </div>
</template>


timothychurchill
Posts: 1
Joined: 16 Nov 2020, 14:56

12 May 2021, 11:14

We are also encountering the same problem.

Code: Select all

<ColumnGroup>
  <Row>
    <Column v-for...>
does not generate any columns. But the same <Column v-for...> block without the <ColumnGroup> and <Row> does generate columns. Any suggestions?!

riverman83
Posts: 3
Joined: 21 Dec 2020, 13:26

06 Jul 2021, 08:26

Did anyone find a solution for this yet?
I'm still encoountering this, without a clue how to solve this.

Thx in advance

saucerboy
Posts: 2
Joined: 04 Oct 2021, 00:37

04 Oct 2021, 00:40

I'm having the same problem with creating dynamic columns inside a column group. Has anyone found a solution? Do the PrimeVue people monitor this at all? I've been overall happy with the library but this limitation would mean we cannot use it in our product.


Post Reply

Return to “PrimeVue”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 2 guests