Knockout自定义绑定update方法更新observable后UI未更新问题
问题根因
- 核心问题1:
ko.bindingHandlers.MyDate的update方法每次执行都会创建全新的MyView实例,和init阶段绑定到UI的实例完全独立,新实例的属性变更自然不会同步到已有UI上 - 核心问题2:
MyView构造函数中对dayValue的赋值写法错误,直接把observable对象覆盖为普通字符串,丢失了响应式通知能力 - 核心问题3:月份切换更新
days数组后,没有对dayValue做边界修正,若原有选中日期超过新月份的最大天数,会导致value绑定和options不匹配,UI不展示选中值
修正代码
1. 修正MyView类逻辑
export class MyView{ public dayValue: Observable<string>; public monthValue: Observable<string>; public yearValue: Observable<string>; public days: Observable<string[]> = ko.observable([]); public months: Observable<string[]> = ko.observable(Array.from({length:12}, (_,i)=>(i+1).toString())); public years: Observable<string[]> = ko.observable(Array.from({length:50}, (_,i)=>(new Date().getFullYear()-25 +i).toString())); constructor(opt: Iclass){ // 正确初始化observable,不要覆盖observable本身 const currentDate = opt.current(); this.dayValue = ko.observable(currentDate.getDate().toString()); this.monthValue = ko.observable((currentDate.getMonth()+1).toString()); this.yearValue = ko.observable(currentDate.getFullYear().toString()); // 订阅年月变化自动更新日期选项,不需要手动绑定change事件 ko.computed(() => { this.noOfDays(); }).extend({deferred: true}); } private noOfDays(): void { const temp: string[] = []; const month: number = parseInt(this.monthValue()) - 1; const year: number = parseInt(this.yearValue()); const maxDay: number = new Date(year, month + 1, 0).getDate(); for (let i = 1; i <= maxDay; i++) { temp.push(i.toString()); } this.days(temp); // 边界修正:如果当前选中日期超过新月份最大天数,自动调整为最大天数 const currentDay = parseInt(this.dayValue()); if (currentDay > maxDay) { this.dayValue(maxDay.toString()); } } // 暴露外部更新日期的方法 public updateDate(newDate: Date): void { this.yearValue(newDate.getFullYear().toString()); this.monthValue((newDate.getMonth()+1).toString()); this.dayValue(newDate.getDate().toString()); } }
2. 修正自定义绑定逻辑
ko.bindingHandlers.MyDate = { init(element: HTMLElement, valueAccessor: any): any { const options: Iclass = ko.utils.unwrapObservable(valueAccessor()) || {}; const myView: MyView = new MyView(options); // 把视图实例存在DOM节点上,update的时候直接复用 ko.utils.domData.set(element, 'myViewInstance', myView); ko.cleanNode(element); ko.applyBindingsToNode(element, { template: { name: "person-template", data: myView } }); return { controlsDescendantBindings: true }; }, update(element: HTMLElement, valueAccessor: any): any { const options: Iclass = ko.utils.unwrapObservable(valueAccessor()) || {}; // 取已绑定的实例,不新建 const myView: MyView = ko.utils.domData.get(element, 'myViewInstance'); if (myView && options.current) { myView.updateDate(ko.utils.unwrapObservable(options.current)); } } }
3. 修正HTML模板(补全引号、删除多余的change事件绑定)
<script type="text/html" id="person-template"> <select data-bind="options: months, value: monthValue"></select> <select data-bind="options: days, value: dayValue"></select> <select data-bind="options: years, value: yearValue"></select> </script>
使用示例代码无需调整,修改后dayValue的更新会自动同步到UI。
内容的提问来源于stack exchange,提问作者UserDev
相关产品推荐
相关产品推荐

