Service Portal Widget: Client Side Script to add items from <select> to <table>

Mudit4
Kilo Expert

Hi Community,

I am very new to this. I am trying to create a widget. This is the basic structure.

find_real_file.png

Now,

When I click on Add Button, the hosts selected should be added to the table in a new row. I cant figure out the code. I neeeed help!

find_real_file.png

Also is there a way to populate the list of host options dynamically?

 

Adding Code Snippets here:

<style>
table, th, td {
  border:1px solid black;
}
</style>
<div class="panel panel-primary">
 <div class="panel-heading">Request OS Account</div>
 <div class="panel-body">
   <form>
     <label for="cname">Customer Name: </label>
     <input type="text" id="cname" name="justification"><br><br>
     <label for="servicetype">Service Type: </label>
     <input type="text" id="servicetype" name="servicetype"><br><br>
     <label for="envname">Environment name: </label>
     <input type="text" id="envname" name="envname"><br><br>
     <label for="hosts">List of Hosts </label>
     <select name="hosts" id="hosts" multiple>
  			<option value="host1">host1</option>
  			<option value="host2">host2</option>
  			<option value="host3">host3</option>
  			<option value="host4">host4</option>
		 </select>
     <button type="button" ng-click="c.addStuff()">Add</button><br><br>
     <table style="width:100%">
       <tr>
         <th>Name</th>
         <th>col2</th>
         <th>col3</th>
       </tr>
       <tr id="select">
         
       </tr>
     </table>
     <br><br>
     <input type="submit" value="Submit" ng-click="c.submitForm()">
   </form> 
  </div>
</div>

Client Side-

api.controller = function($http) {
    /* widget controller */
    var c = this;

    c.addStuff = function() {
    
    }
};
1 ACCEPTED SOLUTION

Here's how one could do it:

- controller:

api.controller = function ($scope, $http) {
	var c = this;

	c.data.host_arr = [
		{
			'id': 1,
			'value': 'host_1',
			'label': 'Host 1',
			'col2': '1 asdfasdfasdf',
			'col3': '1 asdfasdfasdf'
		},
		{
			'id': 2,
			'value': 'host_2',
			'label': 'Host 2',
			'col2': '2 asfdasdfasdf',
			'col3': '2 asdfasdfasdf'
		},
		{
			'id': 3,
			'value': 'host_3',
			'label': 'Host 3',
			'col2': '3 asdfasdf',
			'col3': '3 asdfasdfasdf'
		},
		{
			'id': 4,
			'value': 'host_4',
			'label': 'Host 4',
			'col2': '4 dfgsdf',
			'col3': '4 sdfgsdfg'
		}
	];

	$scope.row_arr = [];
	$scope.selectedHost_arr = [];

	c.addStuff = function addStuff () {
		return $scope.selectedHost_arr
			.filter(retainNotEmptyString)
			.map(convertIntoIndexIn(c.data.host_arr))
			.filter(retainValidIndex)
			.map(convertIntoHostFrom(c.data.host_arr))
			.map(insertAsRowInto($scope.row_arr));
	};

	function convertIntoIndexIn (host_arr) {
		return function convertIntoIndex (value) {
			return host_arr.findIndex(hasValue(value));
		};
	}

	function convertIntoHostFrom (host_arr) {
		return function convertIntoHost (index) {
			return host_arr[index];
		};
	}

	function hasValue (value) {
		return function has (host) {
			return host.value == value;
		};
	}

	function insertAsRowInto (row_arr) {
		return function insertAsRow (host) {
			row_arr.push({
				'id': row_arr.length,
				'name': host.label,
				'col2': host.col2,
				'col3': host.col3
			});
		};
	}

	function retainNotEmptyString (value) {
		return value != '';
	}

	function retainValidIndex (index) {
		return index != -1;
	}

};

- the Body HTML template:

<div class="panel panel-primary">
  <div class="panel-heading">Request OS Account</div>
  <div class="panel-body">
    <form>
      <label for="cname">Customer Name: </label>
      <input type="text" id="cname" name="justification"><br><br>
      <label for="servicetype">Service Type: </label>
      <input type="text" id="servicetype" name="servicetype"><br><br>
      <label for="envname">Environment name: </label>
      <input type="text" id="envname" name="envname"><br><br>
      <label for="hosts">List of Hosts </label>
      <select name="hosts" x-id="hosts" multiple ng-model="selectedHost_arr">
        <option ng-repeat="host in c.data.host_arr track by host.id" value="{{ host.value }}">{{ host.label }}</option>
      </select>
      <button type="button" ng-click="c.addStuff()">Add</button><br><br>
      <table style="width:100%">
        <tr>
          <th>Name</th>
          <th>col2</th>
          <th>col3</th>
        </tr>
        <tr ng-repeat="row in row_arr track by row.id">
          <td>{{ row.name }}</td>
          <td>{{ row.col2 }}</td>
          <td>{{ row.col3 }}</td>
        </tr>
      </table>
      <br><br>
      <button type="button" value="Submit" ng-click="c.submitForm()">Submit</button>
    </form> 
  </div>
</div>

Of course in a normal situation data.host_arr would be loaded in the Server script. The main things here are c.data.host_arr, $scope.row_arr and $scope.selectedHost_arr which hold the list of hosts to display, the list of rows created and the currently selected values in the select box.

The 1st one provides the list of hosts - this is newly introduced enhancement, the 2nd one is "generated"/filled when clicking the "Add" button and provides the data for the table rows and the last one is "the model" for values selected in the select box. Transferring the options to the select box is handled by AngularJS because of the ng-repeat directive on the sole "template" option element. Transferring the selected options from the select box to the scope property selectedHost_arr is handled by AngularJS because of the ng-model directive on the select element. Transferring the data from the row_arr scope property as table rows is again handled by AngularJs because of the ng-repeat directive applied to the sole tr element.

