Get a first look at what's coming. The Developer Passport Australia Release Preview kicks off March 12. Dive in! 

Client controller must contain a JavaScript function, see default value for example

ServiceNow Use6
Tera Guru

Hi,

In the widget i am getting error under Client Controller

"Client controller must contain a JavaScript function, see default value for example". . Kindly help.

 

			function($scope, TimeCardPortalService, spUtil, $sanitize, $rootScope, $window, $timeout, spModal, $q) {
	var c = this;
	/*
	console.log('c:');
	console.log(c);
	*/
				
	_highlightField();
	var skipNextRecordWatchUpdate = false;
	$scope.pageSize = $scope.data.pageSize;
	var minWidthFields = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "total"];
	$scope.getWidth = function(field) {
			if(field && $scope.data.column_meta[field] 
			 && $scope.data.column_meta[field]['width_in_percent']) {
				return {
						'min-width': $scope.data.column_meta[field]['width_in_percent'] + '%'
				};
			}
			
			// Days and Total will have Minimum width of 5%.
			// Smaller the resolution, smaller the scroll. 
			if (field && $scope.data.column_meta[field] && (minWidthFields.indexOf(field) > -1)) {
				return {
					'min-width': '5%'
				}
			}
			
			// Default width of 10% which has not configured width_in_percent.
			return {
				'min-width': '10%'
			}
	}
	
	function updatePagination(from, to) {
			$scope.total = c.data.totalRows;
			$scope.current = {
					to: to > c.data.totalRows? c.data.totalRows: to,
					from: from,
					pageSize: $scope.pageSize
			};
			$scope.paginatedList = c.data.list.slice(from-1, to);
	}
	
	updatePagination(1, $scope.pageSize);
	
	$scope.$on('sp_load_new_page', function(event, page) {
			$scope.paginatedList = c.data.list.slice(page.from-1, page.to);
	});
	
	$scope.cancelEdit = function(evt, index){
		if(_isEmptyObject($scope.data.editTimecardObj))
			return;
		_preventDefault(evt);
		$scope.data.list[index] = $scope.data.editTimecardObj;
		$scope.data.editTimecardObj = {};
		$scope.data.timecardInEditMode = '';
		$scope.data.highlightedField = $scope.data.previousHighlightedField || $scope.data.defaultField;
		$scope.highlightDuplicates();
	};
	
	$scope.saveRecord = function(evt, record, promiseResolver){
		_preventDefault(evt);
		if($scope.data.recordSetForDelete == record.sys_id) return;
		var data = {};
		var editabelFields = $scope.data.editable_fields;
		for (var i in editabelFields){
			var field = editabelFields[i];
			if(field === "rate_type" || field === "project_time_category" || field === "resource_plan" ) {
				// we don't need sanitization here as this is not directly edited by user
				// this value is only selected from pre existing values and typed by user
				data[field] = record[field].value;
				continue;
			}
			var val = $sanitize(record[field].display_value);
			if(!isValidNumber(val))
				data[field] = 0;
			else
				data[field] = val;

		}
		$scope.data.timecardInEditMode = '';
		$scope.data.editTimecardObj = {};
		TimeCardPortalService.updateTimeCard(record.sys_id, data, editabelFields.join(',')+',total').then(function(response){
			var result = response.data.result;
			for(var field in result){
				record[field].display_value = result[field].display_value;
				record[field].value = result[field].value;
				if(isValidNumber(result[field].value))
					record[field].invalid = false;
				$scope.data.highlightedField = $scope.data.previousHighlightedField;
			}
			var status = response.data.status;
			if(status == 'error')
				refreshWidget();
			else {
				skipNextRecordWatchUpdate = true;
				$scope.highlightDuplicates();
			}
			if(promiseResolver) {
				promiseResolver();
			}
		});
	};

	$scope.deleteRecord = function(evt){
		var _target = $(evt.target);
		var _id = _target.closest('.flex-item').data('id')  || evt.target.id;
		var record = $scope.data.list.filter(function(listItem) {
			return (listItem.sys_id == _id);
		})[0];
		var confirmModalCtrl;

		var index = $(".container .table-body .flex.tc-row").index(_target.closest('.flex.tc-row'));
		$scope.data['tcPopover' + record.sys_id] = false;

		$scope.data.confirmDialogOpts = {
			title: $scope.data.messages.confirmDlgTitle,
			text: $scope.data.messages.confirmMssg,
			okTxt: $scope.data.messages['delete'],
			cancelTxt: $scope.data.messages.cancel,
			okBtnClass: 'btn-danger',
			ok: function(){
				$scope.data.recordSetForDelete = record.sys_id;
				TimeCardPortalService.deleteTimeCard(record.sys_id).then(function(response){
					$scope.data.recordSetForDelete = '';
					$scope.data.list.splice(index, 1);
					skipNextRecordWatchUpdate = true;
					$scope.highlightDuplicates();
					c.data.totalRows -= 1;
					updatePagination($scope.current.from, $scope.current.to);
					$rootScope.$broadcast("record-delete");
				});
				confirmModalCtrl.close();
			},
			afterOpen: function(ctrl){
				confirmModalCtrl = ctrl;
			},
			afterClose : function(){
				$scope.showConfirm = false;
				confirmModalCtrl = null;
				$scope.data.confirmDialogOpts = null;
			}
		};
		$scope.showConfirm = true;
		$scope.safeDigest();
		_preventDefault(evt);
	};
	
	function updateRecordState(evt, newState) {
		var _target = $(evt.target);
		var _id = _target.closest('.flex-item').data('id') || evt.target.id;
		var record = $scope.data.list.filter(function(listItem) {
			return (listItem.sys_id == _id);
		})[0];
		TimeCardPortalService.updateTimeCardState(record.sys_id, newState).then(function(response) {
			var status = response.data.status;
			var message = response.data.data.message;
			if (status != 'success')
				spUtil.addErrorMessage('message');
			refreshWidget();
		})
	}

	$scope.updateRecordState = function(evt, newState){
		$scope.hideTcPopover();
		if($scope.data.timecardInEditMode) {
			cancelGridEditModePromiseWrapper(event, true).then(function () {
				updateRecordState(evt, newState);
			})
		} else {
			updateRecordState(evt, newState);
		}
	};

	$scope.recallRecord = function(evt){
		var _target = $(evt.target);
		var _id = _target.closest('.flex-item').data('id')  || evt.target.id;
		var record = $scope.data.list.filter(function(listItem) {
			return (listItem.sys_id == _id);
		})[0];
		var confirmModalCtrl;

		var index = $(".container .table-body .flex.tc-row").index(_target.closest('.flex.tc-row'));
		$scope.data['tcPopover' + record.sys_id] = false;

		$scope.data.confirmDialogOpts = {
			title: $scope.data.messages.recallDlgTitle,
			text: $scope.data.messages.recallConfirmMssg,
			okTxt: $scope.data.messages.recall,
			cancelTxt: $scope.data.messages.cancel,
			okBtnClass: 'btn-danger',
			ok: function(){
				TimeCardPortalService.updateTimeCardState(record.sys_id, 'Recalled').then(function(response) {
					var status = response.data.status;
					var message = response.data.data.message;
					if (status != 'success')
						spUtil.addErrorMessage(message);
					else
						spUtil.addInfoMessage(message);
				})
				confirmModalCtrl.close();
			},
			afterOpen: function(ctrl){
				confirmModalCtrl = ctrl;
			},
			afterClose : function(){
				$scope.showConfirm = false;
				confirmModalCtrl = null;
				$scope.data.confirmDialogOpts = null;
			}
		};
		$scope.showConfirm = true;
		$scope.safeDigest();
		_preventDefault(evt);
	};

	$scope.generateTimecards = function(){
		TimeCardPortalService.generateTimecards({
			timesheet_id: $scope.data.timesheetId,
			tables: $scope.data.tables
		}).then(function(response){
			spUtil.addInfoMessage(response.data.message);
		});
	};

	$scope.copyFromPreviousTimesheet = function(){
		$rootScope.$broadcast('tcp.copy.timesheet');
	};

	$scope.validateInput = function(field){
		var val = field.display_value;
		if(isValidNumber(val))
			field.invalid = false;
		else
			field.invalid = true;
	};

	function isValidNumber(number){
		var decimalSeperator = g_user_decimal_separator;
		var regx =  new RegExp('^-?((\\d+(\\'+ decimalSeperator + '\\d+)?)|(\\' + decimalSeperator + '\\d+))$');
		if(regx.test(number) && !isNaN(parseFloat(number))){
			return true;
		}
		return false;
	}

	function refreshWidget(start, end){
		/* Carry forward options for grid customizaions */
		start = start ? start:1;
		end = end ? end: $scope.pageSize
		$scope.data.options = $scope.options.tm_grid_options;
		spUtil.update($scope).then(function(){
			var timecardToEdit = $scope.data.timecardToEdit;
			if(timecardToEdit){
				$scope.data.timecardToEdit = '';
				$scope.showTimecardInEditMode(timecardToEdit);
			}
			updatePagination(start, end);
			$scope.highlightDuplicates();
			$scope.addSyncScrollEventListener();
		});
	}

	var filterQuery = 'time_sheet='+ $scope.data.timesheetId;
	var recordWatcherTimer;
	spUtil.recordWatch($scope, 'time_card', filterQuery, function(){
		$timeout.cancel(recordWatcherTimer);
		recordWatcherTimer = $timeout(function(){
			if (skipNextRecordWatchUpdate) {
				skipNextRecordWatchUpdate = false;
				$scope.highlightDuplicates();
			} else
				refreshWidget();
		}, 250);
	});

	$scope.mergeDuplicateTimeCards = function(record) {
		TimeCardPortalService.mergeDuplicateTc({
			timesheet_id: $scope.data.timesheetId,
			record : record
		}).then(function() {
			refreshWidget();
		});
	};

	$scope.$on('tcp.refresh.widgets', function(){
		skipNextRecordWatchUpdate = true;
		refreshWidget();
	});

	$scope.$on('tcp.highlight.field',function(scope, field){
		_highlightField(field);
	});

	$scope.$on('tcp.edit.timecard', function(scope, timecardId){
		$scope.showTimecardInEditMode(timecardId);
	});
	
	var foundRecord = null;
	
	$scope.$on('pagination.send.window.for.index', function() {
		$scope.enableEditMode(foundRecord[0]);
		foundRecord = null;
	});
	
	$scope.$on('ng.repeat.completed.tc-grid', function() {
		if(foundRecord) {
			$scope.enableEditMode(foundRecord[0]);
			foundRecord = null;
		}
	});

	function cancelGridEditModePromiseWrapper(event, buttonClicked) {
		return $q(function(resolve) {
			$scope.cancelGridEditMode(event, buttonClicked, resolve);
		});
	}
	
	function submitTimesheet (timesheetId, oldState) {
		TimeCardPortalService.submitTimesheet(timesheetId).then(function(response){
				$scope.data.canSubmitTimesheet = response.data.canSubmitTimesheet;
				var newState = response.data.data.value;
				$scope.data.state = response.data.data;
				var mssg = response.data.message;
				if(response.data.status == 'error'){
					refreshWidget();
					if(mssg)
						spUtil.addErrorMessage(mssg);
				}
				else if(oldState == newState){
					refreshWidget();
					if(mssg)
						spUtil.addInfoMessage(mssg);
				}
				else if(mssg && response.data.status == 'success'){
					spUtil.addInfoMessage(mssg);
				}
			}
		);
	}
	
	function submitTimesheetButtonClicked(event, timesheetId, oldState) {
		if($scope.data.timecardInEditMode) {
			cancelGridEditModePromiseWrapper(event, false).then(function () {
				submitTimesheet(timesheetId, oldState);
			})
		} else {
			submitTimesheet(timesheetId, oldState);
		}
	}

	$scope.$on('submitTimesheetButtonClicked', function(event, data) {
		submitTimesheetButtonClicked(data.event, data.timesheetId, data.oldState);
	});

	$scope.showTimecardInEditMode = function(timecardId){
		var index = null
		var record = $scope.data.list.filter(function(record, idx){
			if(record.sys_id == timecardId) {
				index = index ? index: idx;
				return true;
			}
			return false;
		});
		
		if(record.length > 0) {
			foundRecord = record;
			
			// if index is on current page. directly call enableEditmode.
			// else figure out on which page it is.
			var pageIndex = index + 1;
			if (pageIndex <= $scope.current.to && pageIndex >= $scope.current.from) {
				$scope.enableEditMode(foundRecord[0]);
			} else {
				// figure out which page it will be.
				var targetPage = parseInt(pageIndex / $scope.current.pageSize);
				targetPage += pageIndex % $scope.current.pageSize ? 1 : 0;
				$rootScope.$broadcast('pagination.get.window.for.index',  {
					targetPage: targetPage
				});
			}
		} else {
			$scope.data.timecardToEdit = timecardId;
			skipNextRecordWatchUpdate = true;
			refreshWidget();
		}
	};

	function _highlightField(field){
		if(field)
			$scope.data.highlightedField = field;
		else
			$scope.data.highlightedField = $scope.data.defaultField;
	}

	function _preventDefault(evt){
		if(evt){
			evt.stopPropagation();
			evt.preventDefault();
		}
	}

	function _isEmptyObject(obj){
		return !obj || Object.keys(obj).length === 0;
	}

	$scope.openTimecard = function(ev){
		_preventDefault(ev);
		var _target = $(ev.target);
		var sysId = _target.closest('.flex-item').data('id');
		$scope.data['tcPopover' + sysId] = false;

		spUtil.get('widget-modal', {embeddedWidgetId: 'widget-form', embeddedWidgetOptions: {table:'time_card', sys_id: sysId, view: 'worker_portal'}}).then(function(widget){
			var modalCtrl;
			var unregister = $scope.$on('sp.form.record.updated', function(){
				skipNextRecordWatchUpdate = true;
				refreshWidget();
				$rootScope.$broadcast("record-add");
				modalCtrl.close();
			});
			widget.options.afterClose = function(){
				unregister();
				$scope.data.modalInstance = null;
				modalCtrl = null;
				$rootScope.$broadcast("timesheet_portal_form_closed");
			};
			widget.options.afterOpen = function(ctrl){
				modalCtrl = ctrl;
			};
			$scope.data.modalInstance = widget;
		});
	};

	$scope.highlightDuplicates = function() {
		$scope.duplicateList = {};
		if($scope.data.list && $scope.data.list.length) {
			for(var i=0; i< $scope.data.list.length; i++) {
				list_item = $scope.data.list[i];
				var _sd = list_item['task.short_description'].value.replace(/\s/g, "");
				var _state = list_item.state.value;
				if($scope.data.restrictedStatesForDuplicateMerge.indexOf(_state.toLowerCase()) !== -1)
					continue;
				var _ptc = list_item.project_time_category.value || '';
				var _taskSysId = list_item.taskSysId;
				
				//Since task can be associated with any categories
				var _category = list_item.category.value;
				
				var _rateType = (list_item.rate_type && list_item.rate_type.value) || '';
				var _rpln = (list_item.resource_plan && list_item.resource_plan.value) || '';

				var uniqueItemKey = _sd + _state + _ptc + _rateType + _taskSysId + _category + _rpln;
				var duplicateList = $scope.duplicateList;
				if(duplicateList[uniqueItemKey] && duplicateList[uniqueItemKey].length && _taskSysId) {
					duplicateList[uniqueItemKey][0].isDuplicate = true;
					duplicateList[uniqueItemKey].push(list_item);
					list_item.isDuplicate = true;
				} else {
					duplicateList[uniqueItemKey] = [];
					duplicateList[uniqueItemKey].push(list_item);
				}
			}
		}

		for(var prop in $scope.duplicateList) {
			if($scope.duplicateList[prop] && $scope.duplicateList[prop].length === 1) {
				$scope.duplicateList[prop][0].isDuplicate = false;
			}
		}
		$scope.$$phase || $scope.$root && $scope.$root.$$phase || $scope.$digest();
	};

	$scope.highlightDuplicates();
}

 

Regards

Suman P.

1 REPLY 1

Tanushree Maiti
Tera Sage

Try this tips if it works for you.

https://www.servicenow.com/community/developer-forum/client-controller-must-contain-a-javascript-fun...

 

https://www.servicenow.com/community/developer-forum/error-message-on-client-controller-quot-it-shou...

 

Please mark this response as Helpful & Accept it as solution if it assisted you with your question.
Regards
Tanushree Maiti
ServiceNow Technical Architect
Linkedin: