The CreatorCon Call for Content is officially open! Get started here.

Karen Lazarenko
ServiceNow Employee
ServiceNow Employee

Please post comments and questions via the Comment link below.

Comments
thirschi
Tera Expert

The issue pops up as soon as the widget is loaded... The widget is loaded in an embedded widget...

 

Could you attach your widget to the post?

proco
Tera Contributor

I posted this in the general conference community, but maybe it's better in a smaller group ... anyone here a runner? I went out this morning around 5:30 and realized it would be nice to have some company while navigating the - interesting - humanity on the strip at that hour.

I did 4 this morning at around a 9 min/mile pace. Thought I'd like to stretch out to around 6 if possible.

conanlloyd
Giga Guru

Would love to but I'm an old/slow runner getting back into it and am a 12-13 min mile runner that's still doing 3-5 min run/1-2 min walk intervals.  I'll stay with the treadmill but enjoy and run safe.

OliverDoesereck
Tera Guru

Sure, its only a simple tester widget

charliesdev
Giga Expert

in course "service portal advanced", p90 mentions using a custom $scope, like:

  spUtil.refresh($scope); // same as c.server.refresh() but passes a custom $scope  

when i find out what that is / what it's good for, i will try to update this.

caroleanderson
ServiceNow Employee
ServiceNow Employee

Here is an article from W3Schools which may help explain $scope: https://www.w3schools.com/angular/angular_scopes.asp

OliverDoesereck
Tera Guru

I cant even get it to work when replacing c.server.update with spUtil.update.

Dont really see the point in sending the scope with the function. Then again, I actually enjoy using c.server.get()

caroleanderson
ServiceNow Employee
ServiceNow Employee

Another article which may help understand AngulrJS $scope can be found at: https://www.tutorialspoint.com/angularjs/angularjs_scopes.htm

ben_hollifield
Tera Guru

Theoretically, it would enable you to get hold of a scope from another widget, for example, and pass that scope to the server. I personally have not yet found a use-case for this, but maybe someone else will have an example. 🙂

goulart
Tera Contributor

Lab 2.4 -- In server side we used input variable. Where is it defined ? Is "input" a default angular variable ? 

jessicah
Tera Contributor

The input object is a special Angular object.  See page 96 in the book for more details

c.data.myVar in the client controller is accessed in server side as input.myVar

goulart
Tera Contributor

Yay

ben_hollifield
Tera Guru

'input', 'options', and 'data' aren't Angular-specific constructs, but they are native to Service Portal. They will always be available in server & client script on widgets without any extra effort on your part.

Nate Anderson
Tera Contributor

Also if you run into errors where "input is undefined"; be sure to wrap your input work in an "if(input) { }" block.... For the server side script*

jessicah
Tera Contributor

Oh, I didn't know that! Thanks Ben!

Karen Lazarenko
ServiceNow Employee
ServiceNow Employee

Hey Room C, Martin put together some example scripts on filtering, orderBy and how to use both at the same time.  Create a Test Widget and copy-paste this code into the HTML Template and Client Script editors to see it in action.

 

HTML Template
<div>
<!-- your widget template -->
<input name="q" type="text" placeholder="Search" ng-model="c.first" autocomplete="off" uib-typeahead="item as item.primary for item in c.getResults($viewValue)" typeahead-wait-ms="250" typeahead-focus-first="false" typeahead-on-select="c.onSelect($item, $model, $label)" typeahead-template-url="sp-typeahead.html" typeahead-popup-template-url="sp-typeahead-popup.html" class="form-control input-typeahead ng-pristine ng-untouched ng-valid ng-empty" title="Search" role="textbox" aria-label="Search" tabindex="0" aria-haspopup="true" aria-autocomplete="list" aria-owns="typeahead-75-1695" data-aria-expanded="false" aria-invalid="false">
<select ng-model="c.orderBy" class="form-control">
<option value="first">First</option>
<option value="last">Last</option>
</select>
<ul class="list-group">
<li ng-repeat="user in c.data.users| filter:c.first | orderBy:c.orderBy" class="list-group-item">{{user.first}} - {{user.last}}</li>
</ul>
</div>

 

Client Script

function($scope) {
/* widget controller */
var c = this;

c.data.users = [];
c.data.users.push({'first':'Bob','last':'Smith'})
c.data.users.push({'first':'Jane','last':'Doe'})
c.data.users.push({'first':'Fred','last':'Luddy'})

$scope.search = function(item) {
// Check the filter value against number and description
if (!c.filter || item.first.toUpperCase() == c.filter.toUpperCase() ){
return true;
}
return false;
};
}

 

Thanks Martin!!

 

Steve Kelly
Mega Sage

