Я пытаюсь реализовать небольшой инструмент перевода с Angular 5. Идея состоит в том, чтобы вызвать метод из шаблона, который будет переводить строку из динамически сгенерированного списка переводов.
В приведенном ниже коде строка не отображается, хотя вызывается оператор карты.
// NOT WORKING
@Component({
selector: 'app-translate',
template: '<h1>{{translate("Takeaway") | async}}</h1>'
})
export class TranslateComponent {
private translateChange = new Subject<any>();
private translateChange$;
constructor() {
this.translateChange$ = this.translateChange.asObservable();
setTimeout(() => {
this.translateChange.next({
'Takeaway': 'To go'
});
}, 1000);
}
translate(str: string) {
return this.translateChange$.map((nameList) => {
return nameList.str || str;
});
}
}
Интересно, что если я удалю оператор карты и передам строку непосредственно в Subject, как показано ниже, то строка будет отображаться.
// WORKING
@Component({
selector: 'app-translate',
template: '<h1>{{translate("Takeaway") | async}}</h1>'
})
export class TranslateComponent {
private translateChange = new Subject<any>();
private translateChange$;
constructor() {
this.translateChange$ = this.translateChange.asObservable();
setTimeout(() => {
this.translateChange.next('To go');
}, 1000);
}
translate(str: string) {
return this.translateChange$;
}
}
Я хотел бы знать, почему оператор .map плохо работает с каналом async и есть ли лучший подход для достижения вышеуказанного.
Спасибо!
@tomvangreen Хорошее место! Но все же в этом случае он должен вернуть переданную строку из-за символа «|| str », что у меня на месте.





Итак, я вижу несколько вещей, которые кажутся немного странными в вашем примере.
Повторно использовать существующие библиотеки
Я не понимаю, зачем вы здесь пытаетесь изобрести велосипед. Существует несколько библиотек перевода, которые уже можно использовать с angular. Я бы посоветовал взглянуть на ngx-translate и посмотреть, соответствует ли он вашим потребностям.
Но на случай, если вы все же захотите создать свой собственный, есть еще кое-что.
Предмет
Мое лучшее предположение заключается в том, что это является причиной вашей проблемы. Асинхронный конвейер загрязнен и, следовательно, его необходимо переоценивать при каждом цикле обнаружения изменений. Он ничего не покажет, пока вы сначала не испустите что-то с темой. И после того, как вы отправили значение, потребуется всего один дополнительный цикл, чтобы снова оставить подписку зависшей.
Попробуйте вместо этого использовать BehaviorSubject. Субъект излучает только один раз, когда устанавливается новое значение. BehaviorSubject всегда будет выдавать последнее установленное значение, и вам необходимо указать начальное значение. Теперь предположим, что у вас есть блок в вашем шаблоне, который скрыт * ngIf. Через 1000 мс (что вы делаете в своем конструкторе) объект выдаст значение. Если вы показываете часть в блоке * ngIf после того, как это произошло, она никогда не получит значения.
С BehaviorSubject будет использоваться последнее переданное значение, поэтому в этом случае все будет в порядке.
Обеспечение функциональности с помощью метода в вашем компоненте
Я думаю, что предоставлять перевод каким-либо методом в вашем компоненте - не лучшая идея. Если вы хотите использовать перевод в любом другом компоненте, вам необходимо создать метод перевода. Я бы предпочел специальную трубку.
ngx-translate делает это примерно так:
{{ 'translationKey' | translate }}
И вы можете использовать его в каждом компоненте.
Я бы, вероятно, создал службу перевода, которая отвечает за отслеживание активных переводов (в основном, служба перевода содержит наблюдаемую translations$) и канал, который имеет службу в качестве зависимости и выполняет фактическое отображение из словаря перевода в переведенное значение . Если вы решили пойти по этому маршруту, убедитесь, что труба отмечена как нечистая.
Отлично, пока не изменилось
Я бы, наверное, добавил оператор distsinctUntilChanged() после вашего оператора карты. Вы хотите выдавать значения только в том случае, если они изменились. Вы можете изменить объект, содержащий ваши переводы, но некоторые значения останутся прежними. В этом случае вам не нужно снова излучать.
Это не сработает:
nameList.str || str. Вы должны использоватьnameList[str] || str, иначе он всегда пытается найти значение с ключом str, а не с ключом, который вы указали.