Search code examples
typescriptaurelia

Aurelia, long list of bindables on custom element. Refactor?


I have a repeating card-type custom element that is bound to JSON data pulled from an api. My current method of creating and binding the data to these cards as they are displayed works, but everything about it screams "REFACTOR" to me. Is there a better way to achieve these ends with Aurelia?

My current custom element pattern looks like this

pager-view.html

  <!-- Loop through the JSON task-data (each card contains the chosen task-data to display) -->
  <template repeat.for="task of pageData[currentPage - 1]">

    <!--Bind each tasks data to a card as we loop-->
    <card-commit

      task-data.bind="task"
      task-id.bind="task.ID"
      task-name.bind="task.name"
      project-id.bind="task.project.ID"
      project-name.bind="task.project.name"
      assigned-to.bind="task.assignedTo.name"
      successors.bind="task.successors"
      commit-status.bind="task.commitStatus"
      planned-start-date.bind="task.plannedStartDate"
      planned-comp-date.bind="task.plannedCompletionDate"
      duration.bind="task.duration"
      actual-start-date.bind="task.actualStartDate"
      commit-date.bind="task.commitDate"
      condition.bind="task.condition"
      constraint.bind="task.taskConstraint"
      constraint-date.bind="task.constraintDate"
      status.bind="task.status"
      note.bind="task.lastNote"
      note-text.bind="task.lastNote.noteText"
      note-entry-date.bind="task.lastNote.entryDate"
      note-avatar-download-url.bind="task.lastNote.owner.avatarDownloadURL"
      note-owner-name.bind="task.lastNote.owner.name"
      actual-completion-date.bind="task.actualCompletionDate"
    ></card-commit>

  </template>

card-commit.ts

import {bindable, inject} from 'aurelia-framework';

export class CardCommit {
  @bindable public taskData;
  @bindable public taskId;
  @bindable public taskName;
  @bindable public projectId;
  @bindable public projectName;
  @bindable public assignedTo;
  @bindable public successors;
  @bindable public commitStatus;
  @bindable public plannedStartDate;
  @bindable public plannedCompDate;
  @bindable public duration;
  @bindable public actualStartDate;
  @bindable public actualStartDelta;
  @bindable public commitDate;
  @bindable public condition;
  @bindable public conditionClass;
  @bindable public conditionText;
  @bindable public constraint;
  @bindable public constraintDate;
  @bindable public status;
  @bindable public note;
  @bindable public noteText;
  @bindable public noteEntryDate;
  @bindable public noteAvatarDownloadUrl;
  @bindable public noteOwnerName;
  @bindable public updateNoteText;
  @bindable public actualCompletionDate;

  constructor() {
    // ... do constructor stuff
  }

  // ... other methods etc
}

card-commit.html

<!-- Do all sorts of stuff with the bound data, for example... -->
<template>
  <!-- This example wouldn't really work, just demonstrating how I'm using the bound data -->
  <article data-task-id="${ taskId }">
    <ul repeat.bind="for example of task">
      <li data-example-commit="example.commitDate">${example.condition}</li>
    </ul>
  </article>
</template>

Maybe I'm being overly nit-picky, but if feels like I should be able to define this relationship in at least a more compact way, specifically the long list bindings being defined (essentially) twice. But I'm just not sure how else I could achieve this and haven't been able to really find much on the subject/issue specifically.


Solution

    • You can you just bind the object to a class attribute (like I did with task-data.bind="task" in pager-view.html)

    • Use this object to reference what you want directly in the template.

    So revised would be something like this...

    pager-view.html

      <!-- Loop through the JSON task-data (each card contains the chosen task-data to display) -->
      <template repeat.for="task of pageData[currentPage - 1]">
    
        <!--Bind each tasks data to a card as we loop-->
        <card-commit
          task-data.bind="task"
        ></card-commit>
    
      </template>
    

    card-commit.ts

    import {bindable, inject} from 'aurelia-framework';
    
    export class CardCommit {
      @bindable public taskData;
    
      constructor() {
        // ... do constructor stuff
      }
    
      // ... other methods etc
    }
    

    card-commit.html

    <!-- Now we reference the data that we want directly on the `taskData` attribute -->
    <!-- Do all sorts of stuff with the bound data, for example... -->
    <template>
      <!-- This example wouldn't really work, just demonstrating how I'm using the bound data -->
      <article data-task-id="${ taskData.taskId }">
        <ul repeat.bind="for example of taskData.task">
          <li data-example-commit="example.commitDate">${example.condition}</li>
        </ul>
      </article>
    </template>
    

    HOWEVER, (and maybe this is possible, but I can't find any references to it), it would be really nice to be able to inject/pass the taskData to the class constructor, sort of like this...

    pager-view.html

    <!--Bind each tasks data to a card as we loop-->
    <card-commit
      card-commit.constructor="task"
    ></card-commit>
    

    And then be able to merge it in the constructor of the class, maybe something like this:

    card-commit.ts

    import {bindable, inject} from 'aurelia-framework';
    
    export class CardCommit {
      @bindable public taskData;
    
      constructor(taskData) {
        Object.entries(taskData).forEach(([key, val]) => {
           Object.assign(this, {[key]: val});
        });
      }
    
      // ... other methods etc
    }
    

    Then we could access the properties directly on the object, like in the initial code presented for refactor.

    If I get the time to find a way to accomplish this I'll update. In the meantime, if anyone else knows how to do it, I'd gladly accept that as the answer in lieu of this.