-
-
Save wave-inguane/d0258d840816358ea9b35c02412f9abd to your computer and use it in GitHub Desktop.
Notes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ($scope, $rootScope, $timeout, spUtil,spModal, $location, $window, nowAttachmentHandler, $log, spAriaUtil) { | |
var url = $location.absUrl(); | |
$timeout(function() { | |
if (g_form) { | |
g_form.submit(action.action_name || action.sys_id); | |
if(url.indexOf('x_smart_selection_sf_rank') != -1){ | |
$window.open('/selection?id=sel_dash','_self'); | |
} | |
} | |
}); | |
} | |
} | |
function($scope, $location) { | |
/* widget controller */ | |
var c = this; | |
var url = "/$pwd_reset.do?sysparm_url=ss_default"; | |
for (var key in urlParams) { | |
if (key == 'sysparm_redirect_url') | |
url += '&sysparm_redirect_url=' + urlParams[key]; | |
if (key == 'sysparm_show_custom_header_footer') | |
url += '&sysparm_show_custom_header_footer=' + urlParams[key]; | |
} | |
$scope.iframeUrl = url; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div> | |
<button type="button" class="btn btn-primary" ng-mousedown="c.handleEvent($event);" ng-mouseover="c.handleEvent($event);" >Primary</button> | |
</div> | |
<div ng-mouseleave="handleEvent2($event)"> </div> | |
api.controller=function($scope, spUtil) { | |
/* widget controller */ | |
var c = this; | |
//ng-mousedown="handleEvent($event)" | |
c.handleEvent = function (e) { | |
if(e.type == "mousedown"){ | |
//spUtil.addInfoMessage("here: "+ e.type); | |
var done = '<p class="text-center text-primary">'+ "here: "+ e.type +'</p>'; | |
spUtil.addInfoMessage(done); | |
} | |
if(e.type == "mouseover"){ | |
// spUtil.addInfoMessage("here: "+ e.type); | |
} | |
} | |
//usage in | |
//<div ng-mouseleave="handleEvent($event)"> </div> | |
//----------------------------------------------- | |
// To be used during development test phase only | |
// Provides impartial feedback on new features | |
//----------------------------------------------- | |
$scope.handleEvent2 = function (e) { | |
//use in footer | |
//Triggered when the pointer is moved to be within the screen region occupied by | |
//the element or one of its descendants. | |
if(e.type == "mouseenter"){ | |
} | |
//use in live chat | |
//Triggered when the mouse button is pressed. | |
if(e.type == "mousedown"){ | |
} | |
//use on thumbs | |
//The same as for mouseenter, except that this event will trigger while the pointer is | |
//still over a descendant element. | |
if(e.type == "mouseover"){ | |
} | |
// use in serch area | |
//Triggered when the pointer is moved to be outside the screen region occupied | |
//by the element and all its descendants. | |
if(e.type == "mouseleave"){ | |
} | |
//Triggered when the pointer is moved while over the element. | |
if(e.type == "mousemove"){ | |
} | |
//The same as for mouseleave, except that this event will trigger while the pointer is | |
//still over a descendant element. | |
if(e.type == ""){ | |
} | |
//Triggered when the mouse button is released. | |
if(e.type == "mouseup"){ | |
} | |
} | |
}; | |
/* | |
$rootScope.$on('action_data', function(event,actionData) { | |
if((actionData.action_request == "report_submit") && | |
(actionData.item_id == "-1") && | |
(actionData.parent_table == "") && | |
(actionData.table_name == "")){ | |
c.server.get({ | |
action: 'submitToCC', | |
SysID: actionData.item_id | |
}).then(function(res) { | |
var r = res.data.canSubmit; | |
if(r == false || r =="false"){ | |
var done = '<p class="text-center text-primary">'+ 'Please Save as Draft' +'</p>'; | |
spUtil.addInfoMessage(done); | |
return; | |
} | |
}); | |
} | |
}); | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//================================================================================================================================= | |
// Notes Body | |
//================================================================================================================================= | |
<div class="panel panel-default" ng-show="c.data.noteID"> | |
<div class="panel-heading clearfix"> | |
<div class="row"> | |
<div class="col-md-11"> | |
<input class="form-control" id="note-title" ng-model="c.data.title" ng-change="c.updateNote('title')" ng-model-options="{debounce: 1000}"/> | |
</div> | |
<!-- | |
<div class="col-md-1"> | |
<button class="btn btn-danger pull-right" ng-click="c.confirmDelete()"> | |
<span class="glyphicon glyphicon-trash"></span> | |
</button> | |
</div> | |
--> | |
<!-- USE CUSTOM DIRECTIVE --> | |
<delete-button-confirm></delete-button-confirm> | |
</div> | |
</div> | |
<div class="panel-body"> | |
<textarea class="form-control" id="note-body" ng-model="c.data.note" ng-change="c.updateNote('body')" ng-model-options="{debounce: 1000}" ></textarea> | |
</div> | |
</div> | |
//============================================================================================================================ | |
// | |
//============================================================================================================================ | |
function($scope,$rootScope,spUtil,spModal) { | |
/* widget controller */ | |
var c = this; | |
/* | |
$rootScope.$on('selectNote2', function(event,data) { | |
console.log('Listener caught NoteID-2: ' + data.sys_id); | |
}); | |
*/ | |
//CRUD-OPERATIONS | |
//---------------------------------------------------------- | |
// CREATE | |
//---------------------------------------------------------- | |
$rootScope.$on('selectNote', function(event,myData) { | |
console.log('Listener-1 caught NoteID: ' + $rootScope.noteID); | |
console.log('Listener-x caught NoteID: ' + typeof myData); | |
console.log('Listener-2 caught NoteID: ' + myData); | |
console.log('Listener-3 caught NoteID: ' + $rootScope.myJson.sys_id); | |
//----------------------------------------------------------- | |
// READ | |
//----------------------------------------------------------- | |
c.server.get({ | |
action: 'getNote', | |
noteID: $rootScope.noteID | |
}).then(function(r) { | |
c.data.title = r.data.note.title; | |
c.data.note = r.data.note.note; | |
c.data.noteID = r.data.note.sys_id; | |
}); | |
}); | |
//----------------------------------------------------------- | |
// UPDATE | |
//----------------------------------------------------------- | |
c.updateNote = function(updateType) { | |
c.server.get({ | |
action: 'updateNote', | |
noteID: c.data.noteID, | |
noteBody: c.data.note, | |
noteTitle: c.data.title | |
}).then(function(r) { | |
//Notify the user of the outcome of the action | |
}); | |
} | |
//----------------------------------------------------------- | |
// DLETE | |
//----------------------------------------------------------- | |
c.deleteNote = function() { | |
c.server.get({ | |
action: 'deleteNote', | |
noteID: c.data.noteID | |
}).then(function(r) { | |
spUtil.addTrivialMessage("The " + c.data.title + " record has been deleted."); | |
$rootScope.$emit('deleteNote', c.data.noteID); | |
c.data.title = ''; | |
c.data.note = ''; | |
c.data.noteID = ''; | |
}); | |
} | |
c.confirmDelete = function(){ | |
spModal.confirm('<p class="lead">'+'<center>'+"Are you sure you want to delete this Note record ?"+'</center>').then(function(confirmed){ | |
if(confirmed){ | |
c.deleteNote(); | |
} | |
}); | |
} | |
} | |
//============================================================================================================================ | |
// | |
//============================================================================================================================ | |
(function() { | |
/* populate the 'data' object */ | |
/* e.g., data.table = $sp.getValue('table'); */ | |
/* | |
Question: Recall that only Notes records for the currently logged in user are displayed in the Notes List widget. How is the User value set on the new Notes records? | |
Answer: There are a few different strategies one could use to set the User value for new Note records including: | |
Set the value in the widget's Server-side script | |
Set the value using a before Business Rule for record inserts | |
Set a default value in the User field's Dictionary Entry | |
The drawback to using the Server-side widget script is that if users use the CreateNotes > Notes module to create a new record, the User field value must be manually set. The benefit to using this strategy is that the logic exists in the Widget itself and is easy to find. | |
The drawback to using a before Business Rule is that you have another application file to maintain and you, the developer, have to remember not all of the logic for your application is in the widget scripts. | |
The drawback to setting a default value in the User field's Dictionary Entry is that you have to remember you did that. You can see the field's default value in the Table record but you have to remember to look there. | |
The Dictionary Entry default value is how the User field is set in the CreateNotes application you loaded from the GitHub repository for this exercise. | |
*/ | |
if (input && input.noteID) { | |
var note = new GlideRecord('x_snc_createnotes_note'); | |
if (note.get(input.noteID)) { | |
//----------------------------------------------------------- | |
// READ | |
//----------------------------------------------------------- | |
if (input.action == 'getNote') { | |
data.note = {}; | |
$sp.getRecordValues(data.note, note, "title, note, sys_id"); | |
} | |
//----------------------------------------------------------- | |
// UPDATE | |
//----------------------------------------------------------- | |
else if (input.action == 'updateNote') { | |
note.title = input.noteTitle; | |
note.note = input.noteBody; | |
var f = note.update(); | |
// Need to update the data object with the new title | |
// and first 20 characters of the note (do not want to write huge | |
// strings to the Notes List widget) | |
//if(f){ | |
data.title = note.getValue('title'); | |
data.note = note.getValue('note');//.slice(0,20); | |
//} | |
} | |
//----------------------------------------------------------- | |
// DLETE | |
//----------------------------------------------------------- | |
else if (input.action == 'deleteNote') { | |
note.deleteRecord(); | |
} | |
} | |
} | |
})(); | |
//================================================================================================================================= | |
// Angular provider | |
//================================================================================================================================= | |
// Type: Directive | |
// Name deleteButtonConfirm | |
//Client Script: | |
function(){ | |
return{ | |
template: '<div class="col-md-1"><button class="btn btn-danger pull-right" ng-click="c.confirmDelete()"><span class="glyphicon glyphicon-trash"></span></button></div>', | |
restrict: 'E' | |
}; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//================================================================================================================================= | |
// Notes List | |
//============================================================================================================================== | |
<div class="panel panel-default"> | |
<div class="panel-heading clearfix"> | |
<h3 class="panel-title pull-left"> | |
<!--${Notes}--> | |
{{::c.options.title}} | |
</h3> | |
<button class="btn btn-default pull-right" ng-click="c.newNote()"> | |
<span class="glyphicon glyphicon-plus"></span> | |
</button> | |
</div> | |
<div class="panel-body"> | |
<input placeholder="Filter" class="form-control" ng-model="noteFilter" /> | |
</div> | |
<div class="list-group"> | |
<a class="list-group-item" ng-click="c.selectItem($index)" ng-repeat="note in data.notes | filter: noteFilter"> | |
<h4 class="list-group-item-heading"> | |
{{note.title}} | |
</h4> | |
<p class="list-group-item-text"> | |
{{note.note}} | |
</p> | |
</a> | |
</div> | |
</div> | |
//================================================================================================================================= | |
// Clent script | |
//================================================================================================================================= | |
function($rootScope,$scope,spUtil) { | |
/* widget controller */ | |
/* | |
Client-side API | |
The client-side Widget API classes are: | |
spUtil: Contains utility methods to perform common functions in a Service Portal widget client script. Access the methods from this class using spUtil. For example, spUtil.addErrorMessage(). | |
addErrorMessage(): display an error message | |
addInfoMessage(): display an informational message | |
addTrivialMessage(): display a message which automatically disappears after a short period of time | |
format(): used to build strings from variables (alternative to concatenation) | |
get(): gets a widget model by ID or sys_id | |
recordWatch(): watches for updates to a table or filter and returns the value from a callback function | |
refresh(): calls the server and replaces the current options and data objects with the server response | |
update(): updates the data object on the server within a given scope | |
spModal: Methods provide an alternative way to show alerts, prompts, and confirmation dialogs. Access the methods from this class using spModal. For example, spModal.alert(). | |
alert(): displays an alert | |
confirm(): displays a confirmation message | |
open(): opens a modal using the specified options | |
prompt(): displays a prompt for user input | |
spAriaUtil: Uses an AngularJS service to show messages on a screen reader. Access the method from this class using spAriaUtil. For example, spAriaUtil.sendLiveMessage(). | |
sendLiveMessage(): announce a message to a screen reader | |
For the complete API documentation, including method arguments and return values, follow the links to the API classes. | |
Client-side Debugging | |
The client-side Widget API includes methods which can be used for logging/debugging: | |
spUtil.addErrorMessage() | |
spUtil.addInfoMessage() | |
spUtil.addTrivialMessage() | |
spModal.alert() | |
Using the Client-side Widget API | |
In an earlier part of this module, you learned there are three client-side Widget APIs: | |
spUtil: Contains utility methods to perform common functions in a Service Portal widget client script. | |
spModal: Methods provide an alternative way to show alerts, prompts, and confirmation dialogs. | |
spAriaUtil: Uses an AngularJS service to show messages on a screen reader. | |
In order to use classes from the client-side Widget API, the global object for the API must be passed as a dependency to the Client Script. The Client Script creates the AngularJS controller using the passed in dependencies. | |
If a Client Script attempts to use a client-side Widget API without passing the dependency, a ReferenceError occurs at runtime. | |
Using AngularJS Events with Widgets | |
AngularJS uses a publish and subscribe strategy for handling events. Events are useful for notifying widgets about important things happening in other widgets. For example: | |
Record selected | |
Changed value | |
Data deleted | |
Data added | |
And more.... | |
Work with events in AngularJS using these functions: | |
$emit(): Send an event up the scope hierarchy | |
$on(): Listen for events of a given type | |
DEVELOPER TIP: Avoid the use of $rootScope.$broadcast() because it can cause performance issues. | |
$rootScope.$emit("eventName",{})--event-->$rootScope---event-->$rootScope.$on("eventName", function(event, dada)){} | |
recordWatch() | |
In the CreateNotes widgets, you are able create, update, and delete Notes records. What happens in the widgets if a | |
user creates a record using the standard ServiceNow UI (UI16)? | |
Although the Notes List and Notes Body widgets have been developed to communicate with each other, | |
they are not notified of changes made to the Create Notes table records when the interaction happens | |
through the standard ServiceNow UI. | |
Use the Client API method spUtil.recordWatch() to register a listener in a widget. The listener is | |
notified when table records are inserted, deleted, or updated | |
spUtil.recordWatch($scope, "table_name", "filter", function(name) { | |
console.log(name); //Returns information about the event that has occurred | |
console.log(name.data); //Returns the data inserted or updated on the table | |
}); | |
} | |
spUtil.recordWatch($scope,"incident","active=true",function(name){ | |
// listen for changes to incident table records where the record is active i | |
console.log(name); //Returns information about the event that has occurred | |
console.log(name.data); //Returns the data inserted or updated on the table | |
}); | |
} | |
//GET QUERY FROM QUERY BUILDER | |
spUtil.recordWatch($scope,"incident","caller_idDYNAMIC90d1921e5f510100a9ad2572f2b477fe^active=true",function(name){ | |
console.log(name.data.operation); | |
console.log(name); | |
}); | |
*/ | |
var c = this; | |
// Use a position indicator to know which record to update | |
c.notePos = 0; | |
//spUtil.addInfoMessage("Welcome"); | |
//----------------------------------------------------------------------------- | |
// CREATE | create prepopulate with default values and display it to be updated | |
//----------------------------------------------------------------------------- | |
c.newNote = function() { | |
c.server.get({ | |
action: 'newNote' | |
}).then(function(r) { | |
//The unshift method is like the push method except that it shoves the item s onto the | |
//front of this array instead of at the end. It returns the array 's new length | |
c.data.notes.unshift(r.data.newNote); | |
c.data.noteID = r.data.noteID; //update scope | |
$rootScope.noteID = c.data.noteID;//update rootScope | |
$rootScope.myJson = {"sys_id" : c.data.noteID}; | |
//eventName, data = {} or a simple value | |
$rootScope.$emit('selectNote', c.data.noteID);//send new data to other widgets | |
$rootScope.$broadcast('selectNote', {"sys_id" : c.data.noteID}); | |
}); | |
} | |
c.selectItem = function(idx) {//powerful tool | |
// Set the position indicator to the index value passed in on select | |
c.notePos = idx; | |
var id = c.data.notes[idx].sys_id; | |
console.log('Note ID: ' + id); | |
$rootScope.noteID = id; //passing data | |
$rootScope.myJson = {"sys_id" : id}; //passing data | |
$rootScope.$emit('selectNote', id); | |
$rootScope.$broadcast('selectNote', {"sys_id" : id}); | |
} | |
//----------------------------------------------------------- | |
// RECORD UPDATED BY OTHER WIDGET | |
//----------------------------------------------------------- | |
// Subscribe to event. Set the new title and note values on the | |
// c.data object | |
$rootScope.$on('updateTitle', function(event,data) { | |
c.data.notes[c.notePos].title = data.title; | |
c.data.notes[c.notePos].note = data.note; | |
}); | |
//-------------------------------------------------------- | |
// RECORD DELETED IN THE BODY WIDGET | |
//-------------------------------------------------------- | |
$rootScope.$on('deleteNote', function(event,data) { | |
c.data.notes.splice($scope.notePos, 1); | |
}); | |
// Record removed from data object on the client and passed to server | |
c.snNoteUpdate = function(name,sysID){ | |
for (var i=0;i<c.data.notes.length;i++){ | |
if(c.data.notes[i].sys_id == name.data.sys_id){ | |
if(name.data.record.note){ | |
c.data.notes[i].note = name.data.record.note.display_value; | |
} | |
if(name.data.record.title){ | |
c.data.notes[i].title = name.data.record.title.display_value; | |
} | |
c.server.update(); | |
} | |
} | |
} | |
// Record removed from data object on the server and passed back to client | |
c.snNoteDelete = function(sysID){ | |
c.server.get({ | |
action: 'snDeleteNote', | |
noteID: sysID | |
}).then(function(r){ | |
c.data.notes = r.data.notes; | |
}); | |
} | |
//------------------------------------------------------------------- | |
// Use Record Watch | |
//------------------------------------------------------------------- | |
spUtil.recordWatch($scope, "x_snc_createnotes_note", "numberANYTHING", function(name) { | |
console.log("OPERATION : "+name.data.operation); | |
console.log("NAME : "+name); | |
// Respond to the Record Insert | |
// Fast and easy... replace the client data object with the server data object | |
if(name.data.operation == "insert"){ | |
c.server.refresh(); | |
} | |
// Respond to Record Update and Delete - Client Script | |
// Calls a Client Script function to do the update and pass the updated data object | |
// to the server. | |
if(name.data.operation == "update"){ | |
c.snNoteUpdate(name, name.data.sys_id); | |
} | |
// Calls a Client Script function which does the update on the server and passes | |
// the updated data object back to the client. | |
if(name.data.operation == "delete"){ | |
c.snNoteDelete(name.data.sys_id); | |
} | |
}); | |
} | |
//======================================================================================================================= | |
// | |
// | |
(function(input,data,$sp,gs,options) { | |
/* populate the 'data' object */ | |
/* e.g., data.table = $sp.getValue('table'); */ | |
/* | |
Server-side API | |
The server-side Widget API class is: | |
GlideSPScriptable: Methods for use in Service Portal widget Server Scripts. Access the GlideSPScriptable methods using the global $sp object. For example, $sp.canRead(). | |
canReadRecord(): returns true if the user can read the specified GlideRecord | |
getCatalogItem(): returns a model and view model for a sc_cat_item or sc_cat_item_guide | |
getDisplayValue: returns the display value of the specified field from either the widget's sp_instance or sp_portal record | |
getField(): returns information about the specified field in a GlideRecord | |
getFields(): checks the specified list of field names, and returns an array of valid field names | |
getFieldsObject(): checks the specified list of field names and returns an object of valid field names | |
getForm(): returns the form | |
getListColumns(): returns a list of the specified table's columns in the specified view | |
getMenuHREF(): returns the (?id=) portion of the URL based on the sp_menu type | |
getMenuItems(): returns an array of menu items for the specified instance | |
getParameter(): returns the value of the specified parameter | |
getPortalRecord(): returns the portal's GlideRecord | |
getRecord(): returns the current portal context | |
getRecordDisplayValues(): copies display values for the specified fields into the data parameter | |
getRecordElements(): for the specified fields, copies the element's name, display value, and value into the data parameter | |
getRecordValues(): copies values for the specified field names from the GlideRecord into the data parameter | |
getStream(): gets the activity stream for the specified record. This method works on tables which extend the Task table | |
getUserInitials(): returns the user's initials | |
getValue(): returns the value of the specified parameter | |
getValues(): copies values from the request or instance to the data parameter | |
getWidget(): gets a widget by id or sys_id, executes that widget's server script using the provided options, then returns the widget model | |
For the complete API documentation, including method arguments and return values, follow the links to the API classes. | |
Server-side Debugging | |
The server-side GlideSystem class includes methods which can be used for logging/debugging: | |
gs.log() - Global API | |
gs.logError() - Global API | |
gs.logWarning() - Global API | |
gs.warn() - Scoped API | |
gs.info() - Scoped API | |
gs.debug() - Scoped API | |
gs.error() - Scoped API | |
gs.addInfoMessage() - Scoped API and Global API | |
gs.addErrorMessage() - Scoped API and Global API | |
Widget Option Schema | |
Using widgets options makes widgets more easily reusable. The widget option schema defines | |
the user-configurable fields. To add, edit, or delete option fields, select the Edit | |
Options Schema menu item in the Widget Editor menu. | |
*/ | |
//create an array to populate without notes | |
data.notes = []; | |
var noteGR = new GlideRecord('x_snc_createnotes_note'); | |
noteGR.addQuery('user', gs.getUser().getID()); | |
noteGR.orderByDesc('sys_created_on'); | |
noteGR.setLimit(options.maximum_records_to_display); | |
noteGR.query(); | |
while (noteGR.next()) { | |
var noteObj = {}; | |
//use service portal helper method to get some display values | |
$sp.getRecordDisplayValues(noteObj, noteGR, 'number,title,sys_id'); | |
//get the first 20 characters of the description | |
noteObj.note = noteGR.getValue('note');//.slice(0,20); | |
//push the populated obj into the array | |
data.notes.push(noteObj); | |
} | |
if (input) { | |
if (input.action == 'newNote') { | |
var newNote = new GlideRecord('x_snc_createnotes_note'); | |
newNote.newRecord(); | |
var id = newNote.insert(); | |
data.noteID = id; | |
data.newNote = {}; | |
//emptyJson , gr ,fields-needed | |
$sp.getRecordValues(data.newNote,newNote,"title,note,sys_id"); | |
} | |
if (input.action == 'snDeleteNote') { | |
var delNote = new GlideRecord('x_snc_createnotes_note'); | |
// The notes record should already be gone. Just | |
// making sure it no longer exists. | |
if(delNote.get(input.noteID)){ | |
delNote.deleteRecord(); | |
} | |
} | |
} | |
})(input,data,$sp,gs,options); | |
//================================================================================================================================= | |
// | |
// | |
[{"hint":"Specify a title for the Notes List widget","name":"title","section":"other","default_value":"NOTES NAV BAR","label":"Title","type":"string"},{"hint":"Select the maximum number of records to display","name":"maximum_records_to_display","section":"other","default_value":"5","label":"Maximum records to display","type":"choice","choices":[{"label":"2","value":"2"},{"label":"3","value":"3"},{"label":"5","value":"5"},{"label":"7","value":"7"},{"label":"11","value":"11"},{"label":"13","value":"13"},{"label":"17","value":"17"}]}] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment