Aurelia - value converter using promise
Asked Answered
C

3

7

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;
            });
        }
}
Colbert answered 19/4, 2016 at 8:30 Comment(2)
can you show the code of formatService?Enyedy
added FormatService code in questionColbert
E
3

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.

Enyedy answered 19/4, 2016 at 9:24 Comment(3)
Yeah, that was just copy error. Actually as I mentioned, FormatService works properly. It's used in other situations and returns Promise with format as intended. The problem is that I cannot return promise from the toView method. It expects a string. and just prints out [Object].Colbert
Ow ok now I see the problem. Well, the toView method also returns a promise, so you need to call 'then'. Can you show the code which uses toView? Or is it used from the view?Enyedy
it is used from the view. ${entity.created | dateTime}Colbert
T
2

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>
Then answered 1/3, 2021 at 22:10 Comment(0)
M
0

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.

Mumps answered 12/10, 2018 at 13:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.