Skip to content

Commit 783f85e

Browse files
committed
Prevent rapid typing from updating the currently focused element. Try to handle the edge where a model and action are on the same element and fire at the same time. #100
1 parent d40e7c1 commit 783f85e

File tree

3 files changed

+35
-10
lines changed

3 files changed

+35
-10
lines changed

django_unicorn/static/js/component.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,15 @@ export class Component {
150150
};
151151
this.actionQueue.push(action);
152152

153-
this.queueMessage(-1, (triggeringElements, err) => {
153+
this.queueMessage(-1, (triggeringElements, _, err) => {
154154
if (err && isFunction(errCallback)) {
155155
errCallback(err);
156156
} else if (err) {
157157
console.error(err);
158158
} else {
159-
this.setModelValues(triggeringElements);
159+
// Can hard-code `forceModelUpdate` to `true` since it is always required for
160+
// `callMethod` actions
161+
this.setModelValues(triggeringElements, true);
160162
this.setDbModelValues();
161163
}
162164
});
@@ -316,14 +318,17 @@ export class Component {
316318
* Sets all model values.
317319
* @param {[Element]} triggeringElements The elements that triggered the event.
318320
*/
319-
setModelValues(triggeringElements) {
321+
setModelValues(triggeringElements, forceModelUpdates) {
320322
triggeringElements = triggeringElements || [];
323+
forceModelUpdates = forceModelUpdates || false;
321324

322-
// Focus on the last element on what triggered the update.
325+
let lastTriggeringElement = null;
326+
327+
// Focus on the last element which triggered the update.
323328
// Prevents validation errors from stealing focus.
324329
if (triggeringElements.length > 0) {
325330
let elementFocused = false;
326-
const lastTriggeringElement = triggeringElements.slice(-1)[0];
331+
lastTriggeringElement = triggeringElements.slice(-1)[0];
327332

328333
if (
329334
hasValue(lastTriggeringElement) &&
@@ -347,7 +352,13 @@ export class Component {
347352
}
348353

349354
this.modelEls.forEach((element) => {
350-
this.setValue(element);
355+
if (
356+
!lastTriggeringElement ||
357+
!lastTriggeringElement.isSame(element) ||
358+
forceModelUpdates
359+
) {
360+
this.setValue(element);
361+
}
351362
});
352363
}
353364

django_unicorn/static/js/eventListeners.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,11 +290,19 @@ export function addModelEventListener(component, el, eventType) {
290290

291291
component.queueMessage(
292292
element.model.debounceTime,
293-
(triggeringElements, err) => {
293+
(triggeringElements, forceModelUpdate, err) => {
294294
if (err) {
295295
console.error(err);
296296
} else {
297-
component.setModelValues(triggeringElements);
297+
triggeringElements = triggeringElements || [];
298+
299+
// Make sure that the current element is included in the triggeringElements
300+
// if for some reason it is missing
301+
if (!triggeringElements.some((e) => e.isSame(element))) {
302+
triggeringElements.push(element);
303+
}
304+
305+
component.setModelValues(triggeringElements, forceModelUpdate);
298306
component.setDbModelValues();
299307
}
300308
}
@@ -355,7 +363,7 @@ export function addDbEventListener(component, el, eventType) {
355363

356364
component.actionQueue.push(action);
357365

358-
component.queueMessage(element.model.debounceTime, (_, err) => {
366+
component.queueMessage(element.model.debounceTime, (_, __, err) => {
359367
if (err) {
360368
console.error(err);
361369
} else {

django_unicorn/static/js/messageSender.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export function send(component, callback) {
1616
return;
1717
}
1818

19+
// Since methods can change the data "behind the scenes", any queue with a callMethod
20+
// action forces model elements to always be updated
21+
const forceModelUpdate = component.actionQueue.some(
22+
(a) => a.type === "callMethod"
23+
);
24+
1925
// Set the current action queue and clear the action queue in case another event happens
2026
component.currentActionQueue = component.actionQueue;
2127
component.actionQueue = [];
@@ -136,7 +142,7 @@ export function send(component, callback) {
136142
component.currentActionQueue = null;
137143

138144
if (isFunction(callback)) {
139-
callback(triggeringElements, null);
145+
callback(triggeringElements, forceModelUpdate, null);
140146
}
141147
})
142148
.catch((err) => {

0 commit comments

Comments
 (0)