Hello,
I’m trying to build an OTP Input component and I’m having some troubles calling the Command binded to the control.
My control code
public Command OnChange
{
get { return (Command)GetValue(OnChangeProperty); }
set { SetValue(OnChangeProperty, value); }
}
public static readonly DotvvmProperty OnChangeProperty
= DotvvmProperty.Register<Command, OTPInput>(c => c.OnChange, null);
protected override void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequestContext context) {
...
var onChangeProperty = GetCommandBinding(OnChangeProperty);
if(onChangeProperty != null)
{
string propertyName = nameof(OnChange);
string value = KnockoutHelper.GenerateClientPostBackScript(propertyName, onChangeProperty, this, true, null);
writer.AddAttribute("data-password-otp-changed", value);
}
...
}
Markup control code:
@viewModel System.Object, mscorlib
@baseType GestRestVVM.Controls.OTP.OTPInput, GestRestVVM
<form autocomplete="off" name="unlock_data_container">
<div class="input-group justify-content-end flex-nowrap">
<input type="password" class="form-control form-control--otp js-otp-input" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" required>
<input type="password" class="form-control form-control--otp js-otp-input" inputmode="numeric" pattern="[0-9]*" required>
<input type="password" class="form-control form-control--otp js-otp-input" inputmode="numeric" pattern="[0-9]*" required>
<input type="password" class="form-control form-control--otp js-otp-input" inputmode="numeric" pattern="[0-9]*" required>
</div>
</form>
Knockout javascript
ko.bindingHandlers["pin-input-plugin"] = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
const $el = $(element);
const callback = $el.attr('data-password-otp-changed');
const $inputs = $el.find('input');
otpHandler = otp($inputs);
otpHandler.init(function (code) {
var property = valueAccessor();
if (ko.isObservable(property)) {
if (code !== property()) {
property(code);
if (callback != undefined) {
new Function(callback).call($el[0]);
}
}
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
otpHandler.destroy();
});
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext)
{
const propertyValue = ko.unwrap(valueAccessor());
const $inputs = $(element).find('input');
if (propertyValue && propertyValue.length === $inputs.length) {
otpHandler.setValue(propertyValue);
}
}
}
When callback is called, I get the following console error:
“postback Uncaught error returned from promise! TypeError: Cannot read properties of undefined (reading ‘$data’)”
When I had DataContext in my control markup code, the function was called but the inputs were transformed into normal inputs like if the otpHandler was destroyed (maybe because of the postback after calling property(code)). So I tried moving the DataContext to the control tag:
<input:OTP DataContext="{value: _root.OTPInput}" Pin="{value: _root.OTPInput}" OnChange="{command: _root.OnChange()}" />
After that I had no more issues with the otpHandler stoping working but the Command function stopped being called. How should I configure it correctly?
I tried to follow Bootstrap Datepicker from contrib repo.
Best Regards,