SearchKit - Improve searchDisplay pager with additional options
[civicrm-core.git] / ext / search_kit / ang / crmSearchDisplay / traits / searchDisplayBaseTrait.service.js
CommitLineData
ab4e6f8a
CW
1(function(angular, $, _) {
2 "use strict";
3
4 // Trait provides base methods and properties common to all search display types
5 angular.module('crmSearchDisplay').factory('searchDisplayBaseTrait', function(crmApi4) {
6 var ts = CRM.ts('org.civicrm.search_kit');
7
8 // Replace tokens keyed to rowData.
9 // If rowMeta is provided, values will be formatted; if omitted, raw values will be provided.
10 function replaceTokens(str, rowData, rowMeta, index) {
11 if (!str) {
12 return '';
13 }
14 _.each(rowData, function(value, key) {
15 if (str.indexOf('[' + key + ']') >= 0) {
16 var column = rowMeta && _.findWhere(rowMeta, {key: key}),
17 val = column ? formatRawValue(column, value) : value,
18 replacement = angular.isArray(val) ? val[index || 0] : val;
19 str = str.replace(new RegExp(_.escapeRegExp('[' + key + ']', 'g')), replacement);
20 }
21 });
22 return str;
23 }
24
25 function getUrl(link, rowData, index) {
26 var url = replaceTokens(link, rowData, null, index);
27 if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
28 url = CRM.url(url);
29 }
30 return url;
31 }
32
33 // Returns display value for a single column in a row
34 function formatDisplayValue(rowData, key, columns) {
35 var column = _.findWhere(columns, {key: key}),
36 displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, columns) : formatRawValue(column, rowData[key]);
37 return angular.isArray(displayValue) ? displayValue.join(', ') : displayValue;
38 }
39
40 // Returns value and url for a column formatted as link(s)
41 function formatLinks(rowData, key, columns) {
42 var column = _.findWhere(columns, {key: key}),
43 value = formatRawValue(column, rowData[key]),
44 values = angular.isArray(value) ? value : [value],
45 links = [];
46 _.each(values, function(value, index) {
47 links.push({
48 value: value,
49 url: getUrl(column.link.path, rowData, index)
50 });
51 });
52 return links;
53 }
54
55 // Formats raw field value according to data type
56 function formatRawValue(column, value) {
57 var type = column && column.dataType,
58 result = value;
59 if (_.isArray(value)) {
60 return _.map(value, function(val) {
61 return formatRawValue(column, val);
62 });
63 }
64 if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
65 result = CRM.utils.formatDate(value, null, type === 'Timestamp');
66 }
67 else if (type === 'Boolean' && typeof value === 'boolean') {
68 result = value ? ts('Yes') : ts('No');
69 }
70 else if (type === 'Money' && typeof value === 'number') {
71 result = CRM.formatMoney(value);
72 }
73 return result;
74 }
75
76 // Return a base trait shared by all search display controllers
77 // Gets mixed in using angular.extend()
78 return {
79 page: 1,
80 rowCount: null,
81 getUrl: getUrl,
82
83 // Called by the controller's $onInit function
84 initializeDisplay: function($scope, $element) {
85 var ctrl = this;
10cd9d37 86 this.limit = this.settings.limit;
ab4e6f8a
CW
87 this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
88
b7fd7a81
CW
89 this.getResults = _.debounce(function() {
90 ctrl.runSearch();
ab4e6f8a
CW
91 }, 100);
92
93 // If search is embedded in contact summary tab, display count in tab-header
94 var contactTab = $element.closest('.crm-contact-page .ui-tabs-panel').attr('id');
95 if (contactTab) {
96 var unwatchCount = $scope.$watch('$ctrl.rowCount', function(rowCount) {
97 if (typeof rowCount === 'number') {
98 unwatchCount();
99 CRM.tabHeader.updateCount(contactTab.replace('contact-', '#tab_'), rowCount);
100 }
101 });
102 }
103
104 function onChangeFilters() {
105 ctrl.page = 1;
106 ctrl.rowCount = null;
107 if (ctrl.onChangeFilters) {
108 ctrl.onChangeFilters();
109 }
b7fd7a81 110 ctrl.getResults();
ab4e6f8a
CW
111 }
112
10cd9d37
CW
113 function onChangePageSize() {
114 ctrl.page = 1;
115 ctrl.getResults();
116 }
117
ab4e6f8a
CW
118 if (this.afFieldset) {
119 $scope.$watch(this.afFieldset.getFieldData, onChangeFilters, true);
120 }
10cd9d37
CW
121 if (this.settings.pager && this.settings.pager.expose_limit) {
122 $scope.$watch('$ctrl.limit', onChangePageSize);
123 }
ab4e6f8a
CW
124 $scope.$watch('$ctrl.filters', onChangeFilters, true);
125 },
126
127 // Generate params for the SearchDisplay.run api
128 getApiParams: function(mode) {
129 return {
130 return: mode || 'page:' + this.page,
131 savedSearch: this.search,
132 display: this.display,
133 sort: this.sort,
10cd9d37 134 limit: this.limit,
ab4e6f8a
CW
135 filters: _.assign({}, (this.afFieldset ? this.afFieldset.getFieldData() : {}), this.filters),
136 afform: this.afFieldset ? this.afFieldset.getFormName() : null
137 };
138 },
139
140 // Call SearchDisplay.run and update ctrl.results and ctrl.rowCount
b7fd7a81 141 runSearch: function() {
ab4e6f8a
CW
142 var ctrl = this;
143 return crmApi4('SearchDisplay', 'run', ctrl.getApiParams()).then(function(results) {
144 ctrl.results = results;
145 ctrl.editing = false;
146 if (!ctrl.rowCount) {
10cd9d37 147 if (!ctrl.limit || results.length < ctrl.limit) {
ab4e6f8a
CW
148 ctrl.rowCount = results.length;
149 } else if (ctrl.settings.pager) {
150 var params = ctrl.getApiParams('row_count');
151 crmApi4('SearchDisplay', 'run', params).then(function(result) {
152 ctrl.rowCount = result.count;
153 });
154 }
155 }
156 });
157 },
158 replaceTokens: function(value, row) {
159 return replaceTokens(value, row, this.settings.columns);
160 },
161 getLinks: function(rowData, col) {
162 rowData._links = rowData._links || {};
163 if (!(col.key in rowData._links)) {
164 rowData._links[col.key] = formatLinks(rowData, col.key, this.settings.columns);
165 }
166 return rowData._links[col.key];
167 },
168 formatFieldValue: function(rowData, col) {
169 return formatDisplayValue(rowData, col.key, this.settings.columns);
170 }
171 };
172 });
173
174})(angular, CRM.$, CRM._);