Filling an array with Ajax/Json data from another view

I have the need to fill a dropdown in multiple places, so I made it be its own javascript file (client.js).

When I run the ajax, it fills the array properly, but when I try to import/inject that class into my other class where I need the array, it’s null.

I admit I don’t have the fullest understanding of import/inject, and how that impacts data across classes.

Here’s my code:

client.js:

import jquery from 'jquery';

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

export class Client {
    constructor()
    {
        this.client_list = null;
        this.valid_client_list();
        this.cookie = null;
    }

    
    
    valid_client_list() {

        const self = this;

        self.cookie = document.cookie.split("=");
        self.user = self.cookie[1];

        if ( self.user )
        {
            var url = 'http://aris2.myaris.net/client_list.pl';

            jquery.ajax({
                type: "GET",
                url: url, // URL of the Perl script
                contentType: "application/json; charset=utf-8",
                dataType: "jsonp",
                headers: { 'Access-Control-Allow-Origin': '*' },
                mode: 'no-cors',
                crossDomain: true,
                jsonp: 'callback',
                // send username to build client list
                data: "REMOTE_USER=" + self.user,
                // script call was *not* successful
                error: function(XMLHttpRequest, textStatus, errorThrown) { 
                  console.log("responseText: " + XMLHttpRequest.responseText 
                    + ", textStatus: " + textStatus 
                    + ", errorThrown: " + errorThrown);
                }, // error 
                // script call was successful 
                // data contains the JSON values returned by the Perl script
                success: function(data){
                  if (data.error) { // script returned error
                    console.log( 'error: ' + data.error );
                  } // if
                  else { // got the list
                    //for (var i = 0; i < data.clients.length; i++ )
                    //{
                    //    console.log( 'client: ' + i + ' ' + data.clients[i] );
                    //}
                    
                    self.client_list = data.clients;           
                  } //else
                } // success
              }); // ajax
        }

        return self.client_list;
    }
}

and the view where I’m trying to use it:

customer.js:

import './style.css';
//import jquery from 'jquery';
import {Client} from './client';

@inject(Client)

export class Customer {
    constructor(client) {

        this.selectedClient = 0;

        this.client = client;

        this.selectedClient = 0;
        this.clients = this.client.valid_client_list();   

        console.log( 'client_list: ' + this.clients );

        this.selectedCustInfo = 0;

        this.custInfoField = [
            { value: 'All', display: 'All' },
            { value: 'Name', display: 'Name' },
            { value: 'Contacts', display: 'Contacts' },
            { value: 'City', display: 'City' },
            { value: 'My Notes', display: 'My Notes' },
            { value: 'All Notes', display: 'All Notes' },
            { value: 'Report As', display: 'Report As' },
            { value: 'Parent Customer', display: 'Parent Customer' },
        ];
    
    }
}
1 Like

I think if you have your valid_client_list() returns a Promise instead, it will make your workflow much easier.

1 Like

How do I return a promise? I’m not familiar with promises and how they work.

1 Like

You wrap the function with a new Promise and return that. Inside the promise resolve with the data of jQuerys success. Here is a tutorial about promises to give you an idea. https://javascript.info/promise-basics

1 Like

Ok. I wrapped my ajax call in a promise like so:

client.js:

import jquery from 'jquery';

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

export class Client {
    constructor()
    {
        this.client_list = null;
        this.valid_client_list();
        this.cookie = null;
    }

    
    
    valid_client_list() {

        const self = this;

        self.cookie = document.cookie.split("=");
        self.user = self.cookie[1];

        if ( self.user )
        {
            var url = 'http://aris2.myaris.net/client_list.pl';

            return new Promise( function(resolve, reject) {
              jquery.ajax({
                type: "GET",
                url: url, // URL of the Perl script
                contentType: "application/json; charset=utf-8",
                dataType: "jsonp",
                headers: { 'Access-Control-Allow-Origin': '*' },
                mode: 'no-cors',
                crossDomain: true,
                jsonp: 'callback',
                // send username to build client list
                data: "REMOTE_USER=" + self.user,
                // script call was *not* successful
                error: function(XMLHttpRequest, textStatus, errorThrown) { 
                  console.log("responseText: " + XMLHttpRequest.responseText 
                    + ", textStatus: " + textStatus 
                    + ", errorThrown: " + errorThrown);
                }, // error 
                // script call was successful 
                // data contains the JSON values returned by the Perl script
                success: function(data){
                  if (data.error) { // script returned error
                    console.log( 'error: ' + data.error );
                  } // if
                  else { // got the list
                    //for (var i = 0; i < data.clients.length; i++ )
                    //{
                    //    console.log( 'client: ' + i + ' ' + data.clients[i] );
                    //}
                    
                    resolve(data);
                    
                    //self.client_list = data.clients;           
                  } //else
                } // success
              }); // ajax
            });

            
        }
    }
}