On page 105 shouldn't the registrationExists function also query the event that the user is trying to register for? Otherwise they can only register for one event and not others. The current query looks for the email address being used for ANY event.

goulart
Tera Contributor

When you create a new Widget, what is suppose to happen if you select the option Create Test Page ? It is related with automated tests ?

 

OliverDoesereck
Tera Guru

It creates a brand new page, adds a container, a column and your new widget. Its not for automated testing, but rather for development purposes or if you want to add the widget to a new page anyway

caroleanderson
ServiceNow Employee
ServiceNow Employee

@karen - please take a look at this. Thanks!

ben_hollifield
Tera Guru

Nate speaks the truth! 'input' only appears once you've performed a server.update() or the like. It does not exist on instantiation of a widget, only 'data' and 'options' exist at that point.

conanlloyd
Giga Guru

Nice catch!

cburkins
Tera Contributor

I believe it's a bit of "magic" that ServiceNow is applying behind the scenes.    The "data" object in the client script becomes the "input" object in the server script.   (someone fact-check me if I'm wrong)

ben_hollifield
Tera Guru

You got it right. Upon server.update(), SN passes the 'c.data' object from the client script up to the server script as 'input'. The server script can then do it's thing with the 'input' object, refresh the 'data' object, and then pass 'data' back to the client script.

cburkins
Tera Contributor

Seems like Page 114 (one-time binding of AngularJS expressions) doesn't properly demo the "::" feature of AngularJS. 

felladin
Tera Guru

If the table doesn't honour the cloud-widgets background colour, check which portal you are opening the page on. If it's not cloudevents you will have a bad time.

goulart
Tera Contributor

thks again

goulart
Tera Contributor

What exactly is $sp ?

ben_hollifield
Tera Guru

If you are running behind and need some catch-up code, visit http://sp.ht4.org/. Solution code will be posted there shortly after you should have already completed. 🙂

OliverDoesereck
Tera Guru

Check this out:

https://docs.servicenow.com/bundle/kingston-application-development/page/app-store/dev_portal/API_reference/GlideSPScriptableScoped/concept/c_GlideSPScriptableScopedAPI.html

felladin
Tera Guru

Here it was described as the Service Portal version of gs

ben_hollifield
Tera Guru

Yep, basically a server-side API, akin to a Script Include.

OliverDoesereck
Tera Guru

Lab  3.1:
Will c.updatedRegs be used in the future Labs? They currently have no function.

I have used them to highlight the changed record:
In the "Table Summary" Widget, HTML:

Replace:

<tr ng-repeat="record in c.data.records | orderBy : c.orderCol : c.sortOrder">

With:

<tr ng-repeat="record in c.data.records | orderBy : c.orderCol : c.sortOrder"
ng-class="{'highlighted':c.updatedRegs[record.sys_id]}">

And add this in the CSS:
.highlighted {
background:#dff0d8;
}

And might as well change the $timeout time from 6000 to 500 for a "flash"

ben_hollifield
Tera Guru

We just did something similar up here at the front desk. 🙂

It appears that the client code was written for just such functionality, but it was forgotten in the HTML. Nice job, and a great 'extra credit' task for anyone else in the class!

Dan Conroy
ServiceNow Employee
ServiceNow Employee

find_real_file.png

Dan Conroy
ServiceNow Employee
ServiceNow Employee

Did you also need to change "changeObj" in the client script to "updateObj"?

find_real_file.png

Dan Conroy
ServiceNow Employee
ServiceNow Employee

In module 3 we talk about the syntax of using scripted default options. A good example of an OOTB widget which does this is widget-ticket-conversation

find_real_file.png

 

find_real_file.png

jessicah
Tera Contributor

Interesting.  on line 4-5 where its using this, should it have the $sp.getParameter as the second option to override the widget options instead of having the options as second?  Since that can be supplied by a redirect URL...

e.g.

data.sys_id = input.sys_id || $sp.getParameter("sys_id") || options.sys_id;

?

Thais Pulliam2
Giga Expert

TYPO FOUND IN LAB 3.1

Hi Guys, I found a typo in the Lab 3.1, more specifically, on page 134.

In the code for the record watch, it asks us to use the below:

spUtil.recordWatch($scope, c.data.table, '', function(updateObj){
  //some code here(...), then:

  /*add sys_ids of any changed records to an array
  this will allow us to highlight them in the HTML template*/
  c.updateRegs[changeObj.data.sys_id] = true;
  $timeout(function(){
    c.updatedRegs[changeObj.data.sys_id] = false;
  }, 6000);
});

But if you pay close attention, changedObj is not defined anywhere and it won't result in anything. The record watch function returns the data in the param, so we should use that one, (updatedObj) instead. Here is the corrected code to be used:

