I think, that I found the reason of your problem. The code of "sn-record-picker" directive contains bug, which is fixed only in New York version. You can use the same code of London too. You need just create new directive with the name snRecordPickerNY for example "sn-record-picker-n-y" instead of "sn-record-picker". You should don't forget to include snRecordPickerNY in the list of "Angular Providers" of your widget. The bug fix exist in the code of cleanLabel function (see below at the beginning of the code of snRecordPickerNY). It tests whether the value is object. In case of object one uses the value of display_value property. One have the case in all reference fields.
The code of directive can looks like
function snRecordPickerNY ($timeout, $http, urlTools, filterExpressionParser, escapeHtml, i18n) {
"use strict";
var cache = {};
function cleanLabel(val) {
if (typeof val == "object")
return typeof val.display_value == "string" ? val.display_value.trim() : "";
return typeof val == "string" ? val.trim() : val;
}
return {
restrict: 'E',
replace: true,
scope: {
field: '=',
table: '=',
defaultQuery: '=?',
startswith: '=?',
searchFields: '=?',
valueField: '=?',
displayField: '=?',
displayFields: '=?',
pageSize: '=?',
onChange: '&',
snDisabled: '=',
multiple: '=?',
options: '=?',
placeholder: '@'
},
template: '<input type="text" ng-disabled="snDisabled" style="min-width: 150px;" name="{{field.name}}" ng-model="field.value"' + ' sn-atf-data-type="reference" sn-atf-data-type-params=\'{"reference" : "{{table}}", "reference_qual" : "{{defaultQuery}}",' + ' "valueField" : "{{valueField}}", "displayField" : "{{displayField}}"}\' sn-atf-class="builtin:ATF.BaseSNRecordPicker" sn-atf-component-value="{{field}}"/>',
controller: function($scope) {
if (!angular.isNumber($scope.pageSize))
$scope.pageSize = 20;
if (!angular.isDefined($scope.valueField))
$scope.valueField = 'sys_id';
this.filterResults = function(data, page) {
return {
results: data.data.result,
more: (page * $scope.pageSize < parseInt(data.headers('x-total-count'), 10))
};
}
;
},
link: function(scope, element, attrs, ctrl) {
var isExecuting = false;
var select2Helpers = {
formatSelection: function(item) {
return escapeHtml(getDisplayValue(item));
},
formatResult: function(item) {
var displayFields = getdisplayFields(item);
if (displayFields.length == 1)
return escapeHtml(cleanLabel(displayFields[0]));
if (displayFields.length > 1) {
var markup = escapeHtml(cleanLabel(displayFields[0]));
var width = 100 / (displayFields.length - 1);
markup += "<div>";
for (var i = 1; i < displayFields.length; i++)
markup += "<div style='width: " + width + "%;' class='select2-additional-display-field'>" + escapeHtml(cleanLabel(displayFields[i])) + "</div>";
markup += "</div>";
return markup;
}
return "";
},
search: function(queryParams) {
var url = '/api/now/table/' + scope.table + '?' + urlTools.encodeURIParameters(queryParams.data);
if (scope.options && scope.options.cache && cache[url])
return queryParams.success(cache[url]);
return $http.get(url).then(function(response) {
if (scope.options && scope.options.cache) {
cache[url] = response;
}
return queryParams.success(response)
});
},
initSelection: function(elem, callback) {
if (scope.field.displayValue) {
if (scope.multiple) {
var items = [], sel;
var values = scope.field.value.split(',');
var displayValues = scope.field.displayValue.split(',');
for (var i = 0; i < values.length; i++) {
sel = {};
sel[scope.valueField] = values[i];
sel[scope.displayField] = displayValues[i];
items.push(sel);
}
callback(items);
} else {
var sel = {};
sel[scope.valueField] = scope.field.value;
sel[scope.displayField] = scope.field.displayValue;
callback(sel);
}
} else
callback([]);
}
};
var config = {
width: '100%',
containerCssClass: 'select2-reference ng-form-element',
placeholder: scope.placeholder || ' ',
formatSearching: '',
allowClear: (scope.options && typeof scope.options.allowClear !== "undefined") ? scope.options.allowClear : true,
id: function(item) {
return item[scope.valueField];
},
ajax: {
quietMillis: NOW.ac_wait_time,
data: function(filterText, page) {
var params = {
sysparm_offset: (scope.pageSize * (page - 1)),
sysparm_limit: scope.pageSize,
sysparm_query: buildQuery(filterText, scope.searchFields, scope.defaultQuery),
sysparm_display_value: true
};
return params;
},
results: function(data, page) {
return ctrl.filterResults(data, page, scope.pageSize);
},
transport: select2Helpers.search
},
formatSelection: select2Helpers.formatSelection,
formatResult: select2Helpers.formatResult,
formatResultCssClass: function() {
return '';
},
initSelection: select2Helpers.initSelection,
multiple: scope.multiple
};
function buildQuery(filterText, searchFields, defaultQuery) {
var queryParts = [];
var operator = "CONTAINS";
if (scope.startswith)
operator = "STARTSWITH";
if (filterText.startsWith("*")) {
filterText = filterText.substring(1);
operator = "CONTAINS";
}
if (defaultQuery)
queryParts.push(defaultQuery);
var filterExpression = filterExpressionParser.parse(filterText, operator);
if (searchFields != null) {
var fields = searchFields.split(',');
if (filterExpression.filterText != '') {
var OR = "";
for (var i = 0; i < fields.length; i++) {
queryParts.push(OR + fields[i] + filterExpression.operator + filterExpression.filterText);
OR = "OR";
}
}
for (var i = 0; i < fields.length; i++)
queryParts.push('ORDERBY' + fields[i]);
queryParts.push('EQ');
}
return queryParts.join('^');
}
scope.field = scope.field || {};
var initTimeout = null;
var value = scope.field.value;
var oldValue = scope.field.value;
var $select;
function init() {
element.css("opacity", 0);
$timeout.cancel(initTimeout);
initTimeout = $timeout(function() {
i18n.getMessage('Searching...', function(searchingMsg) {
config.formatSearching = function() {
return searchingMsg;
}
;
});
element.css("opacity", 1);
element.select2("destroy");
$select = element.select2(config);
$select.bind("change", onChanged);
$select.bind("select2-selecting", onSelecting);
$select.bind("select2-removing", onRemoving);
$select.bind("sn-atf-setvalue", onAtfSetValue);
scope.$emit('select2.ready', element);
});
}
function onAtfSetValue(e) {
var valueToSet = e.detail ? e.detail.newValue : null;
if (valueToSet) {
oldValue = scope.field.value;
scope.field.value = valueToSet.value;
scope.field.displayValue = valueToSet.displayValue;
$select.select2('val', valueToSet.value).select2('close');
scope.$apply(function() {
callChange(oldValue, e);
});
}
}
function onSelecting(e) {
isExecuting = true;
oldValue = scope.field.value;
var selectedItem = e.choice;
if (scope.multiple && selectedItem[scope.valueField] != '') {
var values = !scope.field.value ? [] : scope.field.value.split(',');
var displayValues = !scope.field.displayValue ? [] : scope.field.displayValue.split(',');
values.push(selectedItem[scope.valueField]);
displayValues.push(getDisplayValue(selectedItem));
scope.field.value = values.join(',');
scope.field.displayValue = displayValues.join(',');
e.preventDefault();
$select.select2('val', values).select2('close');
scope.$apply(function() {
callChange(oldValue, e);
});
}
}
function onRemoving(e) {
isExecuting = true;
oldValue = scope.field.value;
var removed = e.choice;
if (scope.multiple) {
var values = scope.field.value.split(',');
var displayValues = scope.field.displayValue.split(',');
for (var i = values.length - 1; i >= 0; i--) {
if (removed[scope.valueField] == values[i]) {
values.splice(i, 1);
displayValues.splice(i, 1);
break;
}
}
scope.field.value = values.join(',');
scope.field.displayValue = displayValues.join(',');
e.preventDefault();
$select.select2('val', scope.field.value.split(','));
scope.$apply(function() {
callChange(oldValue, e);
});
}
}
function callChange(oldValue, e) {
var f = scope.field;
var p = {
field: f,
newValue: f.value,
oldValue: oldValue,
displayValue: f.displayValue
}
scope.$emit("field.change", p);
scope.$emit("field.change." + f.name, p);
if (scope.onChange)
try {
scope.onChange(e);
} catch (ex) {
console.log("directive.snRecordPicker error in onChange")
console.log(ex)
}
isExecuting = false;
}
function onChanged(e) {
e.stopImmediatePropagation();
if (scope.$$phase || scope.$root.$$phase) {
console.warn('in digest, returning early');
return;
}
if (e.added) {
var selectedItem = e.added;
if (!scope.multiple) {
scope.field.value = selectedItem[scope.valueField];
if (scope.field.value) {
scope.field.displayValue = getDisplayValue(selectedItem);
} else
scope.field.displayValue = '';
}
} else if (e.removed) {
if (!scope.multiple) {
scope.field.displayValue = '';
scope.field.value = '';
}
}
scope.$apply(function() {
callChange(oldValue, e);
});
}
function getDisplayValue(selectedItem) {
var displayValue = selectedItem[scope.valueField];
if (selectedItem) {
if (scope.displayField && angular.isDefined(selectedItem[scope.displayField]))
displayValue = selectedItem[scope.displayField];
else if (selectedItem.name)
displayValue = selectedItem.name;
else if (selectedItem.title)
displayValue = selectedItem.title;
}
return cleanLabel(displayValue);
}
function getdisplayFields(selectedItem) {
var displayFields = [];
if (selectedItem && selectedItem[scope.valueField]) {
var current = "";
if (scope.displayField && angular.isDefined(selectedItem[scope.displayField]))
current = selectedItem[scope.displayField];
else if (selectedItem.name)
current = selectedItem.name;
else if (selectedItem.title)
current = selectedItem.title;
displayFields.push(current);
}
if (scope.displayFields) {
var columns = scope.displayFields.split(",");
for (var i = 0; i < columns.length; i++) {
var column = columns[i];
if (selectedItem[column])
displayFields.push(selectedItem[column]);
}
}
return displayFields;
}
scope.$watch("field.value", function(newValue) {
if (isExecuting)
return;
if (angular.isDefined(newValue) && $select) {
if (scope.multiple)
$select.select2('val', newValue.split(',')).select2('close');
else
$select.select2('val', newValue).select2('close');
}
});
if (attrs.displayValue) {
attrs.$observe('displayValue', function(value) {
scope.field.value = value;
});
}
init();
}
};
}