Slickgrid and promises

I am attempting to use aurelia-slickgrid as a means to represent tables of data gotten back from an http ajax call.

However, it appears that the slickgrid is firing too soon? I have a response being logged to console that shows my phone list, but the error happens before that.

here is my code:

customer.html:

<template if.bind="phone_list">
                                <aurelia-slickgrid 
                                    grid-id="phoneList" 
                                    column-definitions.bind="columnDefinitions" 
                                    grid-options.bind="gridOptions" 
                                    dataset.bind="phone_list"
                                    grid-height="400" 
                                    grid-width="800">
                                </aurelia-slickgrid>
</template>

customer.js:

    async findArisId() {
        const self = this;
        self.cookie = document.cookie.split("=");
        
        self.user = self.cookie[1];

        if ( self.user )
        {
            var url = 'http://aris2.myaris.net/search.pl';
            url += '?REMOTE_USER=' + self.user;

            console.log( 'url: ', url );

            if ( self.cust_num )
            {
              var newCustNum = self.cust_num.replace( /#/, '' );
                url += '&aris_id=' + newCustNum;
            }

            if ( self.cust_info )
            {
                url += '&cust_info=' + self.cust_info;
                url += '&cust_info_field=' + self.selectedCustInfo;
            }

            if ( self.invoice_number )
            {
                url += '&invoice_number=' + self.invoice_number;
            }

            if ( self.po_number )
            {
                url += '&po_number=' + self.po_number;
            }

//            console.log( 'url', url );

            return self.httpClient
            .fetch(url)
              .then(async (response) => {
                let aris_idObj = await response.json(); 
//                console.log( 'response: ', aris_idObj ); 
                this.customerRecord = 
                  await this.getCustomerData(aris_idObj.customerData.aris_id);
                if( this.customerRecord )
                {
                  this.status_code_list = await this.statusObj.getStatusCodes(this.customerRecord[0].customer.debtor_id);  
                  this.selectedStatusCode = this.customerRecord[0].customer.status_code;
     // PHONE LIST for Slickgrid
             self.phone_list = this.customerRecord[3].phone;
                  console.log('customerRecord[3]', this.customerRecord[3]);
                  console.log('phone_list', self.phone_list);
                  //console.log('status_code_list', this.status_code_list);
                }
                  return this.customerRecord;
              });
  
        }
    }

Of note:

I can’t use bind() or attached() as async calls to start the cycle, because I have to have a user enter in a customer number for it to display the record.

I’m unsure how to force slickgrid to init after the phone_list is filled in?

Thanks.

There’s not enough info in your post and I don’t understand your question. You mention some errors happening but you’re not including them in your post. The thing that you have to know is that SlickGrid always require the Column Definitions to be ready before you give it any data which is why I always create the grid first then get the data (with async data it’s always going to be later anyway). If you want to show a grid later, then I go with a if.bind on the grid container, which is what you’ve seem to have done but you’re not showing the code you have so it’s hard to pinpoint where your problem is… but again you need to make sure that the grid gets created before the data is passed. The Example 22 and its ViewModel has a fetch sample, you should follow it.

I don’t really understand why in your code you have a reference this selfinside an async function, you should try switching to using the fat arrow => in your code as much as possible and always the global this available.

Ok.

I do have the columnDefinitions being defined as so:

export default class Customer {
    stat;
    client;
    gridOptions;
    columnDefinitions;
    phone_list = [];

    constructor(client, stat, httpClient, router, taskQueue) {

        this.httpClient = new HttpClient();
        this.clientObj = client;
        this.statusObj = stat;
        this.router = router;
        this.defineGrid();

and then this:

      defineGrid() {
        this.columnDefinitions = [
          { id: 'descr', name: 'Name', field: 'descr', sortable: true, minWidth: 100 },
          { id: 'phone', name: 'Phone/Fax/Email', field: 'phone', sortable: true, minWidth: 100}
        ];

        this.gridOptions = { enableAutoResize: false };
      }

I can’t use attached like it is demonstrated in Example 22, because I rely on user input to get the data from the database.

I do have async calls, and it is properly filling out the data.

I have a <template if.bind="customerRecord"> that properly displays the html code if it’s returned data.

I have a <template if.bind="phone_list"> that is supposed to make sure the list is not empty before displaying it.

The errors I’m getting are these:

ncaught TypeError: Right-hand side of 'instanceof' is not an object
    at init (slick.grid.js?3a3f:322)
    at new SlickGrid (slick.grid.js?3a3f:5941)
    at AureliaSlickgridCustomElement.initialization (aurelia-slickgrid.js?f3e8:141)
    at AureliaSlickgridCustomElement.attached (aurelia-slickgrid.js?f3e8:98)
    at Controller.attached (aurelia-templating.js?8628:3783)
    at View.attached (aurelia-templating.js?8628:1788)
    at ViewSlot.add (aurelia-templating.js?8628:1951)
    at IfCore._show (aurelia-templating-resources.js?fcf2:249)
    at If._swap (aurelia-templating-resources.js?fcf2:324)
    at If._update (aurelia-templating-resources.js?fcf2:301)

and this:

Uncaught TypeError: Cannot read property 'autosizeColumns' of undefined
    at AureliaSlickgridCustomElement.datasetChanged (aurelia-slickgrid.js?f3e8:347)
    at BehaviorPropertyObserver.selfSubscriber (aurelia-templating.js?8628:3992)
    at BehaviorPropertyObserver.call (aurelia-templating.js?8628:3854)
    at BehaviorPropertyObserver.setValue (aurelia-templating.js?8628:3834)
    at AureliaSlickgridCustomElement.descriptor.set [as dataset] (aurelia-templating.js?8628:3947)
    at Object.setValue (aurelia-binding.js?5f98:3700)
    at Binding.updateTarget (aurelia-binding.js?5f98:4952)
    at Binding.call (aurelia-binding.js?5f98:4967)
    at SetterObserver.callSubscribers (aurelia-binding.js?5f98:328)
    at SetterObserver.call (aurelia-binding.js?5f98:3773)

What I am seeing leads me to believe it’s trying to call the init of Slickgrid too soon for me because phone_list is not filled out at that point.

My question is - how do I fill out the phone_list properly so that it will display in the grid?

You mention an if.bind on the container. Is this different than doing this:

<template if.bind="phone_list">
                                <aurelia-slickgrid 
                                    grid-id="phoneList" 
                                    column-definitions.bind="columnDefinitions" 
                                    grid-options.bind="gridOptions" 
                                    dataset.bind="phone_list"
                                    grid-height="400" 
                                    grid-width="800">
                                </aurelia-slickgrid>
                            </template>

Thanks again.

again you still seem to have a racing condition, I have to say it again the grid must be created before you provide it data (more like a house must be built before you can live in it), it looks like you’re giving everything at the same time but you really have to initialize/create the grid prior to loading data in it

I understand what you are saying, but I don’t know how to init the grid beforehand?

I have tried the following based on Example 22.

html:

<template if.bind="phone_list.length > 0">
                                <aurelia-slickgrid 
                                    grid-id="phoneGrid" 
                                    column-definitions.bind="columnDefinitions" 
                                    grid-options.bind="gridOptions" 
                                    dataset.bind="phone_list"
                                    grid-height="400" 
                                    grid-width="800"
                                    asg-on-aurelia-grid-created.delegate="loadGrid($event.detail)">
                                </aurelia-slickgrid>
                            </template>

and js:

import './style.css';
import {$, jquery} from 'jquery';
import {Client} from './client';
import {inject, TaskQueue} from "aurelia-framework";
//import { DOM } from "aurelia-pal";
import 'aurelia-bootstrap-datepicker';
import {Status_Codes} from './status_code';
import {commas, check_flag} from './utils';
import {HttpClient} from 'aurelia-fetch-client';
import {Router} from 'aurelia-router';
//import dataTable from 'datatables';

//import chosen from 'chosen';
//import select2 from './include/select2.full.min.js'
import {
  AureliaGridInstance,
  Column,
  FieldType,
  Filters,
  GridOdataService,
  GridOption,
  GridStateChange,
  Metrics,
  OdataOption,
  OdataServiceApi,
  OperatorType,
} from 'aurelia-slickgrid';

window.jQuery = jquery;
window.$ = jquery;

@inject(Client, Status_Codes, HttpClient, Router, TaskQueue, AureliaGridInstance)

export default class Customer {
    stat;
    client;
    gridOptions;
    columnDefinitions;
    phone_list = [];
    phoneGrid = AureliaGridInstance;
    

    constructor(client, stat, httpClient, router, taskQueue) {

        this.httpClient = new HttpClient();
        this.clientObj = client;
        this.statusObj = stat;
        this.router = router;
        this.defineGrid();
        this.taskQueue = taskQueue;
        //this.phoneGrid = AureliaGridInstance;
        this.clients;
}

    async findArisId() {
        const self = this;
        self.cookie = document.cookie.split("=");
        
        self.user = self.cookie[1];

        if ( self.user )
        {
            var url = 'http://aris2.myaris.net/search.pl';
            url += '?REMOTE_USER=' + self.user;

            console.log( 'url: ', url );

            if ( self.cust_num )
            {
              var newCustNum = self.cust_num.replace( /#/, '' );
                url += '&aris_id=' + newCustNum;
            }

            if ( self.cust_info )
            {
                url += '&cust_info=' + self.cust_info;
                url += '&cust_info_field=' + self.selectedCustInfo;
            }

            if ( self.invoice_number )
            {
                url += '&invoice_number=' + self.invoice_number;
            }

            if ( self.po_number )
            {
                url += '&po_number=' + self.po_number;
            }

//            console.log( 'url', url );

            return self.httpClient
            .fetch(url)
              .then(async (response) => {
                let aris_idObj = await response.json(); 
//                console.log( 'response: ', aris_idObj ); 
                this.customerRecord = 
                  await this.getCustomerData(aris_idObj.customerData.aris_id);
                if( this.customerRecord )
                {
                  this.status_code_list = await this.statusObj.getStatusCodes(this.customerRecord[0].customer.debtor_id);  
                  this.selectedStatusCode = this.customerRecord[0].customer.status_code;
                  self.phone_list = this.customerRecord[3].phone;
                  console.log('customerRecord[3]', this.customerRecord[3]);
                  console.log('phone_list', self.phone_list);
                  //console.log('status_code_list', this.status_code_list);
                }
                  return this.customerRecord;
              });
  
        }
    }

 defineGrid() {
        this.columnDefinitions = [
          { id: 'descr', name: 'Name', field: 'descr', sortable: true, minWidth: 100 },
          { id: 'phone', name: 'Phone/Fax/Email', field: 'phone', sortable: true, minWidth: 100}
        ];

        this.gridOptions = { enableAutoResize: false };
      }

 loadGrid(aureliaGrid)
      {
        this.phoneGrid = aureliaGrid;
      }
}

But I am still getting this error:

Uncaught TypeError: Right-hand side of 'instanceof' is not an object
    at init (slick.grid.js?3a3f:322)
    at new SlickGrid (slick.grid.js?3a3f:5941)
    at AureliaSlickgridCustomElement.initialization (aurelia-slickgrid.js?f3e8:141)
    at AureliaSlickgridCustomElement.attached (aurelia-slickgrid.js?f3e8:98)
    at Controller.attached (aurelia-templating.js?8628:3783)
    at View.attached (aurelia-templating.js?8628:1788)
    at ViewSlot.add (aurelia-templating.js?8628:1951)
    at IfCore._show (aurelia-templating-resources.js?fcf2:249)
    at If._update (aurelia-templating-resources.js?fcf2:304)
    at If.conditionChanged (aurelia-templating-resources.js?fcf2:292)

AureliaGridInstance is not injectable, that is only an instance of the grid after it’s ready. Remove that from your inject, it’s only available via the asg-on-aurelia-grid-created. I understand that you don’t want to use the attached but I would strongly suggest to first get a grid working then after change it to a dynamically loaded grid.

Ok - So if I do use attached, is there a way to delay it?

I’m asking because I keep mentioning that the page is already loaded before I even get data to get from the database via the user?

Or can I call attached again?

Is my loadGrid used appropriately? I had a hard time translating from Typescript.

thanks so much for your patience.

The data can be passed later to the grid, just make sure you create it (with column definitions) before sending the data.

I’m not sure if you understood what the asg-on-aurelia-grid-created does, you only need it if you need to access the SlickGrid grid, dataView objects, or any of the Aurelia-Slickgrid services. I made it this way so that we can have multiple grids in the same page and each will have it own services (non-singleton) and doing something on 1 grid won’t affect the other grid. For a simple grid, you won’t need asg-on-aurelia-grid-created