Assistance with AngularJS for Checkbox input in Service Portal Custom Widget

User303823
Tera Contributor

Experts - 

 

I'm very new to AngularJS and am essentially learning on the fly for a current project. I have been able to follow the basic flow from HTML to Client to Server in my study, but in my application of this to a check box input element... my understanding breaks down. 

 

Here are some of my requirements for a custom ServicePortal Widget:

  • The server defines a data object variable, queries and records two values from the returned records in that object; the object has a 'content' field and a 'sysid' field
  • The HTML would then display checkboxes based on the query result and allow the user to select one or more (the 'content' field used as the label and the 'sysid' field the value)
  • The client script would then receive the value(s) of the selected checkboxes and send these back to the server to update a new query (essentially running a filter)

My ultimate goal is to capture a string of sysids from the selected checkboxes and update a server query so that I can filter the content displayed in the HTML (not included here for simplicity). 

 

I've been successful in creating the checkboxes but clicking and working with the values... much less success. 

 

I feel like the way the AngularJS (perhaps ng-model) interacts with the onChange() function defined is just wrong. Also I may have some of the functions in the wrong place. Maybe something else. Some simplified code exacerpts follow to help illustrate.

 

Server

 

 

(function() {
	var localInput = input; //Captures 'input' from client
	
	data.type = []; //Object to store record data

	data.testing = 'Response to appear here.'; //Following two vars for testing only
	var testing = '';
	
	var typeGR = new GlideRecord('custom_table_here'); //The query
	typeGR.addQuery('u_class_type', 100);
	typeGR.addQuery('active', true);
	typeGR.query();
	
	while (typeGR.next()){ //Capture a content 'type' and 'sysid' of the record
		data.type.push({
		  "content": typeGR.u_class_title.toString(),
		  "sysid": typeGR.sys_id.toString()});
	}
//If the 'action' from the client-sent object is not empty,  print out the string of sysids and populate the data object
	if (localInput.view != '') { 
		testing = localInput.view;
		data.testing = testing;
	}
	
})();

 

 

 

HTML

 

 

<!--Below is extracted from additonal html -->
<div class="fit-content">
  <div>
      <p><strong>Content Type:</strong></p>
       <form>
       <div ng-repeat="t in data.type">
         <label>
           <input type="checkbox" ng-model="c.content.selected" ng-true-value={{t.sysid}} ng-false-value="all" ng-change="c.changeView()">
             {{t.content}}
           </label>
       </div>
       </form>
  </div>
</div>    

<div class="col-md-8 col-xs-12 fit-content">
  <div class="input-group" style="width:100%">
    <input ng-model="c.filterText" ng-keypress="c.checkEnter($event)"class="form-control" style="width:100%" placeholder="Search the library..." aria-label="Search library...">
    <span class="input-group-btn">
      <button class="btn btn-default align-icon" type="button" ng-click="c.search()" data-original-title="Search the Library" aria-label="Search the Library" data-toggle="tooltip" data-placement="bottom">
        <i class="fa fa-search"></i>
      </button>
    </span>
  </div>
</div>

<!-- This search result section not yet implemented, placeholding -->
  <div ng-if="c.data.library.length == 0 && !c.filterText" class="panel-body panels-container">
    ${Nothing found in the library} 
  </div>
  <div ng-if="c.data.library.length == 0 && c.filterText" class="panel-body panels-container">
    ${Nothing found in the library} 
   </div>
   <strong>Testing output:</strong> {{c.data.testing}}
   <br />
   <i>Will ultimately hold additional details from records returned by server query.</i>

 

 

 

Client

This is where I'm really lacking. I'm fairly certain the use of ng-model and content of the changeView() function are incorrect. I do not seem to be getting the value(s) from the check boxes. 

function() {
var c = this;

c.selectedItems = '';
c.viewFilter = '';
	
c.changeView = function() { //onChange function
c.selectedItems += c.content.selected + ','; //Build string of sysids - need loop?
c.viewFilter = selectedItems; //Set final string to 'filter' var for use below
	
	
c.server.get({ //communicate with server - not sure if in right place in code
   action: "change_view", //set here to influence server logic
   view: c.viewFilter, //the string of sysids
   search_text: c.filterText //from keyword search input
 }).then(function(response){
    c.data = response.data; //Set the client data  = to server response data to update html
  });
}
}

 

Wondering if this is enough background for anyone to point me to a resource which can help my understanding or recommend an approach. I've done a great deal of reading and studying on these forums and on Angular itself. I'm just missing something fundamental. Happy to include more details as necessary.

3 REPLIES 3

Community Alums
Not applicable

Hi @User303823 ,

 

Can you please try this updated code-

HTML-

<!--Below is extracted from additional HTML -->
<div class="fit-content">
  <div>
      <p><strong>Content Type:</strong></p>
       <form>
       <div ng-repeat="t in c.data.type">
         <label>
           <input type="checkbox" ng-model="t.selected" ng-change="c.changeView(t)">
             {{t.content}}
           </label>
       </div>
       </form>
  </div>
</div>    

<div class="col-md-8 col-xs-12 fit-content">
  <div class="input-group" style="width:100%">
    <input ng-model="c.filterText" ng-keypress="c.checkEnter($event)" class="form-control" style="width:100%" placeholder="Search the library..." aria-label="Search library...">
    <span class="input-group-btn">
      <button class="btn btn-default align-icon" type="button" ng-click="c.search()" data-original-title="Search the Library" aria-label="Search the Library" data-toggle="tooltip" data-placement="bottom">
        <i class="fa fa-search"></i>
      </button>
    </span>
  </div>
</div>

<div ng-if="c.data.library.length == 0 && !c.filterText" class="panel-body panels-container">
  ${Nothing found in the library} 
</div>
<div ng-if="c.data.library.length == 0 && c.filterText" class="panel-body panels-container">
  ${Nothing found in the library} 
</div>
<strong>Testing output:</strong> {{c.data.testing}}
<br />
<i>Will ultimately hold additional details from records returned by server query.</i>

 

Client Script-

(function() {
    var c = this;
    
    c.selectedItems = [];
    
    c.changeView = function(item) {
        if (item.selected) {
            c.selectedItems.push(item.sysid);
        } else {
            var index = c.selectedItems.indexOf(item.sysid);
            if (index > -1) {
                c.selectedItems.splice(index, 1);
            }
        }
        
        // Send the updated list of selected items to the server
        c.server.get({
            action: "change_view", 
            view: c.selectedItems.join(','), // The string of sysids
            search_text: c.filterText // from keyword search input
        }).then(function(response) {
            c.data = response.data; // Update the client data with the server response
        });
    }
})();

 

If my response has resolved your query, please consider giving it a thumbs up ‌‌ and marking it as the correct answer‌‌!


Thanks & Regards,

Sanjay Kumar

User303823
Tera Contributor

Thank you Sanjay. 

 

I'm mostly following and still working through the code to make sure I understand the changes and suggestions. What I am noticing right away is that the check boxes are almost working as a 'button'. The checkbox doesn't remain 'checked' and a click will add the sys_id to the string and will repeatedly add it. Deselecting the check box should remove the sys_id but that isn't possible currently. 

 

Appreciate the quick response. I'm much closer with your help! Just need to work out why the onChange (i.e. click) of the box isn't working as intended. The attached screenshot shows the string of sys_ids is being created and duplicate ids for each 'click' instead of removing.

More research this morning and I'm wondering if there's still not something incorrect in the Client script portion. Here are my findings:

  • Copying in Sanjay's client script (no edits) *does not* render any of the checkbox input elements for the page. 

 

(function() {

//All script here : checkboxes do not render in the HTML

})();​

 

  • Editing how the function is defined in one of the following ways *will* render the checkbox elements on the page. 

 

api.controller = function($scope) {

//All script here

};​

OR

function() {

//All script here

};​

 

However, things are only partially working with the edited client script. I can click a checkbox, however it is instantly/automatically 'unchecked'. While it does capture the sysid as stated before, becase the state is not held, I cannot deselect it to remove it from the list. 

 

There are no console errors or warnings. Still digging into it.

 

Appreciate any other suggestions.