need to copy the answer on custom metric div to another metric preceding the order of the div.
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 hours ago
i have a client controller on a widget to copy answer from one custom metric to another metric, but it's not working for yes/no radial buttons metric, for string the value is copying.
tried the below script
api.controller = function ($scope, $timeout) {
var c = this;
c.suggestion = '';
c.message = '';
function getCtx() {
// Find the current custom question's field + the survey formModel
var s = $scope;
var foundField = null, foundModel = null;
for (var i = 0; i < 20 && s; i++) {
if (!foundField && s.field) foundField = s.field;
if (!foundModel && s.formModel) foundModel = s.formModel;
if (foundField && foundModel) break;
s = s.$parent;
}
return { field: foundField, formModel: foundModel };
}
function getFieldsArray(formModel) {
if (!formModel) return [];
var fields = formModel._fields || formModel.fields || formModel.formFields || [];
if (Array.isArray(fields)) return fields;
// map -> array
if (fields && typeof fields === 'object') {
return Object.keys(fields).map(function (k) { return fields[k]; });
}
return [];
}
// ---- NEW: utility to normalize suggestion into a boolean ----
function coerceToBoolean(val) {
if (val === undefined || val === null) return null;
// Accept raw boolean
if (typeof val === 'boolean') return val;
var s = String(val).trim().toLowerCase();
// common forms
if (['true', 't', '1', 'yes', 'y'].indexOf(s) !== -1) return true;
if (['false', 'f', '0', 'no', 'n'].indexOf(s) !== -1) return false;
// Heuristic: starts with "yes" or "no"
if (s.startsWith('yes')) return true;
if (s.startsWith('no')) return false;
// Could not decide
return null;
}
// ---- Helper: detect boolean-ish field ----
function isBooleanField(f) {
var dt = (f.datatype || '').toLowerCase();
var t = (f.type || '').toLowerCase();
// ServiceNow surveys often use datatype='boolean' or type='boolean'
// Some widgets render yes/no as 'yesno' or as a checkbox
return dt === 'boolean' || t === 'boolean' || t === 'yesno' || t === 'checkbox';
}
// Find target question = nearest previous NON custom question by order
function findTargetField(fields, currentField) {
if (!currentField) return null;
var currentOrder = parseInt(currentField.order, 10);
if (isNaN(currentOrder)) currentOrder = 999999;
var best = null;
var bestOrder = -1;
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
if (!f) continue;
var ord = parseInt(f.order, 10);
if (isNaN(ord)) continue;
// exclude the current suggestion field itself
if (f.name === currentField.name) continue;
// exclude other custom widgets
if ((f.type || '').toLowerCase() === 'custom') continue;
if ((f.datatype || '').toLowerCase() === 'custom') continue;
// nearest previous
if (ord < currentOrder && ord > bestOrder) {
best = f;
bestOrder = ord;
}
}
return best;
}
// ---- UPDATED: setFieldValue now handles boolean radios/checkboxes ----
function setFieldValue(formModel, targetField, value) {
if (!targetField) return false;
var wasBoolean = isBooleanField(targetField);
var finalValue = value;
if (wasBoolean) {
var boolVal = coerceToBoolean(value);
if (boolVal === null) {
// If we cannot infer boolean from the suggestion, don't set
return false;
}
finalValue = boolVal;
// Update Angular model (most survey widgets honor stagedValue for booleans)
targetField.stagedValue = finalValue;
targetField.value = finalValue; // keep both in sync for safety
// Try DOM update paths:
// 1) Checkbox (single input)
var checkboxEl = document.getElementById('sp_formfield_' + targetField.name);
if (checkboxEl && checkboxEl.type === 'checkbox') {
checkboxEl.checked = !!finalValue;
checkboxEl.dispatchEvent(new Event('input', { bubbles: true }));
checkboxEl.dispatchEvent(new Event('change', { bubbles: true }));
return true;
}
// 2) Radio group: values might be 'true'/'false', '1'/'0', 'Yes'/'No'
var radioCandidates = [
{ name: targetField.name, value: finalValue ? 'true' : 'false' },
{ name: targetField.name, value: finalValue ? '1' : '0' },
{ name: targetField.name, value: finalValue ? 'yes' : 'no' },
{ name: targetField.name, value: finalValue ? 'Yes' : 'No' }
];
var selected = null;
for (var i = 0; i < radioCandidates.length; i++) {
var rc = radioCandidates[i];
var sel = document.querySelector('input[type="radio"][name="' + rc.name + '"][value="' + rc.value + '"]');
if (sel) { selected = sel; break; }
}
if (selected) {
selected.checked = true;
// Also uncheck the opposite if needed
var name = selected.name;
var group = document.querySelectorAll('input[type="radio"][name="' + name + '"]');
group.forEach(function (r) {
if (r !== selected) r.checked = false;
});
selected.dispatchEvent(new Event('input', { bubbles: true }));
selected.dispatchEvent(new Event('change', { bubbles: true }));
return true;
}
// If neither checkbox nor radio found, still return true since the model is set.
return true;
}
// Fallback: non-boolean (string/text/etc.)
targetField.stagedValue = finalValue;
// Optional: update DOM if already rendered (nice UX)
if (targetField.name) {
var el = document.getElementById('sp_formfield_' + targetField.name);
if (el) {
// For selects/choices, set value and trigger change
if (el.tagName === 'SELECT') {
el.value = finalValue;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
} else {
el.value = finalValue;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}
}
}
return true;
}
// Initial load: take suggestion from the current field value (stagedValue/value)
$timeout(function () {
var ctx = getCtx();
if (!ctx.field || !ctx.formModel) {
c.message = 'Could not find survey context (field/formModel).';
return;
}
// Suggestion comes from THIS suggested field itself
c.suggestion = ctx.field.stagedValue || ctx.field.value || '';
}, 0);
c.accept = function () {
c.message = '';
var ctx = getCtx();
if (!ctx.field || !ctx.formModel) {
c.message = 'Could not find survey context (field/formModel).';
return;
}
var fields = getFieldsArray(ctx.formModel);
var targetField = findTargetField(fields, ctx.field);
if (!targetField) {
c.message = 'Could not find the target question to populate.';
return;
}
var suggestionValue = ctx.field.stagedValue || ctx.field.value || c.suggestion || '';
if (!suggestionValue) {
c.message = 'No suggestion text to apply.';
return;
}
var ok = setFieldValue(ctx.formModel, targetField, suggestionValue);
// Make sure Angular digests // Make sure Angular digests the model update
try { $scope.$applyAsync(); } catch (e) {}
c.message = ok ? ('Filled target question: ' + (targetField.label || targetField.name)) : 'Failed to fill target question.';
};
c.ignore = function () {
c.message = 'Ignored';
};
};
can someone help to find the solution
0 REPLIES 0