Skip to content

Commit 397005f

Browse files
authored
Merge pull request #208 from mcselle/main
Fix for hasFocus / checked binding if not applied via DataBindProvider
2 parents 1edce52 + 16ddffc commit 397005f

File tree

4 files changed

+82
-9
lines changed

4 files changed

+82
-9
lines changed

packages/binding.core/spec/checkedBehaviors.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ import {
44
} from '@tko/utils'
55

66
import {
7-
applyBindings
7+
applyBindings, applyBindingsToNode
88
} from '@tko/bind'
99

1010
import {
11-
observable,
12-
observableArray
11+
observable, observableArray
1312
} from '@tko/observable'
1413

1514
import {
@@ -33,6 +32,48 @@ describe('Binding: Checked', function () {
3332
provider.bindingHandlers.set(templateBindings)
3433
})
3534

35+
it('Checked Binding should update observable value if checked binding is applied in another CustomBinding', function () {
36+
const RadioGroupExample = {
37+
init: (element: HTMLElement, valueAccessor: () => any) => {
38+
const group = element;
39+
const options = valueAccessor();
40+
const items = options.items();
41+
42+
for (let i = 0; i < items.length; i++) {
43+
//let radioBox = $('<input type="radio">').appendTo(group);
44+
const radioInput = document.createElement('input');
45+
radioInput.type = 'radio';
46+
group.appendChild(radioInput);
47+
48+
applyBindingsToNode(radioInput, {
49+
checked: items[i].checked,
50+
attr:{
51+
value: items[i].value
52+
}
53+
})
54+
}
55+
}
56+
};
57+
58+
options.bindingProviderInstance.bindingHandlers.set("customBinding", RadioGroupExample)
59+
60+
const $decision = observable("")
61+
const yesOption = { value: "True", checked: $decision }
62+
const noOption = { value: "False", checked: $decision }
63+
64+
testNode.innerHTML = '<div data-bind="customBinding: radioBoxProp"></div>'
65+
applyBindings({radioBoxProp: { items: observable([yesOption, noOption]) } }, testNode);
66+
67+
const yesRadio = testNode.children[0].children[0]
68+
const noRadio = testNode.children[0].children[1]
69+
70+
expect($decision()).toEqual("")
71+
triggerEvent(yesRadio, 'click')
72+
expect($decision()).toEqual("True")
73+
triggerEvent(noRadio, 'click')
74+
expect($decision()).toEqual("False")
75+
})
76+
3677
it('Triggering a click should toggle a checkbox\'s checked state before the event handler fires', function () {
3778
// This isn't strictly to do with the checked binding, but if this doesn't work, the rest of the specs aren't meaningful
3879
testNode.innerHTML = "<input type='checkbox' />"

packages/binding.core/spec/hasfocusBehaviors.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
} from '@tko/utils'
44

55
import {
6-
applyBindings
6+
applyBindings, applyBindingsToNode
77
} from '@tko/bind'
88

99
import {
@@ -22,7 +22,6 @@ import * as coreBindings from '../dist'
2222

2323
import '@tko/utils/helpers/jasmine-13-helper'
2424

25-
2625
arrayForEach(['hasfocus', 'hasFocus'], binding => {
2726
describe(`Binding: ${binding}`, function () {
2827
var bindingHandlers
@@ -42,6 +41,33 @@ arrayForEach(['hasfocus', 'hasFocus'], binding => {
4241
beforeEach(function () { waits(100) })
4342
}
4443

44+
it('Should set an observable value to be true on focus and false on blur even if the binding is applied through another binding', function () {
45+
const createElementWithHasFocusBinding = {
46+
init: (element: HTMLElement, valueAccessor: () => any) => {
47+
const parent = element;
48+
const $hasFocus = valueAccessor();
49+
50+
applyBindingsToNode(parent, { hasFocus: $hasFocus })
51+
}
52+
};
53+
options.bindingProviderInstance.bindingHandlers.set("customBinding", createElementWithHasFocusBinding)
54+
55+
testNode.innerHTML = `<input data-bind='customBinding: customProps' /><input />`
56+
57+
const $myVal = observable(false)
58+
applyBindings({customProps: $myVal}, testNode );
59+
const input = testNode.children[0] as HTMLInputElement;
60+
61+
input.focus()
62+
triggerEvent(input, 'focusin')
63+
expect($myVal()).toEqual(true);
64+
65+
// Move the focus elsewhere
66+
(testNode.childNodes[1] as HTMLElement).focus()
67+
triggerEvent(input, 'focusout')
68+
expect($myVal()).toEqual(false)
69+
})
70+
4571
it('Should respond to changes on an observable value by blurring or focusing the element', function () {
4672
var currentState
4773
var model = { myVal: observable() }

packages/binding.core/src/checked.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ export var checked = {
7777
elemValue = undefined
7878
}
7979
}
80-
valueAccessor(elemValue, {onlyIfChanged: true})
80+
// valueAccessor(elemValue, {onlyIfChanged: true})
81+
const modelValue = valueAccessor(elemValue, {onlyIfChanged: true});
82+
if (isWriteableObservable(modelValue) && (modelValue.peek() !== elemValue)) {
83+
modelValue(elemValue);
84+
}
8185
}
8286
};
8387

packages/binding.core/src/hasfocus.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from '@tko/utils'
55

66
import {
7-
unwrap, dependencyDetection
7+
unwrap, dependencyDetection, isWriteableObservable
88
} from '@tko/observable'
99

1010
var hasfocusUpdatingProperty = createSymbolOrString('__ko_hasfocusUpdating')
@@ -31,9 +31,11 @@ export var hasfocus = {
3131
}
3232
isFocused = (active === element)
3333
}
34-
// var modelValue = valueAccessor();
35-
valueAccessor(isFocused, {onlyIfChanged: true})
3634

35+
const modelValue = valueAccessor(isFocused, {onlyIfChanged: true});
36+
if (isWriteableObservable(modelValue) && (modelValue.peek() !== isFocused)) {
37+
modelValue(isFocused);
38+
}
3739
// cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function
3840
element[hasfocusLastValue] = isFocused
3941
element[hasfocusUpdatingProperty] = false

0 commit comments

Comments
 (0)