aurelia / framework

The Aurelia 1 framework entry point, bringing together all the required sub-modules of Aurelia.
MIT License
11.76k stars 623 forks source link

Injecting Element do not work with @templateController #1011

Closed euglv closed 2 days ago

euglv commented 2 days ago

I'm submitting a bug report

Please tell us about your environment:

Current behavior:

@templateController

and

@inject(Element)

do not work together for custom attribute. <!--anchor--> Coment element is passed to constructor instead of actual DOM element:

@templateController
@inject(BoundViewFactory, ViewSlot, Element)
export class RedBoxCustomAttribute {
  constructor(factory, slot, element) {
    this.factory = factory;
    this.slot = slot;
    this.element = element;
    console.log(element) // <!--anchor--> is logged to console
  }

https://gist.run/?id=6a2ce66ed37b552dcfd2614d76cbe8fa&sha=531b9a54616b6b872e3e7d0eb498d6e8af00487a

euglv commented 2 days ago

https://gist.run/?id=6a2ce66ed37b552dcfd2614d76cbe8fa&sha=531b9a54616b6b872e3e7d0eb498d6e8af00487a

I have figured out that BoundViewFactory is responsible for creating DOM elements. In my example I can store element in the end of bind method:

  bind(bindingContext, overrideContext) {
    let newContext = { };
    overrideContext = createOverrideContext(newContext, overrideContext);
    if (!this.view) {
      this.view = this.factory.create();
      this.view.bind(newContext, overrideContext);
      this.slot.add(this.view);
    } else {
      this.view.bind(newContext, overrideContext);
    }
    this.element = this.view.firstChild;
  }
euglv commented 3 hours ago
this.element = this.view.firstChild;

This is not correct way to store DOM element reference in template controller custom attribute. If custom attribute is used together with repeat.for it can refer several DOM nodes. To cover all cases template controller should store array of DOM elements:

bind(bindingContext, overrideContext) {
    let newContext = { };
    overrideContext = createOverrideContext(newContext, overrideContext);
    if (!this.view) {
      this.view = this.factory.create();
      this.view.bind(newContext, overrideContext);
      this.slot.add(this.view);
    } else {
      this.view.bind(newContext, overrideContext);
    }
    const elements = [];
    let element = view.firstChild;
    if (element instanceof Element)
      elements.push(element);
    while (element && element != view.lastChild) {
      element = element.nextElementSibling;
      if (element)
        elements.push(element);
     }
     this.elements = elements;
  }