Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Riese/repeat performance #35523

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 110 additions & 4 deletions corehq/apps/cloudcare/static/cloudcare/js/form_entry/form_ui.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';

Check warning on line 1 in corehq/apps/cloudcare/static/cloudcare/js/form_entry/form_ui.js

View workflow job for this annotation

GitHub Actions / Lint Javascript

'use strict' is unnecessary inside of modules
hqDefine("cloudcare/js/form_entry/form_ui", [
'jquery',
'knockout',
Expand Down Expand Up @@ -205,8 +205,11 @@
function Container(json) {
var self = this;
self.pubsub = new ko.subscribable();
// console.time("container create");
self.fromJS(json);

// console.timeEnd("container create");
self.dummy = {};
self.fromJSDummy(json);
/**
* Used in KO template to determine what template to use for a child
* @param {Object} child - The child object to be rendered, either Group, Repeat, or Question
Expand Down Expand Up @@ -250,17 +253,22 @@
},
children: {
create: function (options) {
// console.time("fromJs.create");
let element = undefined;
if (options.data.type === constants.GROUPED_ELEMENT_TILE_ROW_TYPE) {
return new GroupedElementTileRow(options.data, self);
element = new GroupedElementTileRow(options.data, self);
} else if (options.data.type === constants.QUESTION_TYPE) {
return new Question(options.data, self);
element = new Question(options.data, self);
} else if (options.data.type === constants.GROUP_TYPE) {
return new Group(options.data, self);
element = new Group(options.data, self);
} else {
console.error('Could not find question type of ' + options.data.type);
}
// console.timeEnd("fromJs.create");
return element;
},
update: function (options) {
// console.time("fromJs.update");
if (options.target.pendingAnswer &&
options.target.pendingAnswer() !== constants.NO_PENDING_ANSWER) {
// There is a request in progress, check if the answer has changed since the request
Expand Down Expand Up @@ -289,6 +297,7 @@
// at the very least we can skip entirely when there's no change.
delete options.data.choices;
}
// console.timeEnd("fromJs.update");
return options.target;
},
key: function (data) {
Expand All @@ -310,6 +319,95 @@
ko.mapping.fromJS(json, mapping, self);
};

Container.prototype.fromJSDummy = function (json) {
var self = this;

if (!json.type) {
Container.groupElements(json);
}

var mapping = {
caption: {
update: function (options) {
if (self.hideCaption) {
return null;
}
return options.data ? DOMPurify.sanitize(options.data.replace(/\n/g, '<br/>')) : null;
},
},
caption_markdown: {
update: function (options) {
return options.data ? markdown.render(options.data) : null;
},
},
children: {
create: function (options) {
// console.time("fromJs.create");
let element = undefined;
if (options.data.type === constants.GROUPED_ELEMENT_TILE_ROW_TYPE) {
element = new GroupedElementTileRow(options.data, self);
} else if (options.data.type === constants.QUESTION_TYPE) {
element = new Question(options.data, self);
} else if (options.data.type === constants.GROUP_TYPE) {
element = new Group(options.data, self);
} else {
console.error('Could not find question type of ' + options.data.type);
}
// console.timeEnd("fromJs.create");
return element;
},
update: function (options) {
// console.time("fromJs.update");
if (options.target.pendingAnswer &&
options.target.pendingAnswer() !== constants.NO_PENDING_ANSWER) {
// There is a request in progress, check if the answer has changed since the request
// was made. For file questions, it is most unlikely that the answer will change while the request
// is in progress, so we just ignore the value.
if (options.target.entry.templateType === "file"
|| options.target.entry.templateType === "signature"
|| formEntryUtils.answersEqual(options.data.answer, options.target.pendingAnswer())
) {
// We can now mark it as not dirty
options.target.pendingAnswer(constants.NO_PENDING_ANSWER);
} else {
// still dirty - most likely edited by the user while the request was going
// Keep answer the same as the pending one to avoid overwriting the user's changes
options.data.answer = _.clone(options.target.pendingAnswer());
}
}

// Do not update the answer if there is a server error on that question
if (ko.utils.unwrapObservable(options.target.serverError)) {
options.data.answer = _.clone(options.target.answer());
}
if (options.target.choices && _.isEqual(options.target.choices(), options.data.choices)) {
// replacing the full choice list if it has a few thousand items
// is actually quite expensive and can freeze the page for seconds.
// at the very least we can skip entirely when there's no change.
delete options.data.choices;
}
// console.timeEnd("fromJs.update");
return options.target;
},
key: function (data) {
const uuid = ko.utils.unwrapObservable(data.uuid);
if (uuid) {
return uuid;
}
const exists = ko.utils.unwrapObservable(data.exists);
const ix = ko.utils.unwrapObservable(data.ix);
if (exists && exists === 'false') {
// this is a add group button. replace last part with d
const lastIdx = ix.lastIndexOf('_');
return lastIdx === -1 ? ix : ix.slice(0, lastIdx) + '_d';
}
return ix;
},
},
};
ko.mapping.fromJS(json, mapping, self.dummy);
};

/**
* Calculates background color for nested Group and Repeat headers.
* Recursively determines nesting level (considering only Group and Repeat),
Expand Down Expand Up @@ -459,7 +557,9 @@
self.displayOptions = json.displayOptions || {};
json.children = json.tree;
delete json.tree;
console.time("form constructor");
Container.call(self, json);
console.timeEnd("form constructor");
self.blockSubmit = ko.observable(false);
self.hasSubmitAttempted = ko.observable(false);
self.isSubmitting = ko.observable(false);
Expand Down Expand Up @@ -712,7 +812,13 @@
}

response.children = allChildren;
console.time("self.fromJS(response)");
self.fromJS(response);
console.timeEnd("self.fromJS(response)");

console.time("self.fromJSDummy(response)");
self.fromJSDummy(response);
console.timeEnd("self.fromJSDummy(response)");
}
});

Expand Down
Loading