Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way in aurelia to bind on a custom element using repeat.for

Using Aurelia I am struggling with binding and repeat.for : suppose I have in my viewmodel with a property menuItems (an array of MenuItem) I would like to repeat the menuitems with a custom template :

export class App {
    menuItems : MenuItem[];
}
export class MenuItem{
   label:string;
}

In my app template I use use a custom element

<require from="./menu-item"></require>
<ul>
  <menu-item repeat.for="item of menuItems"></menu-item>
</ul>

My custom template (menu-item.html):

<template>
  <li>${label}</li>
</template>

What is the correct way the get the template bound or access the bound MenuItem?

I've tried the following: ${label} and ${item.label} but that does not work. I can see in the bind(bindingContext) callback that bindingContext has a property 'item' : bindingContext.item which is the MenuItem that is being bound.

I also tried to create bindable attribute on the MenuItem class:

export class MenuItem{
   @bindable current any;
   label:string;
}

and the following repeater:

<menu-item repeat.for="item of menuItems" current.bind="item"></menu-item>

and a corresponding template

<template>
  <li>${current.label}</li>
</template>

note: see edit 1 below, for my comment on this point in the code.

This approach also does not work.

Other explorations included not using a custom element (worked), using <compose view-model='MenuItem', model.bind='item'/>, does not work in this example either and would be to elaborate, I think.

Working solution, see also Aurelia repeat.for binding on a custom element :

repeat and bind on a custom attribute of the template and viewmodel class:

<menu-item repeat.for="item of menuItems" current.bind="item" containerless></menu-item>

The viewmodel class:

import {bindable, customElement} from 'aurelia-framework'

@customElement('menu-item')
export class MenuItem{
  label = "default label";
  @bindable current;

  constructor(label){
    this.label = label;
  }
  attached(){
    console.log("MenuItem = attached");
  }
}

Edit 1:

I see in the output the html of templates repeated for as many there are MenuItems. But these items are not bound: the <li> is empty. I expected to see the labels.

Edit 2:

in this post I typed "items in menuItems", that's not correct it should be "item of menuItems". But that was not the cause of my struggling, I had it mis-typed in this post only

Edit 3:

@jeremy-danyow suggested that

  • html should be well formed: menu-item within ul is not correct (using containerless attribute helps by removing the menu-item element)
  • custom elements should always have a closing tag I have adapted the code. Also I made a plunker: Aurelia repeat.for binding on a custom element

That plunker works, provided a @bindable attribute;

like image 351
Arjan Avatar asked Mar 16 '16 11:03

Arjan


1 Answers

Browsers only allow li elements inside ul elements. <ul><menu-item></menu-item></ul> is invalid markup in the same way that <ul><div></div></ul> is invalid markup.

Consider making a <menu> element whose template contains the ul and it's li elements instead.

One other thing- only a few standard elements are "self closing"- eg inputs, etc. Custom elements must always have a closing tag. Bad: <menu-item /> Good: <menu-item></menu-item>

You might be wondering "why can't aurelia figure this out for me?"

Valid question. Here's why: Aurelia does not implement a custom html parser. It uses the DOM to parse standard HTML. This is different than other frameworks folks might be accustomed to that implement custom parsers for non-standard markup syntaxes.

like image 192
Jeremy Danyow Avatar answered Oct 04 '22 05:10

Jeremy Danyow