powerbuoy
powerbuoy

Reputation: 12838

Async binding in Aurelia

I'm trying to bind an async value to one of my Aurelia templates, and obviously all I get is [object Promise] in return.

I found this article http://www.sobell.net/aurelia-async-bindings/ which excellently explains how to solve this problem using a binding behavior which looks like this:

// http://www.sobell.net/aurelia-async-bindings/
export class asyncBindingBehavior {
    bind (binding, source) {
        binding.originalUpdateTarget = binding.updateTarget;

        binding.updateTarget = a => {
            if (typeof a.then === 'function') {
                binding.originalUpdateTarget('...');

                a.then(d => {
                    binding.originalUpdateTarget(d);
                });
            }
            else {
                binding.originalUpdateTarget(a);
            }
        };
    }

    unbind (binding) {
        binding.updateTarget = binding.originalUpdateTarget;
        binding.originalUpdateTarget = null;
    }
}

This works perfectly when the promise resolves with a string or other non-object-like variable.

But what if my promise resolves with an object? How would I go about accessing the property I need inside that object?

Because if I do: ${object.property & async} inside my template then it will fail as object.property isn't a promise - only object is.

I added a bit of a hack that allows me to specify a property as an argument to async, like this: ${object & async:'property'} and updated my binding behavior as such:

// http://www.sobell.net/aurelia-async-bindings/
export class asyncBindingBehavior {
    bind (binding, source, property) {
        binding.originalUpdateTarget = binding.updateTarget;

        binding.updateTarget = a => {
            if (typeof a.then === 'function') {
                binding.originalUpdateTarget('...');

                a.then(d => {
                    if (property) {
                        binding.originalUpdateTarget(d[property]);
                    }
                    else {
                        binding.originalUpdateTarget(d);
                    }
                });
            }
            else {
                binding.originalUpdateTarget(a);
            }
        };
    }

    unbind (binding) {
        binding.updateTarget = binding.originalUpdateTarget;
        binding.originalUpdateTarget = null;
    }
}

But this feels very much like a hack to me, and it also won't allow me to access any deeper properties like object.parent.child.

I also found this (rather old) issue on GitHub: https://github.com/aurelia/templating/issues/81 where they use a getValue method. I've never heard of this method and trying to use it fails so I'm not sure how that works at all...

Any ideas?

Upvotes: 3

Views: 1703

Answers (1)

Roamer-1888
Roamer-1888

Reputation: 19288

You could sidestep your conundrum by specifying a function as the third parameter, giving the flexibility to do much more than simple property extraction.

You could write something like this :

export class asyncBindingBehavior {
    bind (binding, source, transformer="default") {
        binding.originalUpdateTarget = binding.updateTarget;
        binding.updateTarget = a => {
            if (typeof a.then === 'function') {
                binding.originalUpdateTarget('...');
                a.then(d => binding.originalUpdateTarget(transformFunctions[transformer](d)));
            } else {
                binding.originalUpdateTarget(a);
            }
        };
    }
    unbind (binding) {
        binding.updateTarget = binding.originalUpdateTarget;
        binding.originalUpdateTarget = null;
    }
}

The transformFunctions lookup would be necessary(?) due to the way Aurelia bindings are specified as HTML-ebbedded or template directives (ie all params must be String). Unless Aurelia offers a better way better way to "pass a function" (Value Converters?), you would write something like this :

export var transformFunctions = {
    default: (d) => d,
    transform1: (d) => d.someProperty,
    transform2: (d) => d.someProperty.someOtherProperty,
    transform3: someFunction,
    transform4: someOtherFunction.bind(null, someData);
}

Of course, you would give the functions better names.

Upvotes: 4

Related Questions