spUtil.recordWatch($scope, c.data.table, '', function(updateObj){
  //some code here(...), then:

  /*add sys_ids of any changed records to an array
  this will allow us to highlight them in the HTML template*/
  c.updateRegs[updateObj.data.sys_id] = true;
  $timeout(function(){
    c.updatedRegs[updateObj.data.sys_id] = false;
  }, 6000);
});

This won't affect completion of the lab since record watch is still running and enabling the lightning bulb. But it will prevent the updateRegs object to actually store any data in it. (Not sure if this will be needed for any of tomorrow's labs). 

Hope this help! 🙂

goulart
Tera Contributor

Lab 3.2 - Display fields 

The columns are not respecting the order of display fields. Any idea how to fix it ?

Last name Company Updated Event First name Email
Luddy Cloud Dimensions 2017-11-20 08:30:59 Infinity Advanced Development Fred fred.luddy@cloudd.com
Anglin Cloud Dimensions 2017-11-20 08:31:58 Infinity Advanced Development Beth beth.anglin@cloudd.com
Bennet Cloud Dimensions 2017-11-20 08:31:47 Infinity Advanced Development Jerrod jerrod.bennet@cloudd.com
Goodliffe Cloud Dimensions 2017-11-20 08:32:04 Infinity Advanced Development Don don.goodliffe@cloudd.com
FIRIFAFIRIFA afaf 2018-05-06 16:33:09 Infinity Advanced Development IT WILL WORK4 fegasdasda@asdas.com
ben_hollifield
Tera Guru

This has to do with how the data is fed into the ng-repeat. It's being fed as an unordered JSON object - therefore, the column order is semi-random. It could be done in a more orderly fashion by using an array instead. In the real world, this would be the better way to go about it. Migrating to this could be a nice 'extra credit' exercise, if you want to try it out.

Dan Conroy
ServiceNow Employee
ServiceNow Employee

Hey Thais! To improve this even more, you can add an ng-class to the table row, so that the row is highlighted when it is updated. The code doesn't actually do anything otherwise! See the screenshot below for an example. The "highlight" class is defined in another lab to give the row a different background colour. 

Dan Conroy
ServiceNow Employee
ServiceNow Employee

Good morning all - if anyone left something in the room yesterday, it has most likely been moved to lost and found. This is on the same level as breakfast and registrations.

OliverDoesereck
Tera Guru

This bothered me yesterday too. As Ben said, the object that is returned by $sp.getFieldsObject isnt an array unfortunately.

You "only" need to convert the Object into an Array.

Here is my Version...
Server Script:
Old:

data.labels=$sp.getFieldsObject(gr,input.fields);

New:

data.labels = [];
var labels = $sp.getFieldsObject(gr, input.fields);

for (var label in labels) {
data.labels.push({"key":label,
"value":labels[label].label});
}



HTML:
Here you have to replace the (key, val) with a variable.
And then call the values directly.
Here is my HTML under Step 5:

 

<table class="table cloud-widgets"
ng-if="c.data.records">
<thead>
<tr>
<th ng-repeat="l in c.data.labels"
ng-click="c.changeSortCol(l.key)">{{l.value}}
<span ng-show="c.orderCol==l.key">
<span class="fa fa-sort-asc"
ng-show="!c.sortOrder"></span>
<span class="fa fa-sort-desc"
ng-show="c.sortOrder"></span>
</span>
</th>

</tr>
</thead>

<tbody>
<tr ng-repeat="record in c.data.records | orderBy : c.orderCol : c.sortOrder"
ng-class="{'highlight':c.updatedRegs[record.sys_id]}">
<td ng-repeat="l in c.data.labels">{{record[l.key]}}</td>
</tr>
</tbody>
</table>



ben_hollifield
Tera Guru

Thanks for posting this, Oliver! We had a number of folks ask about it yesterday - this solution will be helpful.

OliverDoesereck
Tera Guru

Thanks Ben 🙂

On a small side note: There is no real point in adding "event" in the display fields as the data is being aggregated by it. But the display fields are a matter of taste

goulart
Tera Contributor

Good Morning ! In lab 3.3 - item C 6 Is there a reason not to use if (!input) { }  ? 

OliverDoesereck
Tera Guru

You can gladly use the if (!input) {}, but in this case, we know the widget will never have a c.server.update/refresh/get call and so input would never be filled. If you know that your widget will be having server calls from the client side, then its surely suggested to have input and !input blocks

Dan Conroy
ServiceNow Employee
ServiceNow Employee

Good morning Room2 - please send any music requests through and our DJ might be able to accommodate!

Dan Conroy
ServiceNow Employee
ServiceNow Employee

Otherwise I am going to start playing some weird Australian psychedelic rock music.

Version history
Last update:
‎04-12-2018 10:44 AM
Updated by: