1

I have a multiselect-dropdown.html with it's own .js, as well as an edit-user.html with it's own .js.

I'm trying to make it trigger a function in edit-user.html when I click a checkbox or a list-item in the multiselect-dropdown.html.

I find the aurelia docs a bit lacking in information.

I don't think I need to paste the js files, I just need direction on how to call a function in edit-user.html from a list-item in multiselect-dropdown.html is it possible? some hints would help.

<template>
  <div class="dropdown">
    <button id="${baseId}" 
            class="dropdown-toggle form-control input" 
            type="button" 
            data-toggle="dropdown" 
            aria-haspopup="true" 
            aria-expanded="false">
      <span class="multiselect-dropdown-selection">${textCaption}</span>
      <span class="caret"></span>
    </button>

    <ul class="dropdown-menu input" 
        aria-labelledby="${baseId}" 
        click.trigger="preventDropDownClose($event)">
      <li if.bind="!disableOriginal">
        <a>
          <label>
            <input 
              type="checkbox" 
              id="multiselect-dropdown-select-default" 
              checked.bind="isSelectOriginal"> 
            ${textSelectOriginal}
          </label>
        </a>
      </li>

      <li>
        <a>
          <label>
            <input 
              type="checkbox" 
              id="multiselect-dropdown-select-all" 
              checked.bind="isSelectAll">
            ${textSelectAll}
          </label>
        </a>
      </li>

      <li role="separator" class="divider"></li>

      <li repeat.for="option of options" onmousedown=>
        <a>
          <label>
            <input // This should send a trigger to a function in edit-user.html
              type="checkbox" 
              class="multiselect-dropdown-option"
              checked.bind="option.checked"> ${option.name}        
          </label>
        </a>
      </li>
    </ul>
  </div>
</template>

Here is the multi-select in edit-user.html, how do I call a function in edit-user.html if a checkbox has been checked or unchecked in multiselect-dropdown.html?

    <div class="form-group user-multiselect-dropdown">
      <label for="address" class="regular-15-25">Mina valda adresser</label>
      <multiselect-dropdown 
        options.bind="selectedAddresses"
        noun-singular="adress"
        noun-plural="adresser">
      </multiselect-dropdown>
    </div>
Jesse
  • 3,522
  • 6
  • 25
  • 40
Kokefa
  • 85
  • 9

2 Answers2

0

You need to bind a function to your <multiselect-dropdown> using a .call attribute like so:

<multiselect-dropdown function-name.call="checkboxChanged()"></multiselect-dropdown>

You can bind to this in your viewmodel of the multiselect-dropdown like this:

@bindable()
public functionName: () => void; // or without typing if you don't use typescript

Mind the casing of the function name; in HTML, what you seperate with a dash (-) is camelCased in your viewmodel, e.g. function-name becomes functionName

Now you just need to call that function in your viewmodel whenever the checkbox changes. You can observe the value of the checkbox using the @observable decorator and call the function whenever it changes. If you want to observe a property of an object you need to use the bindingEngine instead.

import { bindable, observable, bindingEngine, autoinject } from "aurelia-framework";

@autoinject
export class Foo {
  @bindable()
  public functionName: () => void;

  @observable()
  public checkboxValue: boolean;

  public option = {
    checked: true;
  }

  constructor(private bindingEngine: BindingEngine) {}

  attached() {
    // subscribe to an object property
    this.subscription = this.bindingEngine.propertyObserver(this.option, "checked").subscribe(() => {
       this.functionName();
    }
  }

  detached() {
    // Dispose subscription to avoid memory leak
    this.subscription.dispose();
  }

  // Observe a single field for changes
  checkboxValueChanged() {
    this.functionName();
  }
}
Jesse
  • 3,522
  • 6
  • 25
  • 40
  • We are not using TypeScript for our project, is this solveable without the use of typescript? – Kokefa Aug 30 '18 at 13:42
  • If you remove the typings, it should still work. You'd just have `@bindable functionName;` – Jesse Aug 30 '18 at 13:43
0

Jesse's answer is one of the two valid approaches to this. The other approach is to dispatch a CustomEvent from within your custom element, and subscribe to it from the view that consumes the custom element.

Inside multiselect-dropdown.js:

// without your JS it's hard to assume what you need, but here's the gist of it
// assuming this method is called in some way in response to a change
onSelectionChanged() {
    const detail = {
        component: this, // include whatever information the consuming view needs
    };
    const event = new CustomEvent('select', { bubbles: true, detail });
    this.el.dispatchEvent(event); // assuming this.el is the injected Element instance
}

In edit-user.html:

<multiselect-dropdown select.trigger="handleSelect($event)">
</multiselect-dropdown>

In edit-user.js:

handleSelect($event) {
    const detail = $event.detail;
    // do stuff with detail.component or whichever info you included
    return false; // stops the event from bubbling further up
}

As you can see, in terms of .call versus dispatching a custom event, they have a lot of overlap in terms of which problems you can solve with them.

My personal preference for invocations that originate from DOM events is to keep them as events. It allows you to stick closer to the natural way of working with HTML when you need to pass changes further up the DOM tree.

.call on the other hand results in less objects and listeners being created and may result in better performance (lower memory usage in particular) in very large applications. It also gives you less to worry about in terms of cleaning up / canceling propagation and such.

Both have their time and places, use whichever makes sense in your situation.

Fred Kleuver
  • 7,797
  • 2
  • 27
  • 38
  • Alright, I'm sure I have seen a similar approach somewhere in the code that I will check further into, I will need to work on this problem again in the coming function I'm going to implement and will try both yours and jesse's – Kokefa Sep 03 '18 at 11:29