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

compose in a foreach loop #22

Closed
mteinum opened this issue Dec 17, 2017 · 3 comments
Closed

compose in a foreach loop #22

mteinum opened this issue Dec 17, 2017 · 3 comments
Labels

Comments

@mteinum
Copy link

mteinum commented Dec 17, 2017

Hi,

It looks like if you compose multiple elements, all views are bound to the last element.

example:

parent.html

<template>
    <div knockout>
        <div data-bind="foreach: values">
            <div data-bind="compose: { model: 'Shell/test/child', activationData: $data } "></div>
        </div>
    </div>
</template>

parent.ts

export class Parent {
    values = ['a', 'b', 'c'];
}

child.html

<template>
    <div knockout>
        <div>
            Text: <span data-bind="text: text"/>
        </div>
    </div>
</template>

child.ts

export class child {
    text: string;

    public activate = (data) => {
        console.log(data);

        this.text = data;
    }
}

Console output

a
b
c

browser output

Text: c
Text: c
Text: c

Are there any workarounds for this?

@mteinum
Copy link
Author

mteinum commented Dec 17, 2017

The problem is in knockout-composition.ts, getViewModelInstance

Original

    /** internal: do not use */
  getViewModelInstance(moduleId: string): Promise<any> {
    let index: number = moduleId.lastIndexOf("/");
    let fileName: string = moduleId.substr(index === -1 ? 0 : index + 1).toLowerCase();

    return loadModule(moduleId, this.loader).then((result: any): any => {
      if (typeof result !== 'function') {
        // Try to find a property which name matches the filename of the module
        let constructorPropName: string|null = getMatchingProperty(result, fileName);

        if (constructorPropName) {
          // Use function of property.
          // This occurs if the constructor function is exported by the module.
          result = result[constructorPropName];
        } else {
          // The module returns an instance.
          return result;
        }
      }

      return this.container.get(result);
    });
  }

Modified

/** internal: do not use */
  getViewModelInstance(moduleId: string): Promise<any> {
    let index: number = moduleId.lastIndexOf("/");
    let fileName: string = moduleId.substr(index === -1 ? 0 : index + 1).toLowerCase();

    return loadModule(moduleId, this.loader).then((result: any): any => {
      if (typeof result !== 'function') {
        // Try to find a property which name matches the filename of the module
        let constructorPropName: string|null = getMatchingProperty(result, fileName);

        if (constructorPropName) {
          // Use function of property.
          // This occurs if the constructor function is exported by the module.
          result = result[constructorPropName];
        } else {
          // The module returns an instance.
          return result;
        }
      }

      return new result();
    });
  }

getViewModelInstance must create a new instance of the class/viewmodel. Singleton looks ok (The module returns an instance).

Fix:

From

return this.container.get(result);

To

return new result();

@ckotzbauer
Copy link
Member

Thanks for your bugreport @mteinum. I registered the constructor function as transient now. This seems more correct regarding to the aurelia docs. Published as 2.1.0

@mteinum
Copy link
Author

mteinum commented Dec 17, 2017

@code-chris thanks for the patch. I have updated to 2.1.0 and it works excellent.

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

No branches or pull requests

2 participants