
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2018 01:07 PM
Looking for official documentation on sn-record-picker and sn-choice-list or where I can review the directive in the platform.
There was a question posted two years ago asking the same thing with a reference to a github repo that is now empty.
Solved! Go to Solution.
- Labels:
-
Service Portal Development
- 11,399 Views

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2018 02:04 PM
I was able to find the source in my browser which is a start.
Ultimately looking to see if the sn-record-picker is capable of having a table query as well as allowing for custom text if an entry isn't found, or what kind of modification it would take to enable this with a custom directive.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-28-2018 02:04 PM
I was able to find the source in my browser which is a start.
Ultimately looking to see if the sn-record-picker is capable of having a table query as well as allowing for custom text if an entry isn't found, or what kind of modification it would take to enable this with a custom directive.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
02-13-2019 11:41 AM
Did you look at the serviceportal.io page about it? the documentation was on the wiki but dosen't seem to be now.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-20-2018 07:21 PM
/scripts/sn/common/controls/directive.snRecordPicker.js
/*! RESOURCE: /scripts/sn/common/controls/directive.snRecordPicker.js */
angular.module('sn.common.controls').directive('snRecordPicker', function($timeout, $http, urlTools, filterExpressionParser, $sanitize, i18n) {
"use strict";
var cache = {};
function cleanLabel(val) {
if (typeof val == "object")
return "";
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" />',
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 $sanitize(getDisplayValue(item));
},
formatResult: function(item) {
var displayFields = getdisplayFields(item);
if (displayFields.length == 1)
return $sanitize(cleanLabel(displayFields[0]));
if (displayFields.length > 1) {
var markup = $sanitize(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'>" + $sanitize(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)
};
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-removed", onChanged);
$select.bind("select2-selecting", onSelecting);
$select.bind("select2-removing", onRemoving);
scope.$emit('select2.ready', element);
});
}
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();
}
};
});
;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-27-2024 08:16 AM
/scripts/sn/common/controls/directive.snChoiceList.js
/*! RESOURCE: /scripts/sn/common/controls/directive.snChoiceList.js */
angular
.module("sn.common.controls")
.directive("snChoiceList", function (i18n, $timeout) {
"use strict";
return {
restrict: "E",
replace: true,
scope: {
snModel: "=",
snTextField: "@",
snValueField: "@",
snOptions: "=?",
snItems: "=?",
snOnChange: "&",
snDisabled: "=",
snDialogName: "=",
},
template:
'<select ng-disabled="snDisabled" ' +
' ng-model="model" ' +
' ng-options="item[snValueField] as item[snTextField] for item in snItems">' +
' <option value="" ng-show="snOptions.placeholder">{{snOptions.placeholder}}</option>' +
"</select>",
link: function (scope, element, attrs) {
if (scope.snDialogName)
scope.$on("dialog." + scope.snDialogName + ".close", function () {
$timeout(function () {
$(element).select2("destroy");
});
});
$(element).css("opacity", 0);
var config = {
width: "100%",
};
if (angular.isFunction(element.select2)) {
i18n.getMessage("No matches found", function (msg) {
config.formatNoMatches = function () {
return msg;
};
});
}
if (scope.snOptions) {
if (scope.snOptions.placeholder) {
config.placeholder = scope.snOptions.placeholder;
config.placeholderOption = "first";
}
if (scope.snOptions.allowClear) {
config.allowClear = scope.snOptions.allowClear;
}
if (
scope.snOptions.hideSearch &&
scope.snOptions.hideSearch === true
) {
config.minimumResultsForSearch = -1;
}
}
function init() {
scope.model = scope.snModel;
render();
}
function render() {
if (!attrs) {
$timeout(function () {
render();
});
return;
}
$timeout(function () {
$(element).css("opacity", 1);
$(element).select2("destroy");
$(element).select2(config);
$(".select2-focusser").attr("aria-expanded", "false");
});
}
init();
scope.$watch(
"snItems",
function (newValue, oldValue) {
if (newValue !== oldValue) {
init();
}
},
true
);
scope.$watch("snModel", function (newValue) {
if (newValue !== undefined && newValue !== scope.model) {
init();
}
});
scope.$watch("model", function (newValue, oldValue) {
if (newValue !== oldValue) {
scope.snModel = newValue;
if (scope.snOnChange) scope.snOnChange({ selectedValue: newValue });
}
});
scope.$on("$destroy", function () {
$(element).select2("destroy");
});
},
controller: function ($scope) {},
};
});