- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-19-2019 07:46 PM
On a portal page, I want to list "Requests created for or created by me", and I've almost got what I want with OOTB widgets.
I use a Simple List widget configured as follows (only showing the significant settings):
Table | sc_request |
Filter |
active=true^requested_forDYNAMIC90d1921e5f510100a9ad2572f2b477fe^ ORopened_byDYNAMIC90d1921e5f510100a9ad2572f2b477fe^EQ |
List page | list |
Link to this page | requests |
View | Custom Requests |
The "Custom Requests" view is a sc_request view that displays only the Number and Description columns.
My problem is that when I click the "View All" link, the columns being displayed are: Number, Task type, Short description and Priority.
So I started digging.
I cloned the "list" page and added some debug statements to the Server script and worked out that the view parameter is not being passed to the page. The very top dozen lines of the script are something like this:
(function() {
var zlog = new ZET_ScriptLog('WDG ZET Data Table from URL Definition - server');
deleteOptions(['table','field_list','filter','order_by', 'order_direction','order','maximum_entries']);
if (input) {
data.table = input.table;
data.view = input.view;
} else {
zlog.Log('input is null');
zlog.Log('$sp.getParameter(table) = ' + $sp.getParameter('table'));
zlog.Log('$sp.getParameter(t) = ' + $sp.getParameter('t'));
data.table = $sp.getParameter('table') || $sp.getParameter('t');
data.view = $sp.getParameter('view');
}
zlog.Log('data.table = ' + data.table);
zlog.Log('data.view = ' + data.view); <-- data.view is not being initialised
So I went back to the link that is accessed by the Simple List widget's View All link, and it also does not pass in the View parameter. I cloned the Simple List widget and added a view parameter to the Body HTML template (towards the bottom of the code. Before:
<div class="panel-footer" ng-if="!c.options.hide_footer && c.options.maximum_entries && c.data.count > c.options.maximum_entries">
<div class="h4 number-shown-label">{{c.getMaxShownLabel(c.options.maximum_entries, c.data.count)}}</div>
<a class="pull-right" ng-href="?id={{c.seeAllPage}}&table={{c.options.table}}&filter={{c.options.filter}}{{c.targetPageID}}">${View all}</a>
</div>
</div>
After (note the <a> element):
<div class="panel-footer" ng-if="!c.options.hide_footer && c.options.maximum_entries && c.data.count > c.options.maximum_entries">
<div class="h4 number-shown-label">{{c.getMaxShownLabel(c.options.maximum_entries, c.data.count)}}</div>
<a class="pull-right" ng-href="?id={{c.seeAllPage}}&table={{c.options.table}}&view={{c.options.view}}&filter={{c.options.filter}}{{c.targetPageID}}">${View all}</a>
</div>
</div>
With this change, my custom Simple List widget now indicates that the sys_id of the view is being passed in. If only that were the end of it. The columns being displayed changed to: Number, Requested for, Opened by, Request state, Due date.
In the "list" page, the view parameter eventually gets consumed by a call to $sp.getListColumns( data.table, data.view ).
After some experimentation, I worked out that if data.view is the name of the table view to be applied (i.e. "z_sc_requests" rather than the sys_id), then the correct columns get displayed when clicking on "View All".
(and to anyone that has made it this far, you have my most profound gratitude)
So my question(s) is(are): Should I have had to go through all the edits made above in order to control columns displayed on a list page? If not, where am I going wrong? Incidentally, am I right in concluding that $sp.getListColumns is expecting strings rather than sys_ids as parameters (and if that's the case should view be getting passed in as a name instead of a sys_id)?
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-23-2019 08:32 PM
For the benefit of anyone to follow, here's my workaround / solution:
Clone the "Simple LIst" widget.
Replace the Body HTML template with:
<div class="panel panel-{{::c.options.color}} b" ng-if="c.data.isValid && (c.options.always_show || c.data.filterText || c.data.list.length)">
<div class="panel-heading" ng-if="::!c.options.hide_header">
<h2 class="h4 panel-title">
<span ng-if="c.options.glyph">
<fa name="{{::c.options.glyph}}" />
</span>{{::c.options.title}}</h2>
<!-- <i class="fa fa-filter" ng-click="c.toggleFilter()" ng-class="{'disabled-filter': !c.showFilter}"></i> -->
<div ng-show="c.showFilter">
<input aria-label="${Filter}" ng-model="c.data.filterText" ng-model-options="{debounce: 300}" sn-focus="c.showFilter" placeholder="{{::data.filterMsg}}" ng-change="c.update()" class="form-control input-sm filter-box">
</div>
</div>
<ul class="list-group hide-x-overflow" ng-style="::{maxHeight: c.getMaxHeight()}" style="overflow-y: auto;">
<li ng-if="c.data.list.length > 0" ng-repeat="item in c.data.list track by item.sys_id" class="list-group-item">
<a class="focus-inline-block" ng-click="c.onClick($event, item, item.url, {})" href="javascript:void(0)" >
<span ng-repeat="action in c.data.actions" href="" ng-click="c.onClick($event, item, action.url, action)" ng-if="action.glyph"
class="list-action l-h-40 pull-right">
<fa name="{{action.glyph}}" ng-class="c.getActionColor(action)" />
</span>
<span ng-if="c.options.image_field" class="pull-left m-r"
ng-class="{'avatar': c.options.rounded_images, 'thumb-sm': c.options.rounded_images}">
<img ng-src="{{item.image_field}}" alt="..." class="img-sm" ng-class="{'img-circle': c.options.rounded_images}">
</span>
<div>
<div ng-switch on="item.display_field.type" ng-class="{'l-h-40': !item.secondary_fields.length}">
<span class="translated-html" ng-switch-when="translated_html" ng-bind-html="item.display_field.value"></span>
<div ng-switch-default>{{item.display_field.display_value}}</div>
</div>
<small class="text-muted" ng-repeat="f in item.secondary_fields">
<span ng-if="!$first"> • </span>
<span ng-switch="f.type" title="{{::f.label}}">
<span ng-switch-when="glide_date"><sn-time-ago timestamp="::f.value" /></span>
<span ng-switch-when="glide_date_time"><sn-time-ago timestamp="::f.value" /></span>
<span ng-switch-default="">{{f.display_value}}</span>
</span>
</small>
</div>
</a>
</li>
<div ng-if="!c.data.list.length" class="list-group-item">
${No records found}
</div>
</ul>
<div class="panel-footer" ng-if="!c.options.hide_footer && c.options.maximum_entries && c.data.count > c.options.maximum_entries">
<div class="h4 number-shown-label">{{c.getMaxShownLabel(c.options.maximum_entries, c.data.count)}}</div>
<a class="pull-right" ng-href="?id={{c.seeAllPage}}&table={{c.options.table}}&view={{c.options.view}}&filter={{c.options.filter}}{{c.targetPageID}}">${View all}</a>
</div>
</div>
The above includes the view parameter in the "View All" link.
Secondly, clone the "Data Table from URL Definition" widget.
Replace the server script with:
(function() {
deleteOptions(['table','field_list','filter','order_by', 'order_direction','order','maximum_entries']);
if (input) {
data.table = input.table;
data.view = input.view;
} else {
data.table = $sp.getParameter('table') || $sp.getParameter('t');
data.view = $sp.getParameter('view');
// Convert data.view to a view Id (data.view is probably a sys_id at this point. $sp.getListColumns expects an Id)
var grPage = new GlideRecord('sys_ui_view');
var grPageOr = grPage.addQuery('sys_id',data.view);
grPageOr.addOrCondition('name',data.view);
grPage.query();
if(grPage.next()){
data.view = grPage.getValue('name');
}
}
if (!data.table) {
data.invalid_table = true;
data.table_label = "";
return;
}
var gr = new GlideRecordSecure(data.table);
if (!gr.isValid()) {
data.invalid_table = true;
data.table_label = data.table;
return;
}
// page is where the record URLs go, URL parameter wins
data.page_id = $sp.getParameter("target_page_id");
zlog.Log('data.page_id = ' + data.page_id);
if (!data.page_id) {
zlog.Log('data.page_id is null');
var sp_page = $sp.getValue('sp_page');
if (sp_page) {
var pageGR = new GlideRecord('sp_page');
pageGR.get(sp_page);
data.page_id = pageGR.id.getDisplayValue();
}
}
// widget parameters
data.table_label = gr.getLabel();
data.fields = $sp.getListColumns(data.table, data.view);
copyParameters(data, ['p', 'o', 'd', 'filter']);
copyParameters(data, ['relationship_id', 'apply_to', 'apply_to_sys_id']);
data.filterACLs = true;
data.show_new = true;
data.show_keywords = true;
data.show_breadcrumbs = true;
data.fromUrl = true;
data.headerTitle = (options.use_instance_title == "true") ? options.title : gr.getPlural();
data.enable_filter = input.enable_filter || options.enable_filter == true || options.enable_filter == "true";
data.dataTableWidget = $sp.getWidget('widget-data-table', data);
function copyParameters(to, names) {
names.forEach(function(name) {
data[name] = $sp.getParameter(name);
})
}
// in case this widget is tied to the wrong instance type
function deleteOptions(names) {
names.forEach(function(name) {
delete options[name];
})
}
})()
In this I've added some code (after line 😎 that reads the data.view parameter (which is a sys_id), and converts it into a string (the name of the view to be used by the table).
Next, clone the "list" page, and open it in page editor. Change the page widget to the cloned "Data Table from URL Definition" record created above.
You now need to create a table view that contains the columns you want to display in the portal. Make a note of the name of this view.
Open your portal page that is to host the cloned Simple List widget, and place your cloned Simple List widget to an appropriate place. Edit the widget's options.
Set List Page to the cloned page created above.
Set View to the table view created above.
Assuming I haven't forgotten any steps, the View All link should now present the table view you have specified.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎09-23-2019 08:32 PM
For the benefit of anyone to follow, here's my workaround / solution:
Clone the "Simple LIst" widget.
Replace the Body HTML template with:
<div class="panel panel-{{::c.options.color}} b" ng-if="c.data.isValid && (c.options.always_show || c.data.filterText || c.data.list.length)">
<div class="panel-heading" ng-if="::!c.options.hide_header">
<h2 class="h4 panel-title">
<span ng-if="c.options.glyph">
<fa name="{{::c.options.glyph}}" />
</span>{{::c.options.title}}</h2>
<!-- <i class="fa fa-filter" ng-click="c.toggleFilter()" ng-class="{'disabled-filter': !c.showFilter}"></i> -->
<div ng-show="c.showFilter">
<input aria-label="${Filter}" ng-model="c.data.filterText" ng-model-options="{debounce: 300}" sn-focus="c.showFilter" placeholder="{{::data.filterMsg}}" ng-change="c.update()" class="form-control input-sm filter-box">
</div>
</div>
<ul class="list-group hide-x-overflow" ng-style="::{maxHeight: c.getMaxHeight()}" style="overflow-y: auto;">
<li ng-if="c.data.list.length > 0" ng-repeat="item in c.data.list track by item.sys_id" class="list-group-item">
<a class="focus-inline-block" ng-click="c.onClick($event, item, item.url, {})" href="javascript:void(0)" >
<span ng-repeat="action in c.data.actions" href="" ng-click="c.onClick($event, item, action.url, action)" ng-if="action.glyph"
class="list-action l-h-40 pull-right">
<fa name="{{action.glyph}}" ng-class="c.getActionColor(action)" />
</span>
<span ng-if="c.options.image_field" class="pull-left m-r"
ng-class="{'avatar': c.options.rounded_images, 'thumb-sm': c.options.rounded_images}">
<img ng-src="{{item.image_field}}" alt="..." class="img-sm" ng-class="{'img-circle': c.options.rounded_images}">
</span>
<div>
<div ng-switch on="item.display_field.type" ng-class="{'l-h-40': !item.secondary_fields.length}">
<span class="translated-html" ng-switch-when="translated_html" ng-bind-html="item.display_field.value"></span>
<div ng-switch-default>{{item.display_field.display_value}}</div>
</div>
<small class="text-muted" ng-repeat="f in item.secondary_fields">
<span ng-if="!$first"> • </span>
<span ng-switch="f.type" title="{{::f.label}}">
<span ng-switch-when="glide_date"><sn-time-ago timestamp="::f.value" /></span>
<span ng-switch-when="glide_date_time"><sn-time-ago timestamp="::f.value" /></span>
<span ng-switch-default="">{{f.display_value}}</span>
</span>
</small>
</div>
</a>
</li>
<div ng-if="!c.data.list.length" class="list-group-item">
${No records found}
</div>
</ul>
<div class="panel-footer" ng-if="!c.options.hide_footer && c.options.maximum_entries && c.data.count > c.options.maximum_entries">
<div class="h4 number-shown-label">{{c.getMaxShownLabel(c.options.maximum_entries, c.data.count)}}</div>
<a class="pull-right" ng-href="?id={{c.seeAllPage}}&table={{c.options.table}}&view={{c.options.view}}&filter={{c.options.filter}}{{c.targetPageID}}">${View all}</a>
</div>
</div>
The above includes the view parameter in the "View All" link.
Secondly, clone the "Data Table from URL Definition" widget.
Replace the server script with:
(function() {
deleteOptions(['table','field_list','filter','order_by', 'order_direction','order','maximum_entries']);
if (input) {
data.table = input.table;
data.view = input.view;
} else {
data.table = $sp.getParameter('table') || $sp.getParameter('t');
data.view = $sp.getParameter('view');
// Convert data.view to a view Id (data.view is probably a sys_id at this point. $sp.getListColumns expects an Id)
var grPage = new GlideRecord('sys_ui_view');
var grPageOr = grPage.addQuery('sys_id',data.view);
grPageOr.addOrCondition('name',data.view);
grPage.query();
if(grPage.next()){
data.view = grPage.getValue('name');
}
}
if (!data.table) {
data.invalid_table = true;
data.table_label = "";
return;
}
var gr = new GlideRecordSecure(data.table);
if (!gr.isValid()) {
data.invalid_table = true;
data.table_label = data.table;
return;
}
// page is where the record URLs go, URL parameter wins
data.page_id = $sp.getParameter("target_page_id");
zlog.Log('data.page_id = ' + data.page_id);
if (!data.page_id) {
zlog.Log('data.page_id is null');
var sp_page = $sp.getValue('sp_page');
if (sp_page) {
var pageGR = new GlideRecord('sp_page');
pageGR.get(sp_page);
data.page_id = pageGR.id.getDisplayValue();
}
}
// widget parameters
data.table_label = gr.getLabel();
data.fields = $sp.getListColumns(data.table, data.view);
copyParameters(data, ['p', 'o', 'd', 'filter']);
copyParameters(data, ['relationship_id', 'apply_to', 'apply_to_sys_id']);
data.filterACLs = true;
data.show_new = true;
data.show_keywords = true;
data.show_breadcrumbs = true;
data.fromUrl = true;
data.headerTitle = (options.use_instance_title == "true") ? options.title : gr.getPlural();
data.enable_filter = input.enable_filter || options.enable_filter == true || options.enable_filter == "true";
data.dataTableWidget = $sp.getWidget('widget-data-table', data);
function copyParameters(to, names) {
names.forEach(function(name) {
data[name] = $sp.getParameter(name);
})
}
// in case this widget is tied to the wrong instance type
function deleteOptions(names) {
names.forEach(function(name) {
delete options[name];
})
}
})()
In this I've added some code (after line 😎 that reads the data.view parameter (which is a sys_id), and converts it into a string (the name of the view to be used by the table).
Next, clone the "list" page, and open it in page editor. Change the page widget to the cloned "Data Table from URL Definition" record created above.
You now need to create a table view that contains the columns you want to display in the portal. Make a note of the name of this view.
Open your portal page that is to host the cloned Simple List widget, and place your cloned Simple List widget to an appropriate place. Edit the widget's options.
Set List Page to the cloned page created above.
Set View to the table view created above.
Assuming I haven't forgotten any steps, the View All link should now present the table view you have specified.