2

So I found how to build and create an Aurelia Custom Element dynamically via a View/View Slot with the code shown below, however, I don't know how to get its instance (well I found a way but it's ugly)

Here's the util service I have to dynamically create a custom element

export class AureliaUtilService {
  constructor(
    private container: Container,
    private viewCompiler: ViewCompiler,
    private viewResources: ViewResources,
  ) { }

  createAureliaViewModelAddToSlot(templateUrl: string, bindableData: any, targetElement?: HTMLElement | Element, clearTargetContent = false): AureliaViewOutput | null {
    const viewFactory = this.viewCompiler.compile('<template><compose view-model.bind="template"></compose></template>', this.viewResources);

    if (targetElement) {
      if (clearTargetContent && targetElement.innerHTML) {
        targetElement.innerHTML = '';
      }

      // Creates a view
      const view = viewFactory.create(this.container);
      const bindings: any = { template: (templateUrl || ''), ...bindableData };

      view.bind(bindings, createOverrideContext(bindings));

      // Add the view to the slot
      const viewSlot = new ViewSlot(targetElement, true);
      if (viewSlot && viewSlot.add) {
        viewSlot.add(view);
      }
      return { view, viewSlot };
    }
    return null;
  }
}

then I call it this way and my custom element is rendered on the screen, yay

const targetElm = document.querySelector('.container');
const templateUrl = PLATFORM.moduleName('custom-element');
const bindings = { collection: this.collection };
const dynamicElement = this.aureliaUtilService.createAureliaViewModelAddToSlot(templateUrl, bindings, targetElm, true);

but now I want and need to modify some properties of that Custom Element, how can I get it's instance? By logging the object in the console, I kinda found the instance by going deep down (really deep that is), but it's ugly, so there must be a better way!?

Here's the deep dive ugly way I found the instance

// first I somehow need to wait the dynamically created Custom Element to fully rendered
// for that I found that I can wrap it in a setTimeout for a full lifecycle to pass
const instance = dynamicElement.view.children[0].children[0].container.viewModel;

// now I can change any of its properties
instance.selectedId = 123;

// I also need to watch for a property changes
// I found that I can do it this way
instance.selectedItemChanged = (item => console.log('item changed', item));

So yeah it works, but there has to be a better way...right?

ghiscoding
  • 12,308
  • 6
  • 69
  • 112
  • 1
    You don't need to check `viewSlot && viewSlot.add`, it's just instantiated. For getting the view model, you can do `view-model.ref` on `` element and from there, you can get the currently rendered view model via `viewModel.currentViewModel` I think – bigopon Feb 25 '20 at 12:27
  • 1
    thanks I got that to work and it's much shorter, however it seems I still seem to need the `setTimeout` for a cycle. Any last suggestion on that? Also I'd be happy to accept your answer if you write it back. – ghiscoding Feb 26 '20 at 01:56
  • Yes, I think we'll need a PR to add the ability to notify when a `` has finished its current composition. Will you be helping with a PR for that, please :D – bigopon Feb 28 '20 at 10:25

0 Answers0