-1

i have some kind of problem. I found many similar problems on stack overflow but nothing resolved my problem... I want to translate the language in Ionic/Angular and I have to fetch it from a database.

When I am loading the application many components getting initialized and obviously many values need to get showed in frontend in the right language. In my initial Version I called the DB every time I needed a word, so in initialization I hat about 40 request with one word the the db. -> than I made a buffer in the language Service for 50ms and created an array for the http(s) call. I reduced the calls from 40 requests to 1-2 calls onLoad the application.

Simple explanation:

//language.service.ts
languageSub = new BehaviorSubject<any>(null);
cache = []; // this is global

pseudocode
getLanguage(shortcut: string[]) {
{{code that makes an array with set timeout and saves to global cache array}}
if(cache.length > 0){
  return this.http.get<language[]>(`${services.language.apiUrl}/textbaustein`, { params })
  .subscribe(data => {
        let temp = {};
    data.forEach(element => {
      temp[element.shortcut] = element.text;
    }
    this.languageSub.next(temp);
  }
}

in my angular Template I have this simple Pipe

{{ value | translatePipe }}

Since the Service does not immediately respond (which would not work because of the buffering) I created a BehaviorSubject, which emits Data after it got the answer from the http call. The language Pipe (and here the horror begins) needs to wait for the subject to response, otherwise he has only undefined.

//language.pipe.ts
constructor(private languageService: LanguageService) { }
transform(value: string): Subscription<string> {
    this.languageService.getLanguage([value]); //this makes the languageService add a value to the global cache array and then call the server

    return this.languageService.languageSub.subscribe(data => {
        console.log(data);

        if (data != null) { //on init the data will be null
            if (data[value] != undefined || data[value] != null) {
                console.log(data[value]);
                return data[value]; //the value gets perfectly logged in console, but never arrives in frontend
            }
        }
    })

}

this does not work the way I want, but I have also no clue how to get it to work in a pretty way (I have already subscribed this BehaviorSubject in every Component and deleted the Pipe, but it creates muuuuch lines of code and looks ugly). I do not want to remove the buffering from the Service, because if more users use the application there would me many "unnecessary" calls.

Does somebody know how to fix this?

Chris
  • 1
  • 1
  • Does [this](https://stackoverflow.com/a/39296015/5779165) answer you question? – Tommi Aug 02 '22 at 09:29
  • i am not 100% sure, but I do not think so.. in my case the function gets called about 40times but the response is only once.. So if I would return an answer from the http call, one call would be happy, but 39 would be sad not getting what they want. That's the reason I made this whole thing asynchronous.. or am I just misunderstanding something or your postet answer? – Chris Aug 02 '22 at 09:42
  • My bad i misread the question, but it looks like when you want to handle async calls within a pipe you combine the custom pipe with the built in async pipe. [This](https://stackoverflow.com/a/45919153/5779165) answer expands on it – Tommi Aug 02 '22 at 10:32
  • Instead of calling http.get in getLanguage, add the translatable value to a separate Subject. Make the service subscribe (maybe in the constructor) to this subject and use the Rxjs debounceTime + switchMap methods. Like you would do with a typeahead. E.g. https://www.learnrxjs.io/learn-rxjs/recipes/type-ahead. – johey Aug 02 '22 at 11:10
  • @johey this is incredibly intelligent. Wonder why I didn't figure this out by myself ;) tank you. I optimized my code to half. but unfortunately it does not solve my "behaviorSubject in pipe" problem... – Chris Aug 02 '22 at 15:31
  • Your Q is so wordy & also long! No more time for everyone who interested to answer that! You should write shorter to get better answer. – Mohammad Hossein Ganjyar Aug 03 '22 at 11:00

1 Answers1

0

after wasting 6hours with "fun with pipes", I did it in about 10 lines of code. probably not the finest way, but eventually anybody else will also know how.

transform(value: string) {
    this.languageService.needText.next([value]); //this is the nice solution from @johey
    let promise = new Promise(resolve => {
        return this.languageService.allLanguage.subscribe((res:[{'shortcut': string, 'text': string}]) => {
            if(res != undefined){
                res.forEach(element => {
                    if(element.shortcut == value){
                        resolve(element.text);
                    }
                });
            }
        })
    })
    return promise;
}

In the template I added just the async, but this was clear before.

{{ test | translatePipe | async }}
Chris
  • 1
  • 1
  • I think you can replace the 2 last lines (let promise ...; return promise;) with: return this.languageService.allLanguage .pipe( map(translations => translations.find(t => t.shortcut === value)?.text) ); – johey Aug 03 '22 at 07:30
  • And maybe move that line to a new function in your LanguageService: e.g. translate(value: string): Observable You could even move the first line of transform to that function. It's a bit cleaner. – johey Aug 03 '22 at 07:35