Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 325
powerbuoy Avatar asked Dec 05 '25 07:12

powerbuoy


1 Answers

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.

like image 182
Roamer-1888 Avatar answered Dec 08 '25 01:12

Roamer-1888