Service Portal: How to use template-url in widgets?

nthumma
Giga Guru

Hello,

Can someone help me with how to use template-url in service portal widgets? I am trying to build a form widget from scratch.

below is my code but its not rendering any form.

find_real_file.png 

find_real_file.png

from HTML, I can see sections are being rendered but form layout is empty.

Anybody have any thoughts or examples how to use template-url?

We are on Jakarta.

1 ACCEPTED SOLUTION

dylan_lindgren
ServiceNow Employee
ServiceNow Employee

Hi Nishanth,

 

Firstly, in your server script you can get a copy of the form model:

data.form = $sp.getForm('incident', '-1', null, 'sp');

Then, in the HTML code of your widget use the spModel directive to render the form:

<sp-model form-model="c.data.form" mandatory template-url="my-form-template"></sp-model>

You can see there's a template-url attribute. This points at a Angular ng-template that's defined in the related list of the widget. Within that template, you can use code similar to the below to ingest the form model being passed in through the form-model attribute. You can see it's pretty basic... looping through containers, then columns, then fields in the column and displaying them.

<div>
    <div ng-repeat="cap in ::containers">
        <div ng-if="::cap.captionDisplay">
            <h1>{{::cap.captionDisplay}}</h1>
        </div>
        <div class="container-fluid">
            <div ng-class="{'col-xs-6': cap.columns.length === 2,'col-xs-12': cap.columns.length === 1}" ng-repeat="col in ::cap.columns">
                <div ng-repeat="f in col.fields">
                    <sp-form-field ng-if="formModel._fields[f.name]" form-model="formModel" field="formModel._fields[f.name]" glide-form="getGlideForm()" ng-show="formModel._fields[f.name].isVisible()"></sp-form-field>
                </div>
            </div>
        </div>
    </div>
</div>

All you would have to do is alter this template so that instead of showing the "containers" one after the other, use JavaScript (or a JS library) to place them into tabs. This isn't something that's specific to ServiceNow, it's something that is just plain web development so there are resources that can be found online as to how to do this.

 

Lastly, in the client script of your widget you can capture the well-known GlideForm object that is emitted up from the spModel directive for your use within your widget.

var g_form

c.$onInit = function() {

    $scope.$on('spModel.gForm.initialized', function(e, gFormInstance) {

        g_form = gFormInstance;
    });

};

You would use the GlideForm object to do anything you would on a regular ServiceNow Client Script, such as submit, modify whether the fields are read only etc. You could create a submit button in your HTML:

<button ng-click="c.submit()">Submit my form!</button>

and then use GlideForm in your client script to submit the form:

c.submit = function() {
    g_form.submit('sysverb_update');
};

View solution in original post

6 REPLIES 6

nthumma
Giga Guru

server script

(function($sp) {
  /* populate the 'data' object */
  /* e.g., data.table = $sp.getValue('table'); */
	
	
//gs.addInfoMessage('Hello World!!');
data.table =  $sp.getParameter("t") || $sp.getParameter("table") || $sp.getParameter("sl_table");
data.sys_id =  $sp.getParameter("sys_id") || $sp.getParameter("sl_sys_id");
data.view = $sp.getParameter("v") || $sp.getParameter("view"); // no default
data.f = {};
data.f = $sp.getForm(data.table, data.sys_id, '', data.view);

})($sp);

dylan_lindgren
ServiceNow Employee
ServiceNow Employee

Hi Nishanth,

 

Firstly, in your server script you can get a copy of the form model:

data.form = $sp.getForm('incident', '-1', null, 'sp');

Then, in the HTML code of your widget use the spModel directive to render the form:

<sp-model form-model="c.data.form" mandatory template-url="my-form-template"></sp-model>

You can see there's a template-url attribute. This points at a Angular ng-template that's defined in the related list of the widget. Within that template, you can use code similar to the below to ingest the form model being passed in through the form-model attribute. You can see it's pretty basic... looping through containers, then columns, then fields in the column and displaying them.

<div>
    <div ng-repeat="cap in ::containers">
        <div ng-if="::cap.captionDisplay">
            <h1>{{::cap.captionDisplay}}</h1>
        </div>
        <div class="container-fluid">
            <div ng-class="{'col-xs-6': cap.columns.length === 2,'col-xs-12': cap.columns.length === 1}" ng-repeat="col in ::cap.columns">
                <div ng-repeat="f in col.fields">
                    <sp-form-field ng-if="formModel._fields[f.name]" form-model="formModel" field="formModel._fields[f.name]" glide-form="getGlideForm()" ng-show="formModel._fields[f.name].isVisible()"></sp-form-field>
                </div>
            </div>
        </div>
    </div>
</div>

All you would have to do is alter this template so that instead of showing the "containers" one after the other, use JavaScript (or a JS library) to place them into tabs. This isn't something that's specific to ServiceNow, it's something that is just plain web development so there are resources that can be found online as to how to do this.

 

Lastly, in the client script of your widget you can capture the well-known GlideForm object that is emitted up from the spModel directive for your use within your widget.

var g_form

c.$onInit = function() {

    $scope.$on('spModel.gForm.initialized', function(e, gFormInstance) {

        g_form = gFormInstance;
    });

};

You would use the GlideForm object to do anything you would on a regular ServiceNow Client Script, such as submit, modify whether the fields are read only etc. You could create a submit button in your HTML:

<button ng-click="c.submit()">Submit my form!</button>

and then use GlideForm in your client script to submit the form:

c.submit = function() {
    g_form.submit('sysverb_update');
};

Thanks for the informative response, I am little confused at sp-form-field form-model. should this form-model match my actual widget form-model (like data.f).

I have just cloned an OOB form widget and trying to ingest the ng-template from your example above.

<sp-model form_model="data.f" mandatory="mandatory" template-url='new-form-one'></sp-model>

 

I have added <p> tag for just testing and its printing the field names but some reason sp-form-field is not rendering.

 

 

<div>
    <div ng-repeat="cap in ::containers">
        <div ng-if="::cap.captionDisplay">
            <h1>{{::cap.captionDisplay}}</h1>
        </div>
        <div class="container-fluid">
            <div ng-class="{'col-xs-6': cap.columns.length === 2,'col-xs-12': cap.columns.length === 1}" ng-repeat="col in ::cap.columns">
                <div ng-repeat="f in col.fields">
                    <p>
                      {{f.name}}
                  </p>
                    <sp-form-field ng-if="formModel._fields.[f.name]" form-model="formModel" field="formModel._fields.[f.name]" glide-form="getGlideForm()" ng-show="formModel._fields.[f.name].isVisible()"></sp-form-field>
                </div>
            </div>
        </div>
    </div>
</div>

find_real_file.png

 

 

Just a followup to my previous comment, if I remove 'g-show="formModel._fields[f.name].isVisible()" I can see all the form fields rendering. Any thoughts on why isVisible is not working?

<div>
    <div ng-repeat="cap in ::containers">
        <div ng-if="::cap.captionDisplay">
            <h1>{{::cap.captionDisplay}}</h1>
        </div>
        <div class="container-fluid">
            <div ng-class="{'col-xs-6': cap.columns.length === 2,'col-xs-12': cap.columns.length === 1}" ng-repeat="col in ::cap.columns">       
                <div ng-repeat="f in col.fields">
                    <sp-form-field ng-if="formModel._fields[f.name]" form-model="formModel" field="formModel._fields[f.name]" glide-form="getGlideForm()"></sp-form-field>
                </div>
            </div>
        </div>
    </div>
</div>