and then in customer.js, I am trying to access the list. I’m still not clear how to access it. I want to use it to iterate over in a select->dropdown in the html.

customer.js:

import './style.css';
import jquery from 'jquery';
import {Client} from './client';
import {inject} from 'aurelia-framework';


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

@inject(Client)

export class Customer {
    constructor(client, api) {

        this.client = client;

        this.selectedClient = null;
        this.clients = this.client.valid_client_list().then( function(data) {
            console.log( '1 client_list: ' + data.clients );
        });

        console.log( '2 client_list: ' + this.clients );

        //console.log( 'data: ' + data.response );

        this.selectedCustInfo = null;

        this.custInfoField = [
            { value: 'All', display: 'All' },
            { value: 'Name', display: 'Name' },
            { value: 'Contacts', display: 'Contacts' },
            { value: 'City', display: 'City' },
            { value: 'My Notes', display: 'My Notes' },
            { value: 'All Notes', display: 'All Notes' },
            { value: 'Report As', display: 'Report As' },
            { value: 'Parent Customer', display: 'Parent Customer' },
        ];   
    }
}

When I log to console in the promise line, I get the data I expect. But when I try to log the value of this.clients, it’s undefined.

What do I need to do to access the data for html?

1 Like

Also of note:

console.log( '2 client_list: ' + this.clients ); runs before the console.log( '1 client_list: ' + data.clients );

I am not sure what I need to change?

1 Like

@rhysshadow Couple of pointers :

  1. Is there any reason for not using fetch? IMO it would make your code a lot simpler.
  2. A rule of thumb is to not do async operations in ctor. IMO it can be refactored to something like this:
export class Client {
  constructor()
  {
    this.client_list = null;
    this.cookie = null;
  }

  async getClientList() {
    let clientList = this.client_list;
    if(clientList !== null) { return clientList; }

    //assuming you have configured the fetch client as shown in docs      
    clientList = this.client_list = await this.fetchClient
          .fetch(endpoint, {
              method: 'GET',
               // other necessary  options
           })
           .then((response) => response.json());

     return clientList;
  }
}

And then from your customer.js you need to do something like this: this.clients = await this.client.getClientList() and move this piece of code out of ctor to the one of the lifecycle hooks like bind, etc.

Some useful links:

3 Likes

I did some reading, and I feel like I have a basic understanding of promises now.

But I am still running into problems.

I made an async getClientList() as suggested, and did the following to customer.js:

import './style.css';
import jquery from 'jquery';
import {Client} from './client';
import {inject} from 'aurelia-framework';


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

@inject(Client)

export class Customer {
    constructor(client, api) {

        this.client = client;

        console.log( 'this.client: ' + this.client );
        this.clientList();

        console.log( 'client constr: ' + this.client );
        this.selectedClient = null;
        this.clients = null;

        
        this.clients = this.client.getClientList();
        
        console.log( 'clients 1 ' + this.clients );
 
       this.selectedCustInfo = null;

        this.custInfoField = [
            { value: 'All', display: 'All' },
            { value: 'Name', display: 'Name' },
            { value: 'Contacts', display: 'Contacts' },
            { value: 'City', display: 'City' },
            { value: 'My Notes', display: 'My Notes' },
            { value: 'All Notes', display: 'All Notes' },
            { value: 'Report As', display: 'Report As' },
            { value: 'Parent Customer', display: 'Parent Customer' },
        ];
    
    }

    async clientList () {
        clients = await this.client.getClientList();
        console.log( 'async clients: ' + clients );
        return clients;
    }
}

I am getting the following error:

Uncaught (in promise) TypeError: Cannot read property 'fetch' of undefined

It appears to think that my this.client is null, even though when I log it to console, it shows as an object.

I’m not sure what else to try right now.

1 Like

This needs to be awaited

this.clients = this.client.getClientList();

Which is again a bit troublesome in constructors. So either add a .then and wrap your code inside or move that part and the rest into a lifecycle hook like attached/created/bind and use await

this.clients = await this.client.getClientList();

1 Like

I added a then like this:

this.clients = this.clientList().then();

but I am still getting the undefined error. Maybe I am using then() wrong?

I think it has to do with how this.client is defined? But I don’t see how.

Thanks for explaining, I’m sure it’s something simple I’m missing in all the reading I’m doing. But I just can’t see it right now.

1 Like

Update:

I did some more reading about then, and did the following:

this.clients = this.clientList().then(accept, reject);

and then this:

   async clientList () {

            console.log( 'async client: ' + this.client );
            function handler( accept, reject )
            {
            
            console.log( 'async clients: ' + clients );
            //return clients;
            accept(clients);
        }

        let clients = await this.client.getClientList();
    }

I am still getting the undefined error for fetch.

1 Like