3

Edit: This question used to be titled "Getting parent via DI when parent is the same type in Aurelia" but due to how my elements are nested, it makes more sense to just bind the parent to the element, so the title has been changed to reflect this.

If I have a custom element, Thing which has a child Thing (which has a another child Thing, etc), how can I inject the parent instance when the class is the same?

export class Thing {
    static inject = [Thing]; // <-- no reference to Thing as we are inside the class
    constructor(parentThing) {
        this.parent = parentThing;
    }
}

As a further complexity, the root Thing element will have no parent, so the DI needs to allow for optional injection.

mark
  • 1,953
  • 1
  • 24
  • 47
  • May I know why you insist on using DI for this case? – qtuan Apr 28 '16 at 10:13
  • I thought it made sense to use DI since you can normally inject the parent via DI. Your approach is a much better strategy though. – mark May 01 '16 at 20:21

3 Answers3

2

I don't think your problem can be solved with DI. Thing has to be @transient if you want to have several instances of it. This means that the container will not hold references to things it creates.

Sylvain
  • 19,099
  • 23
  • 96
  • 145
1

This problem doesn't look right or necessary to use DI. If an element need to receive some specific input data from its consumer, @bindable would be my natural first thinking. So how about creating a @bindable parentThing in Thing?

In other hand, if what you want is to access parent binding context, consider bind() component life cycle.

qtuan
  • 687
  • 4
  • 10
0

Here's how to do that: https://gist.run?id=b075366d29f2400d3cc931f6fc26db24

app.html

<template>
  <require from="./thing"></require>

  <thing name="A">
    <thing name="B">
      <thing name="C">
        <thing name="D">
        </thing>
      </thing>
    </thing>
  </thing>
</template>

app.js

export class App {
}

optional-parent.js

import {resolver} from 'aurelia-dependency-injection';

@resolver()
export class OptionalParent {
  constructor(key) {
    this.key = key;
  }

  get(container) {
    if (container.parent && container.parent.hasResolver(this.key, false)) {
      return container.parent.get(this.key)
    }
    return null;
  }

  static of(key) {
    return new OptionalParent(key);
  }
}

thing.html

<template>
  <h3>
    My Name is "${name}".
    <span if.bind="parent">My parent's name is "${parent.name}".</span>
    <span if.bind="!parent">I don't have a parent.</span>
  </h3>
  <content></content>
</template>

thing.js

import {bindable, inject} from 'aurelia-framework';
import {OptionalParent} from './optional-parent';

@inject(OptionalParent.of(Thing))
export class Thing {
  @bindable name;

  constructor(parent) {
    this.parent = parent;
  }
}
Jeremy Danyow
  • 26,470
  • 12
  • 87
  • 133
  • Unfortunately, In my code, `Thing` resolves to undefined both outside the class in thing.js and in the constructor of OptionalParent. This appears to be somehow different than the GistRun example. – mark Apr 28 '16 at 01:06
  • we would need to see your code to understand what you mean – Jeremy Danyow Apr 28 '16 at 01:27
  • Sorry to be blunt, but it's pretty simple. The class name Thing (which is different in my code OFC) literally resolves to `undefined`. I am suspicious my Babel transpiler is wack. – mark Apr 28 '16 at 01:33