Juri Krainjukov
Juri Krainjukov

Reputation: 742

Aurelia - value converter using promise

I need to format Date using format returned by promise. I tried returning promise from toView(value). But that doesn't work.

@autoinject
export class DateTimeValueConverter {

    constructor(private formatService:FormatService) {
    }

    toView(value) {
        return this.formatService.getFormat().then(format=>
            moment(value).format(format)
        );
    }

}

Here's FormatService's code, which works properly

export class FormatService {

    private format;

    constructor(private http:AppHttp) {
        this.format= null;
    }

    public getFormat() : Promise<string>{

        if (this.format){
            var promise = new Promise<string>((resolve, reject)=>{
                resolve(this.format);
            });
            return promise;
        }

        return this.http.get('format')
            .then((format) => {
                if (format){
                    this.format= format;
                }
                return format;
            });
        }
}

Upvotes: 7

Views: 1330

Answers (3)

Piwnik
Piwnik

Reputation: 301

Actually it's possible. You need binding behavior, which will wait until value converter promise is resolved. I found this article with such async binding behavior

And it works with promise values and value converters

In your case you need create asyncBindingBehavior

export class asyncBindingBehavior {
 
  bind(binding, source, busymessage) {
    binding.originalupdateTarget = binding.updateTarget;
    binding.updateTarget = (a) => { 
      if (typeof a.then === 'function') {
        if (busymessage) 
          binding.originalupdateTarget(busymessage);
        a.then(d => { binding.originalupdateTarget(d); });
      }
      else
        binding.originalupdateTarget(a);
     };
  }
 
  unbind(binding) {
    binding.updateTarget = binding.originalupdateTarget;
    binding.originalupdateTarget = null;
  }
}

And use with value converter

<span>${ Date.now() | dateTime & async }</span>

P.S. don't forget import asyncBindingBehavior

<require from="./asyncBindingBehavior"></require>

Upvotes: 2

alienriver49
alienriver49

Reputation: 692

I was looking for a similar solution and was very honed in on using a value converter, because on initial thought that's what makes sense; but as fikkatra mentions that's not currently possible. After doing some digging, the better solution is using a binding behavior to get the desired affect.

So, converting that DateTimeValueConverter to a binding behavior would look like:

@autoinject
export class DateTimeBindingBehavior {
    constructor(private formatService:FormatService) {
    }

    public bind(binding, source) {
        binding.originalUpdateTarget = binding.updateTarget;
        binding.updateTarget = value => {
            this.formatService.getFormat().then(format =>
                moment(value).format(format)
            );
        };
    }

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

Here's an example of how you'd use this in your view:

<div>${someDate & dateTime}</div>

Similar to value converters you can also pass arguments to them, which you can read about here.

Upvotes: 0

fikkatra
fikkatra

Reputation: 5822

As far as I know, you cannot use async functionality within value converters. One solution I see, is to pass the format as a parameter from the viewmodel to the value converter (through the view). But this means you need to fetch the format within the viewmodel, which kind of destroys the whole point of value converters...

Another solution I see, is to adjust FormatService so that it caches the format (assuming that 'format' doesn't change often). This way, the getFormat function will be synchronous and you can use it within the value converter. Of course, you will need to find a way to initialize format within FormatService before any value converters are called.

Upvotes: 3

Related Questions