How to get a Widget to pass variables to a Catalog Item?

Rachael10
Kilo Contributor

I have found articles in how to pass variables from a Catalog Item to a Widget, but not the other way around (which we are trying to do).

We have a widget that pulls asset information based on queries and displays it.   I would like to be able to select an asset, and then pass some variables from that asset to a Catalog item that creates a request (and a subsequent work task within a workflow).   We have the widget built, and we have the Catalog Item built, but we need assistance with finding the method to use to pass the variables to the Catalog Item within the Portal.   I will include the widget script below, the sysid to the form, and the link to the form (which is locked down, but you'll get the idea), as well as the variable names I would like to pass.

Widget HTML:

<div class="simple-ui">

  <div class="panel">

      <h3>DAX</h3>

      <form>

          <div class="input-group input-group-default">

              <span class="input-group-addon">

                  <span>Cost center</span>

                  <input name="cc" type="text" ng-model="c.data.cc"

                              ng-model-options="{debounce: 250}" autocomplete="off"

                              uib-typeahead="item as item.name for item in c.getCostCenter($viewValue)"

                              typeahead-focus-first="true">

              </span>

              <span class="input-group-addon">

                  <span>Assigned to</span>

                  <input name="at" type="text" ng-model="c.data.at"

                              ng-model-options="{debounce: 250}" autocomplete="off"

                              uib-typeahead="item as item.name for item in c.getAssignedTo($viewValue)"

                              typeahead-focus-first="false">

              </span>

              <span class="input-group-addon">

                  <span>Location</span>

                  <input name="lo" type="text" ng-model="c.data.lo"

                              ng-model-options="{debounce: 250}" autocomplete="off"

                              uib-typeahead="item as item.name for item in c.getLocation($viewValue)"

                              typeahead-focus-first="false">

              </span>

              <span class="input-group-addon">

                  <span>Device ID</span>

                  <input name="ci" type="text" ng-model="c.data.ci"

                              ng-model-options="{debounce: 250}" autocomplete="off"

                              uib-typeahead="item as item.name for item in c.getDeviceId($viewValue)"

                              typeahead-focus-first="false">

              </span>

              <span class="input-group-btn">

                  <button name="search" type="submit" class="btn btn-default" ng-click="c.getAssetsByCostCenter(1);">Search</button>

              </span>

          </div>

      </form>

  </div>

  <div ng-if="c.data.list" class="panel-body">

      <h3>Assets</h3>

      <table class="table table-striped table-responsive">

          <thead>

              <tr>

                  <th ng-repeat="field in data.fields_array track by $index" ng-click="setOrderBy(field)">

                      <div class="th-title">{{data.column_labels[field]}}</div>

                      <i ng-if="c.data.o == field" class="fa" ng-class="{'asc': 'fa-chevron-up', 'desc': 'fa-chevron-down'}[data.d]"></i>

                  </th>

              </tr>

          </thead>

          <tbody>

              <tr ng-repeat="item in data.list track by item.sys_id">

                  <td class="pointer" ng-class="{selected: item.selected}" ng-click="go(data.table, item)" ng-repeat="field in data.fields_array" data-field="{{field}}" data-th="{{data.column_labels[field]}}">{{item[field].display_value}}</td>

              </tr>

          </tbody>

      </table>

  </div>

  <div ng-if="c.data.list" class="panel-footer">

          <div class="btn-toolbar m-r pull-left">

              <div class="btn-group">

                  <a ng-if="c.data.num_pages > 1" ng-disabled="c.data.p == 1" href="javascript:void(0)" ng-click="setPageNum(c.data.p - 1)" class="btn btn-default"><i class="fa fa-chevron-left"></i></a>

              </div>

              <div ng-if="c.data.num_pages > 1 && c.data.num_pages < 20" class="btn-group">

                  <a ng-repeat="i in getNumber(c.data.num_pages) track by $index" ng-click="setPageNum($index + 1)" href="javascript:void(0)" ng-class="{active: ($index + 1) == data.p}" type="button" class="btn btn-default">{{$index + 1}}</a>

              </div>

              <div class="btn-group">

                  <a ng-if="c.data.num_pages > 1" ng-disabled="c.data.p == c.data.num_pages" href="javascript:void(0)" ng-click="setPageNum(data.p + 1)" class="btn btn-default"><i class="fa fa-chevron-right"></i></a>

              </div>

          </div>

          <div class="m-t-xs panel-title">${Rows {{data.window_start + 1}} - {{ mathMin(data.window_end,data.row_count) }} of {{data.row_count}}}</div>

          <span class="clearfix"></span>

  </div>

</div>

CSS:

.panel-heading {

  padding-left: 8px;

}

thead {

  border-bottom: 1px solid #ddd;

}

table {

  margin-bottom: 0;

}

.table > thead > tr > th {

  border: 1px solid #ddd;

  cursor: pointer;

  vertical-align: middle;

  &:first-child {

      border-left: none;

  }

  &:last-child {

      border-right: none;

  }

}

th i {

  display: inline-block;

  margin-left: 5px;

  color: #A0A0A0;

}

th .disabled{

  color:#ddd;

}

.th-title {

  display: inline-block;

  color: #428bca;

}

.panel-body {

  overflow: auto;

  padding: 0px;

}

.selected {

  color: #fff;

  background-color: #909090;

  border-color: 1px solid #fff;

}

tbody tr:last-child {

  border-bottom: none;

}

.pruned-msg {

  padding-bottom: 10px;

  padding-left: 4px;

  text-align: center;

}

.pruned-msg-filter-pad {

  padding-top:8px;

}

.filter-breadcrumbs {

  border-bottom: 1px solid #ddd;

  padding-top: 3px;

}

Client Script:

function ($scope, $location, spUtil, amb, $http) {

  var c = this;

  c.data.list = null;

  /*

  * options:

  * hide_footer (bool) = true to remove the data table footer contents

  * hide_header (bool) = true to remove the data table header contents

  * show_new (bool) = true to show the "New" record button

  * show_keywords (bool) = true to show the keyword search field

  * table (string) = the table name to query

  * filter (string) = the encoded query

  * o (string) = the order by column

  * d (string) = The order by direction: asc or desc

  * p (int) = the page to jump to

  * fields (string) = comma seperated list of fields that become the list columns

  * view (string) = the default view to load for columns, overrides fields

  */

  $scope.data.o = 'assigned_by';

  $scope.data.d = 'assc';

  var keys = ['table', 'filter', 'p', 'o', 'd'];

  var eventNames = {

  click: 'data_table.click',

  setFilter: 'data_table.setFilter',

  setKeywords: 'data_table.setKeywords'

  };

  $scope.go = function(table, item) {

  var parms = {};

  parms.table = table;

  parms.sys_id = item.sys_id;

  parms.record = item;

  $scope.ignoreLocationChange = true;

  for (var x in c.data.list) {

  c.data.list[x].selected = false;

  }

  item.selected = true;

  $scope.$emit(eventNames.click, parms);

  };

  $scope.newRecord = function(){

  var parms = {

  id: 'form',

  table: $scope.data.table,

  sys_id: '-1'

  };

  if ($scope.data.filter != '')

  parms.query = $scope.data.filter;

  $location.search(parms);

  };

  function recoverStateFromUrl() {

  $scope.data.fields = [];

  var s = $location.search();

  for (var x in keys) {

  if (s[keys[x]]) {

  $scope.data[keys[x]] = s[keys[x]];

  }

  }

  $scope.server.update().then(function(data) {

  if (s.sys_id) {

  for (var x in data.list) {

  if (data.list[x].sys_id == s.sys_id) {

  $scope.go(s.table, data.list[x]);

  }

  }

  }

  });

  }

  if ($scope.options.fromUrl) {

  $scope.$on('$locationChangeSuccess', function(e) {

  if ($scope.ignoreLocationChange){

  $scope.ignoreLocationChange = false;

  return;

  }

  // Helps to recover state when using the browser's back button

  recoverStateFromUrl();

  });

  }

  $scope.getNumber = function(num) {

  return new Array(num);

  }

  $scope.mathMin = function(v1,v2) {

  return Math.min(v1,v2);

  }

  function getData(updateUrl) {

  var f = $scope.data;

  spUtil.update($scope).then(function(data) {

  f.view = data.view;

  if ($scope.options.fromUrl && updateUrl)

  setPermalink(f.table, f.filter, f.o, f.d, f.p);

  if ($scope.options.show_breadcrumbs && data.filterBreadcrumbs)

  $scope.$broadcast('widget-filter-breadcrumbs.setBreadcrumbs', data.filterBreadcrumbs.data);

  initRecordWatcher(f.table, f.filter);

  });

  }

  function setPermalink(table, filter, orderBy, orderDirection, page){

  $scope.ignoreLocationChange = true;

  var search = $location.search();

  angular.extend(search, {

  spa: 1,

  table: table,

  filter: filter,

  p: page,

  o: orderBy,

  d: orderDirection

  });

  $location.search(search);

  }

  var watcher;

  function initRecordWatcher(table, filter){

  if (watcher)

  watcher.unsubscribe();

  if (table && filter) {

  var watcherChannel = amb.getChannelRW(table, filter);

  amb.connect();

  watcher = watcherChannel.subscribe(function() {

  spUtil.update($scope)

  });

  }

  }

  $scope.setPageNum = function(num) {

  $scope.data.p = num;

  //getData(true);

  c.getAssetsByCostCenter(num, $scope.data.o, $scope.data.d);

  }

  $scope.setOrderBy = function(field) {

  var d = "asc";

  if ($scope.data.o == field) {

  if ($scope.data.d == "asc")

  d = "desc";

  else

  d = "asc";

  }

  $scope.data.o = field;

  $scope.data.d = d;

  //$scope.setSearch(true);

  $scope.data.p = 1;

  c.getAssetsByCostCenter(1, $scope.data.o, $scope.data.d);

  }

  $scope.setSearch = function(updateUrl) {

  $scope.data.p = 1;

  getData(updateUrl);

  }

  $scope.$on(eventNames.setFilter, function(e, newFilter){

  $scope.data.filter = newFilter;

  $scope.setSearch(false);

  });

  $scope.$on(eventNames.setKeywords, function(e, keywords){

  $scope.data.keywords = keywords;

  $scope.setSearch(false);

  });

  $scope.$on('widget-filter-breadcrumbs.queryModified', function(e, newFilter){

  $scope.data.filter = newFilter;

  $scope.setSearch(true);

  });

  $scope.rowsWerePruned = function() {

  if (!$scope.data.list)

  return;

  $scope.rowsPruned = $scope.mathMin($scope.data.window_end,$scope.data.row_count) - $scope.data.window_start - $scope.data.list.length;

  return $scope.rowsPruned > 0;

  }

  $scope.showFilter = function() {

  return !$scope.data.list.length && !$scope.data.num_pages && !$scope.data.invalid_table && !$scope.loadingData;

  }

  c.appendQuery = function(query){

  if ($scope.data.filter.length > 1)

  $scope.data.filter += '^';

  $scope.data.filter += query;

  $scope.setSearch();

  }

  // Makes Widget Async

  var title = $scope.data.title;

  $scope.data = $scope.options;

  $scope.loadingData = true;

  $scope.server.update().then(function() {

  $scope.loadingData = false;

  $scope.data.title = title;

  initRecordWatcher($scope.data.table, $scope.data.filter);

  });

  function parseQuery(table, queryString){

  return $http.post('/api/now/sp/parsequery/' + table, queryString).then(function(response){

  return response.data.result;

  });

  }

  c.createQueryTerm = function(table, field, sys_id, operator){

  return $http.get('/api/now/sp/getInOutQueryTerm', {

  params: {

  table: table,

  sys_id: sys_id,

  field: field,

  operator: operator

  }

  }).then(function(response){

  if (response && response.data && response.data.result)

  return response.data.result.parts;

  });

  }

  c.showMatching = function(field, newTerm) {

  var queryString = $scope.data.filter;

  var eq = "";

  parseQuery($scope.data.table, queryString).then(function(oldTerms) {

  for(var i=0; i<oldTerms.length; i++){

  var term = oldTerms[i];

  if (isSameField(newTerm, term))

  continue;

  if (eq.length)

  eq += '^';

  eq += getEncodedTerm(term);

  }

  if (eq.length)

  eq += '^';

  eq += getEncodedTerm(newTerm);

  $scope.data.filter = eq;

  $scope.setSearch();

  });

  };

  c.filterOut = function(field, newTerm) {

  var eq = $scope.data.filter;

  if (eq.length)

  eq += '^';

  eq += getEncodedTerm(newTerm);

  $scope.data.filter = eq;

  $scope.setSearch();

  };

  function isSameField(t1, t2) {

  if ('left' in t1 && 'left' in t2)

  return t1.left.field === t2.left.field;

  else if ('left' in t1)

  return t1.left.field === t2.field;

  else if ('left' in t2)

  return t1.field === t2.left.field;

  return t1.field === t2.field;

  }

  function getEncodedTerm(term) {

  var eq;

  if (term.left) {

  eq = getEncodedTerm(term.left);

  eq += '^OR';

  eq += getEncodedTerm(term.right);

  } else {

  eq = term.field;

  eq += term.operator;

  eq += term.value;

  }

  return eq;

  }

  c.getCostCenter = function(q) {

  return c.server.get({input_cc: q}).then(function(response) {

  return response.data.cc;

  });

  }

  c.getAssignedTo = function(q) {

  return c.server.get({input_at: q}).then(function(response) {

  return response.data.at;

  });

  }

  c.getLocation = function(q) {

  return c.server.get({input_lo: q}).then(function(response) {

  return response.data.lo;

  });

  }

  c.getDeviceId = function(q) {

  return c.server.get({input_ci: q}).then(function(response) {

  return response.data.ci;

  });

  }

  c.getAssetsByCostCenter = function(pageNum, order, by) {

  var inputStr = JSON.stringify(c.data.cc);

  console.log('c.data.cc=' + inputStr);

  var cc = '';

      var at = '';

      var lo = '';

      var ci = '';

      if (c.data.cc != null && c.data.cc.name != null) {

          cc = c.data.cc.name;

      }

      if (c.data.at != null && c.data.at.name != null) {

          at = c.data.at.name;

      }

      if (c.data.lo != null && c.data.lo.name != null) {

          lo = c.data.lo.name;

      }

      if (c.data.ci != null && c.data.ci.name != null) {

          ci = c.data.ci.name;

      }

  if (cc || at || lo || ci) {

  var paras = {submit_cc:cc,submit_at:at,submit_lo:lo,submit_ci:ci,submit_p:pageNum,submit_o:order,submit_d:by};

  c.server.get(paras).then(function(response) {

  c.data = response.data;

  var inputStr = JSON.stringify(c.data);

  console.log('c.data=' + inputStr);

  });

  } else {

  alert('Please enter the search conditions');

  }

  };

}

Server Script:

(function() {

  if (!input) // asynch load list

      return;

  var inputStr = JSON.stringify(data);

  console.log('data=' + inputStr);

  if (input.submit_cc || input.submit_at || input.submit_lo || input.submit_ci) {

  data.list = [];

    var cc = input.submit_cc;

      var at = input.submit_at;

      var lo = input.submit_lo;

      var ci = input.submit_ci;

      var pageNum = input.submit_p;

  var order = input.submit_o;

  var by = input.submit_d;

      data.fields = 'ci,display_name,assigned_to,u_cost_center,department,u_division,u_section,location,warranty_expiration,u_public___staff,u_pc_id';

      data.fields_array = data.fields.split(',');

      data.column_labels = {};

      data.column_labels.ci = 'Configuration item';

      data.column_labels.display_name = 'Display name';

      data.column_labels.assigned_to = 'Assigned to';

      data.column_labels.u_cost_center = 'SAP cost center';

      data.column_labels.department = 'Department';

      data.column_labels.u_division = 'Division';

      data.column_labels.u_section = 'Section';

      data.column_labels.location = 'Location';

      data.column_labels.warranty_expiration = 'Warranty expiration';

      data.column_labels.u_public___staff = 'Public/Staff';

      data.column_labels.u_pc_id = 'PC ID';

  if (order) {

  data.o = order;

  } else {

  data.o = 'assigned_by';

  }

  if (by) {

  data.d = by;

  } else {

  data.d = 'asc';

  }

      getAssetsByCostCenter(cc,at,lo,ci,pageNum);

    data.cc = {};

  data.cc.name = cc;

    data.at = {};

      data.at.name = at;

    data.lo = {};

      data.lo.name = lo;

    data.ci = {};

      data.ci.name = ci;

  } else {

      console.log('get input, cc=' + input.input_cc);

      data.limit = 100;

      var table = '';

      var field = '';

      var condition = '';

      if (input.input_cc) {

          table = 'u_sap_cost_centers';

          field = 'u_sap_cost_center';

          condition = input.input_cc;

          getTypeaheadList(table, field, condition, 'cc');

      } else if (input.input_at) {

          table = 'sys_user';

          field = 'name';

          condition = input.input_at;

          getTypeaheadList(table, field, condition, 'at');

      } else if (input.input_lo) {

          table = 'cmn_location';

          field = 'name';

          condition = input.input_lo;

          getTypeaheadList(table, field, condition, 'lo');

      } else if (input.input_ci) {

          table = 'cmdb_ci';

          field = 'name';

          condition = input.input_ci;

          getTypeaheadList(table, field, condition, 'ci');

      } else {

          return;

      }

  }

  function getTypeaheadList(table, field, condition, list) {

      console.log('data limt=' + data.limit + ',table=' + table + ',field=' + field + ',condition=' + condition + ',list=' + list);

      data[list] = [];

      var rec = new GlideRecord(table);

      if (condition) {

          rec.addQuery(field, 'CONTAINS', condition);

      }

      rec.query();

      var count = 0;

      while (rec.next() && count < data.limit) {

          if (!$sp.canReadRecord(rec)) {

              continue;

          }

          var record = {};

          record.name = rec.getValue(field);

          data[list].push(record);

          count++;

          console.log(table + ' ' + count + ', name=' + record.name)

      }

  }

  function getAssetsByCostCenter(cc,at,lo,ci,pageNum) {

      console.log('querying assets, cc=' + cc + ',at=' + at + ',lo=' + lo + ',ci=' + ci + ',p=' + pageNum);

      data.p = pageNum;

      data.p = parseInt(data.p);

      data.window_size = 20;

      data.page_index = data.p - 1;

      data.window_start = data.page_index * data.window_size;

      data.window_end = (data.page_index + 1) * data.window_size;

      var gr = new GlideRecord('alm_asset');

  var joinModel = gr.addJoinQuery('cmdb_model_category', 'model_category', 'sys_id');

      joinModel.addCondition('name','Computer');

      //var joinComputer = gr.addJoinQuery('cmdb_ci_computer', 'ci', 'u_pc_id');

      //joinCi.addCondition('u_pc_id', ci);

  if (cc) {

          var joinCostCenter = gr.addJoinQuery('u_sap_cost_centers', 'u_cost_center', 'sys_id');

          joinCostCenter.addCondition('u_sap_cost_center', cc);

      }

      if (at) {

          var joinUser = gr.addJoinQuery('sys_user', 'assigned_to', 'sys_id');

          joinUser.addCondition('name', at);

      }

      if (lo) {

          var joinLocation = gr.addJoinQuery('cmn_location', 'location', 'sys_id');

          joinLocation.addCondition('name', lo);

      }

      if (ci) {

          var joinCi = gr.addJoinQuery('cmdb_ci', 'ci', 'sys_id');

          joinCi.addCondition('name', ci);

      }

  var orderBy = data.o;

  if (data.o == 'u_public___staff') {

  orderBy = 'ci.u_public___staff';

  }

  console.log('orderby=' + orderBy);

  if (data.d == 'asc') {

  gr.orderBy(orderBy);

  } else {

  gr.orderByDesc(orderBy);

  }

      gr.chooseWindow(data.window_start, data.window_end);

      gr.query();

   

  data.row_count = gr.getRowCount();

      data.num_pages = Math.ceil(data.row_count / data.window_size);

      console.log('assets: ' + data.row_count + ',number of pages=' + data.num_pages);

   

      var count = 0;

      while(gr.next()) {

          record = {};

          record.sys_id = gr.getValue('sys_id');

       

          var fci = gr.getDisplayValue('ci');

          record.ci = {};

          record.ci.value = fci;

          record.ci.display_value = fci;

          var fdn = gr.getValue('display_name');

          record.display_name = {};

          record.display_name.value = fdn;

          record.display_name.display_value = fdn;

          var fat = gr.getDisplayValue('assigned_to');

          record.assigned_to = {};

          record.assigned_to.value = fat;

          record.assigned_to.display_value = fat;

          var fcc = gr.getDisplayValue('u_cost_center');

          record.u_cost_center = {};

          record.u_cost_center.value = fcc;

          record.u_cost_center.display_value = fcc;

          var fdept = gr.getDisplayValue('department');

          record.department = {};

          record.department.value = fdept;

          record.department.display_value = fdept;

          var fdiv = gr.getDisplayValue('u_division');

          record.u_division = {};

          record.u_division.value = fdiv;

          record.u_division.display_value = fdiv;

          var fsec = gr.getDisplayValue('u_section');

          record.u_section = {};

          record.u_section.value = fsec;

          record.u_section.display_value = fsec;

          var floc = gr.getDisplayValue('location');

          record.location = {};

          record.location.value = floc;

          record.location.display_value = floc;

          var fwar = gr.getDisplayValue('warranty_expiration');

          record.warranty_expiration = {};

          record.warranty_expiration.value = fwar;

          record.warranty_expiration.display_value = fwar;

       

  var fps = gr.getDisplayValue('ci.u_public___staff');

  record.u_public___staff = {};

          record.u_public___staff.value = fps;

          record.u_public___staff.display_value = fps;

  var fpi = gr.getDisplayValue('ci.u_pc_id');

  record.u_pc_id = {};

          record.u_pc_id = fpi;

          record.u_pc_id.display_value = fpi;

/*

  var grCi = new GlideRecord('cmdb_ci_computer');

  grCi.addCondition('u_pc_id', fci);

  grCi.query();

  while (grCi.next()) {

  var fpub = grCi.getValue('u_public___staff');

  record.u_public___staff.value = fpub;

  record.u_public___staff.display_value = fpub;

  var fpid = grCi.getValue('u_pc_id');

  record.u_pc_id.value = fpid;

  record.u_pc_id.display_value = fpid;

  }

*/        

          data.list.push(record);

       

          console.log('asset, ' + count + ',' +

                                  record.display_name + ',' +

                                  record.assigned_to.display_value + ',' +

                                  record.u_cost_center.display_value

                                  );

          count++;

      }

  }

})();

SysID of Catalog Item:   657b4cff13f67640ea925d422244b05e

URL of Catalog Item (you won't be able to access):   SP Loading page

Variables to pass: email, requestor name and deviceid

Thank you for any help you can provide.

3 REPLIES 3

smcdonaldaz
Tera Guru

Hi Rachel,  did you get this working?  I'm just starting down this same path (Widget -> Catalog Item) but from KBA -> Incident.

Any advice would be appreciated.

Rachel, I ended up opening the catalog item via a URL and embedded the parameters in the URL.  You will need an onLoad client script to parse the URL and assign the parameter values to the variables.

Kristoffer Mon1
Giga Expert

From the widget's client script, use the following line of code to write to a hidden variable:

$scope.page.g_form.setValue('u_json_config', angular.toJson(c.data));

 

"u_json_config", in my case stores the serialized string on your catalog form. 

 

From here you can apply multiple ways to process the serialized string. For example:

  • write a business rule to parse the string into the an json object, so that you can populate current gliderecord
  • a client script on the catalog form can be written to "validate" the serialized string and abort the submission if necessary.
    var json_obj = JSON.parse(g_form.getValue('u_json_config'));
    
    if (!json_obj.form_is_valid){
    return false; //abort form submission
    }
    ​

 

How all this helps.