I have extensive experience as a knockout user, but I am currently struggling to achieve a specific scenario. For the past few days, I have been trying to create a system within a knockout component that allows observables to translate themselves into different languages.
In order to accomplish this, I have developed a custom binding that is applied to elements in the following manner:
<p data-bind="translatedText: {observable: translatedStringFour, translationToken: 'testUiTransFour'}"></p>
This custom binding is then linked to a property in my knockout component using a standard observable:
private translatedStringFour: KnockoutObservable<string> = ko.observable<string>("I'm an untranslated string four....");
Although I am using TypeScript for this project, I can work with either TS or JS interchangeably. With the custom binding, I can still update the observable in the same way as a normal text binding by using 'translatedStringFour("foo").
We store the translations in HTML5 localStorage with the translationToken from the binding serving as the key. When the page loads, another component is responsible for retrieving the translated strings based on the user's chosen language.
These translated strings are then stored in localStorage using the translationToken. When the custom bind is invoked, we search localStorage for the corresponding value to replace the untranslated string. The code for our custom binding is as follows:
ko.bindingHandlers.translatedText = {
init: (element: HTMLElement, valueAccessor: Function, allBindings: KnockoutAllBindingsAccessor, viewModel: any, bindingContext: KnockoutBindingContext) => {
var value = valueAccessor();
var associatedObservable = value.observable;
var translationToken = value.translationToken;
},
update: (element: HTMLElement, valueAccessor: Function, allBindings: KnockoutAllBindingsAccessor, viewModel: any, bindingContext: KnockoutBindingContext) => {
var value = valueAccessor();
var associatedObservable = value.observable;
var translationToken = value.translationToken;
var translatedText = sessionStorage[translationToken];
if (undefined === translatedText) {
translatedText = "No Translation ID";
}
associatedObservable(translatedText);
ko.utils.setTextContent(element, associatedObservable());
}
}
While this system works well once all translations are loaded into localStorage, there may be instances where the translations are not yet available during the initial page load. In such cases, I have various methods in place to notify the components when the translations are ready, such as window.postMessage(), someElement.dispatchEvent(), or ko.postbox.publish().
The challenge arises in integrating the event/message handler within the binding handler so that the element can receive notifications and retry translations. This requirement is crucial for certain components that may need these translations before they are fully loaded. Despite multiple attempts, including using postmessage API, custom events, and JQuery, I have not been successful in implementing an event listener within the binding.
If you have any insights on how to add an event handler inside a custom binding without relying on external dependencies other than Knockout core, your input would be greatly appreciated.
Shawty
Update (About an hour later)
After reviewing Regis's answer, I realized that my issue was related to targeting the element within the binding. By attaching event handlers to the Window object rather than the specific element, I was able to resolve the problem. This change allowed me to successfully implement the necessary functionality for handling events within the custom binding.