Lance G
Lance G

Reputation: 144

"attached" or DOM-render equivalent for nested view-model.ref

We have a page, "parent", which references a template via the view-model.ref called "child" in the parent.html. We change the data of this child template by clicking on items on the parent page which invokes the child function using OpenDetailsDiv. Say I use a button for this event like below:

parent.html

<child view-model.ref="clientusertasks"></child>
<input type="button" value="Click Me" click.trigger="OpenDetailsDiv" />

In this manner we can invoke a function on the "child" view-model from the parent view-model like so:

parent.js

import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';
import 'fetch';
import AuthService from 'AuthService';
import { BpoClientUserTasks } from './bpo-client-user-tasks';

@inject(HttpClient, AuthService, BpoClientUserTasks)
export class Parent {
  smallDivObj = {};
  freq = '';
  period = '';
  filterVal = '';
  client = '';

  constructor(http, AuthService, BpoClientUserTasks) {

    http.configure(config => {
      config
        .withBaseUrl("WebServices.asmx/")
        .withDefaults({
          headers: {
            'Accept': 'application/json'
          }
        });
    });

    this.http = http;
    this.auth = AuthService;
    this.clientusertasks = BpoClientUserTasks;
  }


  OpenDetailsDiv(myObject) {
    this.clientusertasks.CallBPOClientUserService(this.freq, this.period, this.filterVal, myObject.TrueClient, myObject.Client);
  }
}

All good so far. The "child" view-model has this function CallBPOClientUserService which looks like the following:

child.js

import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';
import 'fetch';
import AuthService from 'AuthService';

@inject(HttpClient, AuthService)
export class Child {
  smallDivObj = {};

  constructor(http, AuthService) {

    http.configure(config => {
      config
        .withBaseUrl("WebServices.asmx/")
        .withDefaults({
          headers: {
            'Accept': 'application/json'
          }
        });
    });

    this.http = http;
    this.auth = AuthService;
  }

  attached() {

  }

  CallBPOClientUserService(freq, period, filterVal, client, displayClient) {
    $('#TasksByClientByUserDiv').addClass("fade");

    this.freq = freq;
    this.period = period;
    this.filterVal = filterVal;
    this.client = client;

    var mymethod = {
      method: 'post',
      body: JSON.stringify({
        "session": this.auth.session,
        "Client": client,
        "FreqFilter": freq,
        "FilterVal": filterVal
      }),
      headers: {
        'content-type': 'application/json'
      }
    };
    //alert(JSON.stringify(mymethod));
    this.http.fetch('GetBPOTasksByClientByUserDiv', mymethod)
      .then(response => response.json())
      .then(info => {
        this.tasksByClientByUser = JSON.parse(info.d);

        //setTimeout("$('#TasksByClientByUserTable').tablesorter();", 100);  
      });
  }
}

Notice that in the function CallBPOClientUserService we wish to call a tablesorter sort function to sort our table in the view AFTER the DOM has been rendered.

Usually I would call upon this function in the "attached" component lifecycle of the view-model. But you can see that the manner in which we are populating this view is from the view-model.ref of the "parent" page which renders the "attached" component for "child" useless for this case (it's only called once after all when the parent is loaded).

So to my question:

Is there an equivalent attached-like component that I tap into to call this tablesorter function?

I have a cheap work-around where I can use a setTimeout which I have commented in the function, but I'd rather do this correctly in Aurelia events and have a guarantee that the DOM has finished.

Upvotes: 0

Views: 372

Answers (1)

Lance G
Lance G

Reputation: 144

I believe I have 2 solutions to this problem which I'm satisfied with and will post them here.

The first is the recommendation above by Fabio to use the microTaskQueue.

Another solution is to use a custom bindable event to invoke the function on completion of the repeat.for on the table here...

<template>
<require from='../tablesorter-bind'></require>

<section id="TasksByClientDiv" class="SmallDivPanel ui-draggable BIBulletinSection100 SmallDivSection hellfire">
    <small-div-header smalldivinfo.bind="smallDivObj"></small-div-header>

    <div id="TasksByClientBox">
        <div style="margin-top: 10px;font-size: 20px;">Frequency: ${freq} / Period: ${filterVal}</div>

        <div id="TasksByClientTableDiv" class="SmallDivMainPanel">
            <table id="TasksByClientTable" >
                <thead class="tablesorter">
                    <tr>
                        <th>Client</th>
                        <th>Received</th>
                        <th>Prepped</th>
                        <th>Loaded</th>
                        <th>Doc Loaded</th>
                    </tr>
                </thead>

                <tbody>
                    <tr click.trigger="OpenDetailsDiv(x)" repeat.for="x of tasksByClient" table-id.bind="tableId">
                        <td>${x.Client}</td>
                        <td>${x.totLoaded}</td>
                        <td>${x.totLoaded}</td>
                        <td>${x.totPrepped}</td>
                        <td>${x.numDocLoaded}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</section>
</template>

where tableId is defined in the View-Model as my tableID

I then setup the custom element like so:

tablesorter-bind.js

import {inject, customAttribute, TaskQueue} from 'aurelia-framework';

@customAttribute('table-id')
@inject(Element, TaskQueue)
export class TablesorterBind {
constructor(element, taskQueue) {
    // "element" will be the DOM element rendered from the template     
    this.element = element;
    this.taskQueue = taskQueue;
}    

attached() {

}

bind(bindingContext, overridingContext) {
    if (overridingContext.$last === true) {
        this.taskQueue.queueMicroTask(
            () => {
                //This is the jQuery update call to the tablesorter function
                $('#' + this.value).trigger('update');
            }
        );
    }
}

}

Upvotes: 0

Related Questions