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

Add ability to call into view model from script / dynamic elements #426

Closed
mkaltner opened this issue May 24, 2016 · 3 comments
Closed

Add ability to call into view model from script / dynamic elements #426

mkaltner opened this issue May 24, 2016 · 3 comments

Comments

@mkaltner
Copy link

mkaltner commented May 24, 2016

There are times when I want to deal with ajax/json data and render it on the client.
A good example of this is using DataTables with a ajax data source.
it would be nice if there was a way to call into the view model to perform actions such as showing editors, calling service clients and other operations from the client code.

In my example of DataTables, the rows/columns and buttons are client generated and therefore do not go through the template/view render.

Is there a var similar to $parent that would resolve client side to point to the view model of the current route view/module?

Here's a sample of what I'm referring to.
After a couple days of searching, I have found no documented way of performing this action.

import {bindable} from "aurelia-framework";

import "jquery"
import "bootstrap"
import "datatables"

export class MyViewModel {
    @bindable columns = [
        {
            title: "Actions", name: "actions",
            render(data, type, row, meta) {
                return `<button click.delegate="showEditor()">Edit</button>`;
            }
        }
    ];

    showEditor() {
        alert("Edit");
    }
}

In the sample above, I'd like to be able to generate the button string client side via DataTables but call the showEditor method on the view model.

If there as a more appropriate way of achieving this, let me know and I'll close the issue.

@EisenbergEffect
Copy link
Contributor

You can walk the HTML node tree and look for nodes that have an au property. Aurelia stores information there. Walk the tree until you find one with au.controller. Once you have that you can then get au.controller.viewModel and that will probably be what you want.

@mkaltner
Copy link
Author

mkaltner commented May 24, 2016

I was able to get it to work somewhat with a similar approach but there were context issues ('this' was not the view model).

Lets look at a different approach, I found this example you wrote in another issue (aurelia/templating#35) and modified it based on the latest changes.

It works fine for ${item.Name} but the button click delegate doesn't work.
Is there something I'm missing?

ViewModel:

import {Compiler} from "../../Lib/Compiler"

import "jquery"
import "bootstrap"
import "datatables"

export class MyViewModel {
    @bindable columns = [];
    compiler: Compiler = null;

    constructor(compiler) {
        this.compiler = compiler;
    }

    bind(bindingContext) {
        var self = this;

        this.columns = [
            {
                title: "Name", name: "name",
                render(data, type, row, meta) {
                    var context = { item: row, viewModel: self, parent: bindingContext };
                    var cell = self.compiler.compile('<button click.trigger="viewModel.showEditor()" class="btn btn-primary">${item.Name}</button>', context);
                    return cell.fragment.innerHTML;
                }
            }
        ];
    }

    showEditor() {
        alert("Test");
    }
}

Compiler:

import {inject, ViewCompiler, ViewResources, Container} from "aurelia-framework";

@inject(ViewCompiler, ViewResources, Container)
export class Compiler {
    viewCompiler: any;
    resources: any;
    container: any;

    constructor(viewCompiler, resources, container) {
        this.viewCompiler = viewCompiler;
        this.resources = resources;
        this.container = container;
    }

    compile(templateOrFragment, bindingContext = null, viewSlot = null): any {
        if (typeof templateOrFragment === "string") {
            var temp = document.createElement("span");
            temp.innerHTML = templateOrFragment;
            templateOrFragment = temp;
        }

        var viewFactory = this.viewCompiler.compile(templateOrFragment, this.resources);
        var view = viewFactory.create(this.container);
        view.bind(bindingContext);
        return view;
    }
}

The DOM does appear to get marked up properly:
2016-05-24 14_27_31-courses _ southloop

Thanks!

  • Mike

@atsu85
Copy link
Contributor

atsu85 commented May 27, 2016

@mkaltner

but there were context issues ('this' was not the view model)

I guess You'll need to pass the context (value for this in the function) Yourself when calling the function of viewmodel. But instead of calling it smth like au.controller.viewModel.someFunction(arg1, arg2) You'll need to use call:
au.controller.viewModel.someFunction.call(au.controller.viewModel, arg1, arg2);
or apply
au.controller.viewModel.someFunction.apply(au.controller.viewModel, [arg1, arg2]);

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