Error on api.rows().select() method angular datatables

Error on api.rows().select() method angular datatables

JDanielJDaniel Posts: 13Questions: 1Answers: 0

Hello everyone

Like the title says, I'm implementing datatables with the angular wrapper and in the same component I have two data tables,
In the first one I have the first column with a checkbox for every row and in the header as well, this is for painting additional info on the selected rows after the click of a button, and this already works fine, and with those rows filled with info I click another button that takes me to the second table where I put those rows and with checkboxes on the first column too

I need to calculate a number of the rows in the second table, but for that they have to be selected and there is where the problem arises, in my case I use dtInstance. Rows(). Select () but when the table is shown, any row is selected, and the console throws this error:

ERROR Error: Uncaught (in promise): TypeError: ctx._select is undefined

After is painted the table, I click the header checkbox and all the rows are selected without problem, but I needed to select right after the table is drawn

If its of any help, before i try to select thw rows,

i execute the next code:

          dtInstance.clear();
          console.log("--Limpiando tabla--")

        //Destroying the table
        dtInstance.destroy();

        //Calling dtTrigger to rerender again
        this.dtTrigger1.next("");

Any kind of help would be appreciated, I'm stuck at this:(

This question has an accepted answers - jump to answer

Answers

  • colincolin Posts: 15,143Questions: 1Answers: 2,586

    We're happy to take a look, but as per the forum rules, please link to a test case - a test case that replicates the issue will ensure you'll get a quick and accurate response. Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.

    Cheers,

    Colin

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0
    edited May 2022

    Thanks @colin for the clarification

    I'm sorry for the delay on answering, but now i can provide the page im working on

    Here is the link:
    http://201.149.34.171/market-place/

    Replication steps:
    Click on "Registrate" button,

    click on "Aceptar" button,

    click on "Seleccionar una red" combo and select "BAZ"

    click on "Ingrese un rango de fechas" icon on the right of the input
    and select 6 and 7, next click on "Aceptar" button (the dates don't matter; its just for quick example)

    Click on 2 rows of the table and click on "Aceptar button"

    click on "Siguiente" button on the below part of the page

    And that's it; the rows are shown correctly, but they should be selected, and the error is on the console.
    (sorry, i tried to put the error on alert or somewhere visible, but try catch or return value of method did not get the error)

    I hope this can be of help,

  • kthorngrenkthorngren Posts: 20,299Questions: 26Answers: 4,769

    I see this error:

    ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'style')
    TypeError: Cannot read properties of undefined (reading 'style')

    Generally with Datatables that means there is a mismatch in the number of columns in the HTML table and what Datatables is trying to use, like this thread. Looks like the main.cfed7c3f87718428.js that is being called is compiled/minimized so its impossible to try and debug. Can you post an example that has non-minimized code so we can help debug?

    Kevin

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Hey @kthorngren

    The main file its now without minify/optimize maybe can give a little more insight now.
    The file is REALLY big now tough :')
    I hope that isn't a problem

    http://201.149.34.171/market-place/

    About the error that took me by surprise O_O

    Apparently, the error that i first posted only shows on firefox, and on edge and chrome throws

    ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'style')
    TypeError: Cannot read properties of undefined (reading 'style')

    I review my table structure regarding the possible reason for the error, but my columns are hardcoded in html.
    the rows are painted with a ng for iterating an array, and the datatable is initialized when the array is already filled with all the items.

  • kthorngrenkthorngren Posts: 20,299Questions: 26Answers: 4,769

    Seems like your test steps might have changed. I don't see this option:

    click on "Seleccionar una red" combo and select "BAZ"

    Please provide the updated steps to replicate the issue. I took a quick look at main.xx.js and find a lot of code to look through. Seeing the message so we can find the area where the problem occurs will be helpful.

    Use the traceback provided to look at the code area that is throwing the error. Hopefully you can see something.

    Kevin

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    @kthorngren

    Sorry about the steps, that was a problem with the API U_U, but it is already solved, and the steps are still the same

    Here are the problems that are thrown in the console:

    Firefox:

    Edge/Chrome:

    Now having witnessed the other error, i searched for it and like you said, its ussually due to differences between headers and rows on the table or bad targeting on columnDefs

    I couldn't find a problem, but I posted the code of the table for reference.

    HTML:

    <table datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger1" id="tablaPlayers" class="table table-responsive stripe hover" style="text-align: center">
                                <thead>
                                    <tr>
                                        <th scope="col" style="text-align: center">
                                            <input [(ngModel)]="selectAllRows1" (ngModelChange)="selectAllChange1($event)" type="checkbox">
                                        </th>
                                        <th scope="col">Nombre sucursal</th>
                                        <th scope="col">Pantallas (Num)</th>
                                        <th scope="col">Disponibilidad</th>
                                        <th scope="col">Fechas</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr *ngFor="let item of arrayPlayers">
                                        <td scope="row"></td>
                                        <td>{{item.Nombre_Sucursal}}</td>
                                        <td>{{item.Pantallas}}</td>
                                        <td>
                                            <!-- {{item.Segundos}} -->
                                            OK
                                        </td>
                                        <td>{{item.Fecha}}</td>
                                    </tr>
                                </tbody>
                            </table>
    

    Typescript table definition:

    this.dtOptions[1] = {
          // jQueryUI: true,
          responsive: true,
          pageLength: 10,
          language: {
            "url": "//cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Spanish.json",
            select: {
              rows: {
                _: "%d filas seleccionadas",
                0: "",
                1: "1 fila seleccionada"
              }
            }
          },
          dom: "rtpi",
          select: '{style:multi, selector: td:first-child}',
          columnDefs: [{
            orderable: false,
            className: 'select-checkbox',
            targets: 0
          },{
            "bVisible": false,
            "aTargets": [0,1,2,3,4]
          }],
          // ,
          // rowCallback: (row: Node, data: any[] | Object, index:number) =>{
          //   const self = this;
          //   $('td', row).off('click');
          //   $('td', row).on('click', () =>{
          //     this.someClickHandler(data);
          //   });
          //   return row;
          // }
        }
    

    When i finish to fill arrayPlayers i call to this method:

      rerender1(limpiar: boolean): void {
        this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
          dtElement.dtInstance.then((dtInstance: any) => {
    
            let tablaId = dtInstance.table().node().id;
            if (tablaId == "tablaPlayers") {
              dtElement.dtInstance.then((dtInstance: any) => {
                //Esto es para limpiar la data de la tabla
                if (limpiar) {
                  dtInstance.clear();
                  console.log("--Limpiando tabla--")
                }
    
                //Destroying the table
                dtInstance.destroy();
                console.log("--Destruyendo tabla--");
    
                //Calling dtTrigger to rerender again
                this.dtTrigger1.next("");
                console.log("--Activando rerenderizacion--");
    
                // console.log("Se seleccionaran los players");
                // dtInstance.rows({}).select();
                // let selectedRows = dtInstance.rows({ selected: true }).count();
                // console.log("Players seleccionados:" +selectedRows);
                this.selectAllChange1(true);
    
    
              })
            }
          });
        });
      }
    

    And just in the final line i call selectAllChange1(), where i select the rows and the error arises:

      selectAllChange1(event: any) {
        // console.log("Se cambio la seleccion:" + event);
        if (event) {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  // console.log("Estas son las filas:"+ Object.values(dtInstance.rows()));
                  let cuenta = dtInstance.rows().count();
                  // console.log("Numero de filas encontradas");
                  // console.log(cuenta);
                  console.log("Se intentara seleccionar filas en tabla players");
                  dtInstance.rows().select();
                })
              }
            });
          });
    
    
        } else {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  dtInstance.rows().deselect();
                })
              }
            });
          });
        }
    
      }
    

    Maybe this can be of more help

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    @kthorngren

    About the steps that was my bad U_U, it was due to an API error but now is solved, and the steps are the same as before.

    The error that the console is throwing are the next:

    Edge/Chrome:

    Firefox:

    Thanks to knowing the error on chrome and edge i could search for it and like ayou said earlier, it was probably due to a mismatch between headers number and rows number or bad targeting on columnDefs, i verified and couldn´t find something bad but for reference i post the code here:

    HTML:

    <table datatable [dtOptions]="dtOptions[1]" [dtTrigger]="dtTrigger1" id="tablaPlayers" class="table table-responsive stripe hover" style="text-align: center">
                                <thead>
                                    <tr>
                                        <th scope="col" style="text-align: center">
                                            <input [(ngModel)]="selectAllRows1" (ngModelChange)="selectAllChange1($event)" type="checkbox">
                                        </th>
                                        <th scope="col">Nombre sucursal</th>
                                        <th scope="col">Pantallas (Num)</th>
                                        <th scope="col">Disponibilidad</th>
                                        <th scope="col">Fechas</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr *ngFor="let item of arrayPlayers">
                                        <td scope="row"></td>
                                        <td>{{item.Nombre_Sucursal}}</td>
                                        <td>{{item.Pantallas}}</td>
                                        <td>
                                            <!-- {{item.Segundos}} -->
                                            OK
                                        </td>
                                        <td>{{item.Fecha}}</td>
                                    </tr>
                                </tbody>
                            </table>
    

    Typescript table definition:

    this.dtOptions[1] = {
          // jQueryUI: true,
          responsive: true,
          pageLength: 10,
          language: {
            "url": "//cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Spanish.json",
            select: {
              rows: {
                _: "%d filas seleccionadas",
                0: "",
                1: "1 fila seleccionada"
              }
            }
          },
          dom: "rtpi",
          select: '{style:multi, selector: td:first-child}',
          columnDefs: [{
            orderable: false,
            className: 'select-checkbox',
            targets: 0
          }],
          // ,
          // rowCallback: (row: Node, data: any[] | Object, index:number) =>{
          //   const self = this;
          //   $('td', row).off('click');
          //   $('td', row).on('click', () =>{
          //     this.someClickHandler(data);
          //   });
          //   return row;
          // }
        }
    

    Where the error arises is after I fill arrayPlayers on the next method:

     getSeleccionadas() {
    
        this.arrayPlayers.length = 0;
        let rangoFechas = this.fechaInicio + " - " + this.fechaFin;
    
        this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
          dtElement.dtInstance.then(async (dtInstance: any) => {
    
            let tablaId = dtInstance.table().node().id;
            if (tablaId == "tablaSucursales") {
    
              let tempArrayPlayers: Array<Player> = [];
              let sumaCotizacion: number = 0;
    
              dtElement.dtInstance.then(async (dtInstance: any) => {
                let selectedRows = dtInstance.rows({ selected: true }).count();
                let data = dtInstance.rows({ selected: true }).data();
    
                for (let index = 0; index < selectedRows; index++) {
                  const element = data[index].toString();
    
                  let Sucursal = element.split(',')[1];
                  let id_Sucursal = element.split(',')[4];   
    
                  // let foundItem = this.arraySucursales.find(x => x.Id_Sucursal == id_Sucursal);    
                  let foundItem = this.arraySucursales.find( function(element){
                    return element.Id_Sucursal == id_Sucursal;
                  })
    
                  // console.log("Sucursal encontrada con id "+id_Sucursal +":");
                  // console.log( Object.values(foundItem!) );
                  let segundos = Number(foundItem!.Segundos);
                  let dias = Number(foundItem!.DiasDisponibles);
                  let Red = foundItem!.Id_Negocio;
    
                  let cotiTemp = {
                    // fechaInicial: this.fechaInicio,
                    // fechaFinal: this.fechaFin,
                    dias: dias,
                    red: Red,
                    sucursales: 1
    
                    // sucursales: this.contadorSucursalesCotizacion
                  }
    
                  let Coti = await this.controllerService.getCotizacion(cotiTemp).toPromise();
                  this.Cotizacion = Coti.data.cotizacion;
                  sumaCotizacion += this.Cotizacion;
    
                  // console.log("El valor de cotizacion es "+sumaCotizacion);
    
                  let playerTemp: Player = {
                    Id_Player: null,
                    Fecha: rangoFechas,
                    Nombre_Sucursal: Sucursal,
                    Id_Sucursal: null,
                    Nombre_Player: null,
                    Segundos: segundos,
                    Pantallas: foundItem!.Pantallas,
                    Cotizacion: this.Cotizacion
                  }
    
                  if (dias > 0) {
                    tempArrayPlayers.push(playerTemp);
                    // this.arrayPlayers.push(playerTemp);
                  }else{
                    // console.log("No disponible para pautar");
                  }              
                }
    
    
                // this.CotizacionMostrar =  Math.ceil(sumaCotizacion);
                // this.Cotizacion = this.CotizacionMostrar
                this.Cotizacion = Math.ceil(sumaCotizacion);
                this.interfazPlayers = true;
                this.arrayPlayers = tempArrayPlayers;
    
                this.rerender1(true);
                // this.selectAllChange1(true);
              })
    
    
            }
          });
        });
    
      }
    

    In rerender1() is where i destroy and update the table data:

    rerender1(limpiar: boolean): void {
        this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
          dtElement.dtInstance.then((dtInstance: any) => {
    
            let tablaId = dtInstance.table().node().id;
            if (tablaId == "tablaPlayers") {
              dtElement.dtInstance.then((dtInstance: any) => {
                //Esto es para limpiar la data de la tabla
                if (limpiar) {
                  dtInstance.clear();
                  console.log("--Limpiando tabla--")
                }
    
                //Destroying the table
                dtInstance.destroy();
                console.log("--Destruyendo tabla--");
    
                //Calling dtTrigger to rerender again
                this.dtTrigger1.next("");
                console.log("--Activando rerenderizacion--");
    
                // console.log("Se seleccionaran los players");
                // dtInstance.rows({}).select();
                // let selectedRows = dtInstance.rows({ selected: true }).count();
                // console.log("Players seleccionados:" +selectedRows);
                this.selectAllChange1(true);
    
    
              })
            }
          });
        });
      } 
    

    And in the final line i call selectAllChange1() where i select the rows and the error arises:

    selectAllChange1(event: any) {
        // console.log("Se cambio la seleccion:" + event);
        if (event) {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  // console.log("Estas son las filas:"+ Object.values(dtInstance.rows()));
                  let cuenta = dtInstance.rows().count();
                  // console.log("Numero de filas encontradas");
                  // console.log(cuenta);
                  console.log("Se intentara seleccionar filas en tabla players");
                  dtInstance.rows().select();
                })
              }
            });
          });
    
    
        } else {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  dtInstance.rows().deselect();
                })
              }
            });
          });
        }
    
      }
    

    I hope this can be of more help and thanks for the help i already received.

  • kthorngrenkthorngren Posts: 20,299Questions: 26Answers: 4,769

    Your code is minimized again. I looked at your above code snippets but don't see anything obvious. I would start by putting a breakpoint at the line the causes the error. Maybe from there you can determine what is undefined.

    Maybe the problem is this:

                //Destroying the table
                dtInstance.destroy();
                console.log("--Destruyendo tabla--");
    

    After this you are calling selectAllChange1() which is trying to access the Datatable API to perform operations. Not sure if its the same Datatable you just destroyed but if ti is then that might be the cause fo the error.

    Kevin

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Sorry, i forgot the minimized part again U_U, its complete now

    When i debugged the error just when dtInstance.rows().select() was executed, the console.logs prior to it throw me correctly the data but on the selection the error is thrown,

    selectAllChange1(event: any) {
        // console.log("Se cambio la seleccion:" + event);
        if (event) {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  // console.log("Estas son las filas:"+ Object.values(dtInstance.rows()));
                  let cuenta = dtInstance.rows().count();
                  // console.log("Numero de filas encontradas");
                  // console.log(cuenta);
                  console.log("Se intentara seleccionar filas en tabla players");
                  dtInstance.rows().select();
                })
              }
            });
          });
    
    
        } else {
    
          this.dtElements.forEach((dtElement: DataTableDirective, index: number) => {
            dtElement.dtInstance.then((dtInstance: any) => {
    
              let tablaId = dtInstance.table().node().id;
              if (tablaId == "tablaPlayers") {
                dtElement.dtInstance.then((dtInstance: any) => {
                  dtInstance.rows().deselect();
                })
              }
            });
          });
        }
    
      }
    

    About

    dtInstance.destroy()

    i think

    this.dtTrigger1.next("");
    

    Recreates the table again and let me modify the table with instance or the logs before select() wouldn´t throw anything :(

  • kthorngrenkthorngren Posts: 20,299Questions: 26Answers: 4,769
    edited June 2022

    I placed a breakpoint here:

    dtInstance.rows().select();

    When select is called the Datatables select.js uses a function clear() to clear the current selections. This is where the problem occurs:

    Its looking for ctx._select as part of the Datatables settings but it doesn't exist:

    if ( force || ctx._select.style === 'single' )
    

    Looking back at your Datatable config you have this:

    select: '{style:multi, selector: td:first-child}',
    

    The single quotes around the config is making it a string not an object so select is not enabled. Remove the quotes, like this:

    select: {style:multi, selector: td:first-child},
    

    This should add select capabilities to the Datatable and it should work.

    Kevin

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Thanks for the answer @kthorngren !

    I tried changing the select options, and debugging, I confirmed that now it's taking the select options as it should :smile: but the error keeps appearing :(

    debugging the object of the clear() method, this is what it gave

    This is the object of the table that is working fine:

    And this is the object of the table that is throwing the eror:

    So for some reason,it looks like the _select doesn´t exist in the ctx object of the second table,

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0
    edited June 2022

    Duplicated comment

  • allanallan Posts: 61,710Questions: 1Answers: 10,103 Site admin
    Answer ✓

    That might happen if the DataTable is initialised before it is inserted into the document. If that happens, you need to call:

    DataTable.select.init( table );
    

    Allan

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Wow! thanks a lot @allan

    I tried and worked like a charm, sorry for the troubles :')

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Hello again

    The rows are now selected just after the table is painted :D

    But the rows doesn´t select when i click over them, i put a rowCallback that prints every click over row and works fine but the row doesn´t click :(

  • allanallan Posts: 61,710Questions: 1Answers: 10,103 Site admin

    Could you give me an updated list of instructions on how to show the error please? I'm getting this.arraySucursales[0] is undefined when clicking Aceptar after selecting two dates now.

    Allan

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    Hello @allan , here are the updated steps (about the problem that you mention that was due to the API not being available :') )

    • Scroll to bottom
    • Click on 'CONOCE MÁS'
    • Click on 'UNIRME'
    • Click on "Seleccione un negocio" combo and select "BAZ"
    • Click on "Ingrese un rango de fechas" icon on the right of the input and select 6 and 7
    • Click on "Aceptar" button (the dates don't matter; its just for quick example)
    • Click on 2 rows of the table and click on "Aceptar button"
    • Click on "Siguiente" button on the below part of the page
  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    By the way, the API that is connected to the page has some problems

    between 11:00am and 12:00pm
    and
    between 2:00pm and 3:00pm UTC-06:00

    So if someone has viewed the page and doesn't work as it should,
    is probably due to that, anyway, thanks again for all the help :)

  • allanallan Posts: 61,710Questions: 1Answers: 10,103 Site admin

    Click on "Seleccione un negocio" combo and select "BAZ"

    I don't see any such option I'm afraid:

    Allan

  • JDanielJDaniel Posts: 13Questions: 1Answers: 0

    @allan I´m terribly sorry :(

    The API is affected by other processes, and gives errors, but i just correct it right now.
    I dont know how to thank you for all the patience and help

Sign In or Register to comment.