All in all you need to switch to an AngularJS (MVC) mindset and let the framework do the heave lifting everywhere possible - being that you are using it anyway.

And hope this all helps 🙂

View solution in original post

5 REPLIES 5

-O-
Kilo Patron
Kilo Patron

You should wire the view (HTML) to the mode using ng-model: make both the select synchronize itself with a property of the scope using ngModel directive. Make the table create its rows from another property of the scope using the ngRepeat directive. Once that is done, when someone clicks the button all you have to do is transfer data from the 1st property into the 2nd one.

But I have a question to you: let's assume someone would like to help you by modifying your code and adding the missing pieces. What should that person do: retype the whole thing (as opposed to copying it from your posting)? Wouldn't posting code in place of pictures be better?

Hi,

Thanks a lot for your quick response. I'm very new to Angular, I'll look into using ngModel as you mentioned.

And yeah posting code does make sense! My bad. I've added the snippets now.

Could you help me out with the client script?

 

Here's how one could do it:

- controller:

api.controller = function ($scope, $http) {
	var c = this;

	c.data.host_arr = [
		{
			'id': 1,
			'value': 'host_1',
			'label': 'Host 1',
			'col2': '1 asdfasdfasdf',
			'col3': '1 asdfasdfasdf'
		},
		{
			'id': 2,
			'value': 'host_2',
			'label': 'Host 2',
			'col2': '2 asfdasdfasdf',
			'col3': '2 asdfasdfasdf'
		},
		{
			'id': 3,
			'value': 'host_3',
			'label': 'Host 3',
			'col2': '3 asdfasdf',
			'col3': '3 asdfasdfasdf'
		},
		{
			'id': 4,
			'value': 'host_4',
			'label': 'Host 4',
			'col2': '4 dfgsdf',
			'col3': '4 sdfgsdfg'
		}
	];

	$scope.row_arr = [];
	$scope.selectedHost_arr = [];

	c.addStuff = function addStuff () {
		return $scope.selectedHost_arr
			.filter(retainNotEmptyString)
			.map(convertIntoIndexIn(c.data.host_arr))
			.filter(retainValidIndex)
			.map(convertIntoHostFrom(c.data.host_arr))
			.map(insertAsRowInto($scope.row_arr));
	};

	function convertIntoIndexIn (host_arr) {
		return function convertIntoIndex (value) {
			return host_arr.findIndex(hasValue(value));
		};
	}

	function convertIntoHostFrom (host_arr) {
		return function convertIntoHost (index) {
			return host_arr[index];
		};
	}

	function hasValue (value) {
		return function has (host) {
			return host.value == value;
		};
	}

	function insertAsRowInto (row_arr) {
		return function insertAsRow (host) {
			row_arr.push({
				'id': row_arr.length,
				'name': host.label,
				'col2': host.col2,
				'col3': host.col3
			});
		};
	}

	function retainNotEmptyString (value) {
		return value != '';
	}

	function retainValidIndex (index) {
		return index != -1;
	}

};

- the Body HTML template:

<div class="panel panel-primary">
  <div class="panel-heading">Request OS Account</div>
  <div class="panel-body">
    <form>
      <label for="cname">Customer Name: </label>
      <input type="text" id="cname" name="justification"><br><br>
      <label for="servicetype">Service Type: </label>
      <input type="text" id="servicetype" name="servicetype"><br><br>
      <label for="envname">Environment name: </label>
      <input type="text" id="envname" name="envname"><br><br>
      <label for="hosts">List of Hosts </label>
      <select name="hosts" x-id="hosts" multiple ng-model="selectedHost_arr">
        <option ng-repeat="host in c.data.host_arr track by host.id" value="{{ host.value }}">{{ host.label }}</option>
      </select>
      <button type="button" ng-click="c.addStuff()">Add</button><br><br>
      <table style="width:100%">
        <tr>
          <th>Name</th>
          <th>col2</th>
          <th>col3</th>
        </tr>
        <tr ng-repeat="row in row_arr track by row.id">
          <td>{{ row.name }}</td>
          <td>{{ row.col2 }}</td>
          <td>{{ row.col3 }}</td>
        </tr>
      </table>
      <br><br>
      <button type="button" value="Submit" ng-click="c.submitForm()">Submit</button>
    </form> 
  </div>
</div>

Of course in a normal situation data.host_arr would be loaded in the Server script. The main things here are c.data.host_arr, $scope.row_arr and $scope.selectedHost_arr which hold the list of hosts to display, the list of rows created and the currently selected values in the select box.

The 1st one provides the list of hosts - this is newly introduced enhancement, the 2nd one is "generated"/filled when clicking the "Add" button and provides the data for the table rows and the last one is "the model" for values selected in the select box. Transferring the options to the select box is handled by AngularJS because of the ng-repeat directive on the sole "template" option element. Transferring the selected options from the select box to the scope property selectedHost_arr is handled by AngularJS because of the ng-model directive on the select element. Transferring the data from the row_arr scope property as table rows is again handled by AngularJs because of the ng-repeat directive applied to the sole tr element.

All in all you need to switch to an AngularJS (MVC) mindset and let the framework do the heave lifting everywhere possible - being that you are using it anyway.

And hope this all helps 🙂

Hi János,

This is working and your code makes a lot of sense to me even when im not that familiar with Angular!

I understand that data.host_arr ideally would populate from server script. However for my use case, Im planning to populate it from an API call on load of the widget.

Again,

Thanks a lot this was very helpful!