Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A workaround for repeat.for and paper-tabs, paper-menu, iron-selector, paper-listbox... #23

Closed
HIRANO-Satoshi opened this issue Aug 15, 2016 · 14 comments

Comments

@HIRANO-Satoshi
Copy link

HIRANO-Satoshi commented Aug 15, 2016

When you have a trouble with paper-tabs, paper-radio-button, paper-menu, paper-listbox... anything that uses iron-selector inside to select an item, try this workaround.

iron-selector selects one from child elements but ignores child child elements inside a child element. (It seems not always, though.)

In the following example, the is sometimes ignored because it is not a direct child element of the .

 <paper-tabs>
    <div>
      <paper-tab>tab 0</paper-tab>
    </div>
 </paper-tabs>

When you use repeat.for, Aurelia wraps things in a element. So, no generated element can be selected. (See #17 for more detail.)

A workaround is au-select attribute that gathers all child elements and child child elements under or similar elements.

In the following example, paper-tabs works au-select with

.
Give selectable="CSS selector" to iron-selector and give class="CSS selector" to selectable elements. (note: * selectable=".selectable_item" requires ".")

      <paper-tabs au-select selectable=".selectable_item">
        <div>
          <paper-tab class="selectable_item" repeat.for="item of items">${item}</paper-tab>
        </div>
      </paper-tabs>

This is a TypeScript version of the au-select custom attribute.

// resources/au-select-custom-attribute.ts
//    add  aurelia.use.plugin('resources/au-select-custom-attribute') to main.ts
import { autoinject } from 'aurelia-framework';

@autoinject
export class AuSelectCustomAttribute {
    constructor(private element: Element) {
        (<any>element)._updateItems = function() {
            //var nodes = (<any>window).Polymer.dom(this).queryDistributedElements(this.selectable || '*');
            var nodes = (<any>window).Polymer.dom(this).querySelectorAll(this.selectable || '*');
            nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
            this._setItems(nodes);
        }
    }   
}

This is a JavaScript version.

// resources/au-select-custom-attribute.js
//    add  aurelia.use.plugin('resources/au-select-custom-attribute') to main.js
//import {inject} from 'aurelia-framework';

export class AuSelectCustomAttribute {
    constructor(element) {
        element._updateItems = function() {
            //var nodes = window.Polymer.dom(this).queryDistributedElements(this.selectable || '*');
            var nodes = window.Polymer.dom(this).querySelectorAll(this.selectable || '*');
            nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
            this._setItems(nodes);
        }
    }   
}

The above code was borrowed from PolymerElements/iron-selector#42.

Or, the following code can be added to main.ts to replace the default behavior, if you don't want to use the au-select attribute.

export function configure(aurelia: Aurelia) {
  (<any>window).Polymer.IronSelectableBehavior._updateItems = function() {
      //var nodes = win.Polymer.dom(this).queryDistributedElements(this.selectable || '*');
     var nodes = (<any>window).Polymer.dom(this).querySelectorAll(this.selectable || '*');
      nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
      this._setItems(nodes);
    }

@bnavetta
Copy link
Owner

This is great, thanks! Would you mind opening a PR to add the code replacing the default behavior and/or the au-select attribute?

@HIRANO-Satoshi
Copy link
Author

Okay, I will try.

@HIRANO-Satoshi
Copy link
Author

Today paper-tabs and repeat.for work without au-select. I can't reproduce the issue and I'm confused.

@HIRANO-Satoshi
Copy link
Author

Here is a simple skeleton of selection with repeat.for.

Although this is an example of paper-dropdown-menu, the pattern is common to others such as paper-tabs.

It shows a list of country names. You will have countryCode after selection.
If you set a country code to the countryCode, the country is pre-selected.

      <paper-dropdown-menu label="Country" >
        <paper-listbox class="dropdown-content"
              selected.two-way="countryCode" attr-for-selected="id" au-select selectable=".item">
          <div>
             <paper-item repeat.for="data of countryData" id.bind="data.alpha3" class="item">
                  ${data.name}
              </paper-item>
           </div>
        </paper-listbox>
      </paper-dropdown-menu>
export class MyViewModel {
    countryData: any = [
          {alpha3: 'BHR', name: 'Bahrain'},
          {alpha3: 'JPN', name: 'Japan'}, 
          {alpha3: 'HUN', name: 'Hungary'}
   ];
   countryCode: string = 'JPN';    // pre-selection
}

@bnavetta
Copy link
Owner

Hmm, was there an Aurelia upgrade?

@HIRANO-Satoshi
Copy link
Author

I have just sent a PR. Please merge it, if it is good.

@bnavetta
Copy link
Owner

Merged!

@wc-matteo
Copy link

It's not working for me (I'm using fd482e6):

Unhandled rejection Error: Error invoking AuSelectCustomAttribute. Check the inner error for details.
------------------------------------------------
Inner Error:
Message: key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?

I checked and _aureliaFramework.Element in au-select-custom-attribute.js is, in fact, undefined...

markup

<paper-dropdown-menu>
    <paper-listbox class="dropdown-content" selected="0" au-select selectable=".item">
        <div>
            <paper-item repeat.for="i of 10" class="item">${i}</paper-item>
        </div>
    </paper-listbox>
</paper-dropdown-menu>

@bnavetta
Copy link
Owner

Should've been fixed by #25

@wc-matteo
Copy link

wc-matteo commented Sep 20, 2016

I'm using the latest commit and it's not fixed... now that I notice, the files in /dist haven't been updated with the fix, right?

Yep, that was it. Building the source fixes it.

@wc-matteo
Copy link

wc-matteo commented Sep 20, 2016

After some testing I must say that this is not a good workaround...

In the case of paper-tabs the added <div> interferes with Polymer styles.
Trying to change the bound collection of a paper-listbox programmatically doesn't work (the elements stop being selectable).

The only good solution I found at the moment is to use Polymer's own repeater:

<paper-tabs>
  <template is="dom-repeat" items.bind="items">
    <paper-tab>{{item}}</paper-tab>
  </template>
</paper-tabs>

@HIRANO-Satoshi
Copy link
Author

@wc-matteo, I confirmed your code works. It solves some annoying issues such as font size of tabs in the original workaround. Great invention.

I am not familiar with Polymer's data biding. How do you use it with Aurelia's data binding?

For example, a tab includes tag usually. With the following code, item.title appears in the tabs, but does not work as a link.

<paper-tabs>
  <template is="dom-repeat" items.bind="router.navigation">
    <paper-tab link>
      <a href.bind="item.href">{{item.title}}</a>
    </paper-tab>
  </template>
</paper-tabs>

@wc-matteo
Copy link

wc-matteo commented Sep 21, 2016

@HIRANO-Satoshi

All credits go to: aurelia/binding#262

I'm not familiar with Polymer's data binding myself, but I found this.

In short, the syntax is: <a href$="{{item.href}}">. Strangely, in this case the attribute (i.e. href) is attached to the autogenerated parent div (i.e. <div class="tab-content style-scope paper-tab">) in the final rendering.

@HIRANO-Satoshi
Copy link
Author

I tried the href$="{{item.href}}" and confirmed it does not work.

The fact you found is strange.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants