Slush bucket widget - save button

walshy
Giga Expert

I am using the slush bucket widget on a portal page so I can use a m2m table to link multiple songs to a gig. I have the songs in in the 'available' bucket and the widget works for me, but I am struggling to work out how to make the 'save' work like it would in the CMS through the edit option. 

I am trying to decide if I should create a save button on the widget or if I should have the save button on the record form do the save once the songs are added to the gig. 

find_real_file.png

If I do the slush widget (which I have been attempting to do now) I have attempted to take some of the code from the record widget save button but I am struggling with that. Which is what made me think about using that button to save the details in the slush widget. 

Any suggestions on the best way to tackle this? 

I looked at the list collector option as a variable on the form, but I couldn't work out how to make the songs save in the related list as I needed. 

Below is the widget code I have so far

html

<div>
	<h4>Select songs for this gig</h4>
  <button type="button" class="btn btn-primary action-btn">Save songs</button>
  <button ng-if="getPrimaryAction()" type="submit" ng-click="triggerUIAction(getPrimaryAction())" ng-disabled="submitting" class="btn btn-primary action-btn pull-right ng-scope" gsft_id="{{::action.sys_id}}">Save selections<!-- ngIf: saveButtonSuffix --><span ng-if="saveButtonSuffix" class="ng-binding ng-scope">(Ctrl + s)</span><!-- end ngIf: saveButtonSuffix --></button>
    
  <div class="row">

    <div class="col-sm-6">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h4 class="panel-title">${Select from below}</h4>
        </div>
        <div class="available list-group slush-scrollable" 
             as-sortable="c.availableAs" 
             ng-model="c.data.all">
          <div ng-repeat="item in c.data.all | filter:c.search" 
               as-sortable-item 
               ng-dblclick="c.onDblClick(item)"
               ng-class="{used: item.used}"
               class="list-group-item">
            <div as-sortable-item-handle>
              <span>{{item.u_song_title.display_value}}</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="col-sm-6">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h4 class="panel-title">${In gig}</h4>
        </div>
        <div class="list-group slush-scrollable" as-sortable="c.selectedAs" ng-model="c.selected">
          <div ng-repeat="item in c.selected" 
               as-sortable-item
               ng-dblclick="c.onSelDblClick(item)"
               class="list-group-item">
            <div as-sortable-item-handle>
              <span>{{item.u_song_title.display_value}}</span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>  
  <div>
  </div>
</div>

CSS

.as-sortable-placeholder {
  background-color: #eee;
}

.slush-scrollable {
  height: 240px;
  overflow-y: scroll;
  overflow-x:hidden;
}

.panel  > .list-group:last-child .list-group-item:last-child {
	border-bottom-width: 1px;
  border-bottom-style: solid;
  border-radius: 0;
}

.used {
	max-height: 0;
  overflow: hidden;
  padding: 0;
  border: none;
}

.available > .list-group-item {
	transition: all 1s;
}

.btn-form-menu {
	vertical-align: inherit;
  padding: inherit;
  background: none;
}

Client

function($rootScope, $scope) {
	var c = this;
	c.selected = [];

	// ng-sortable clone means cannot reorder
	c.availableAs = {
		itemMoved: function (event) {
			event.source.itemScope.item.used = true;
		},
		clone: true // clone this side
	}

	c.selectedAs = {
		itemMoved: function (event) {
			// moved back to available
			var item = event.source.itemScope.item;
			moveToAvailable(item);
			removeItem(c.data.all, item);
		},
		dragStart: function () {
			c.availableAs.clone = false;
		},
		dragEnd: function () {
			c.availableAs.clone = true;
		}
	}

	// double moves from Available to Selected
	c.onDblClick = function(item) {
		var t = angular.copy(item);
		item.used = true; // original is now used
		c.selected.push(t);
	}

	// double on selected removes and unsets Available used
	c.onSelDblClick = function(item) {
		moveToAvailable(item);
		removeItem(c.selected, item);
	}

	function removeItem(array, item) {
		var n = array.indexOf(item);
		if (n !== -1)
			array.splice(n, 1);		
	}
	
	function moveToAvailable(item) {
		var t = item.sys_id.value;
		angular.forEach(c.data.all, function(target) {
			if (target.sys_id.value == t)
				target.used = false;
		})		
	}
	
	function _save() {
		var primaryAction = $scope.getPrimaryAction();
    if (primaryAction)
      $scope.triggerUIAction(primaryAction);
	}
	
	$scope.triggerUIAction = function(action) {
		if ($scope.data.disableUIActions && !action.primary) {
      return;
    }
}

}

server

(function () {
	// grab the filtered songs
	data.all = [];
	var filterQuery = "u_perform_statusIN1,2,3^u_song_status=2";
	var gr = new GlideRecord('x_120100_band_songs');
				
	gr.orderBy('u_song_title');
  
	gr.addEncodedQuery(filterQuery);
	gr.query();
  while (gr.next()) {
    var t = $sp.getRecordElements(gr, 'u_song_title,sys_id');
		data.all.push(t);
	}
})();

 

1 ACCEPTED SOLUTION

Hi Walshy,

In your server script for the input:
     addSong.songs = input.songs[indx]
should be 
     addSong.songs = input.songs[indx].sys_id.value
according to your screenshot of the Object and if the "songs" field is a reference field on the m2m table.

Also as a side note to clear things up a little in your comments of your Client Script in the server.get() method. (Hopefully you don't mind)
find_real_file.png

 

 

 

View solution in original post

11 REPLIES 11

Gosia Urbanska
Giga Guru

If you want to use your "Save songs" button:

1. add to 

<button type="button" class="btn btn-primary action-btn">Save songs</button>

ng-click function and you really  do not need the next button; So for example ng-click = c.saveMySongs()

2. in controller add code for your function

c.saveMySongs = function(){

//pass your data to server in order to save

c.server.get({

action:'saveSongs',

songs: c.selected,

gig_sysID://add here your gig sys_id

//pass whatever you need here

}).then(function(response){

// add some actions 

});

}

3. on server script add GlideRecord to save records 

 

https://docs.servicenow.com/bundle/jakarta-servicenow-platform/page/build/service-portal/concept/wid... 

 

Hey Malgorzata

Thanks for that I will dive in and see how I go, makes sense, lets just see if I have the capacity. 

Cheers!

 

Cam.

I've gone through this and I am failing, I figured I needed to save both the gig and the song to the m2m table to create the relationship. 

Below is what I have tried to do. 

HTML 

<div>
	<h4>Add songs here <button type="button" class="btn btn-primary action-btn pull-right" ng-click="c.saveMySongs()">Save songs</button></h4>
    <div class="row">
    <div class="col-sm-6">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h4 class="panel-title">${Select from below}</h4>
        </div>
        <div class="available list-group slush-scrollable" 
             as-sortable="c.availableAs" 
             ng-model="c.data.all">
          <div ng-repeat="item in c.data.all | filter:c.search" 
               as-sortable-item 
               ng-dblclick="c.onDblClick(item)"
               ng-class="{used: item.used}"
               class="list-group-item">
            <div as-sortable-item-handle>
              <span>{{item.u_song_title.display_value}}</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="col-sm-6">
      <div class="panel panel-default">
        <div class="panel-heading">
          <h4 class="panel-title">${In gig}</h4>
        </div>
        <div class="list-group slush-scrollable" as-sortable="c.selectedAs" ng-model="c.selected">
          <div ng-repeat="item in c.selected" 
               as-sortable-item
               ng-dblclick="c.onSelDblClick(item)"
               class="list-group-item">
            <div as-sortable-item-handle>
              <span>{{item.u_song_title.display_value}}</span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>  
  <div>
  </div>
</div>

CLIENT

function($rootScope, $scope, spUtil) {
	var c = this;
	c.selected = [];

	// clone means cannot reorder
	c.availableAs = {
		itemMoved: function (event) {
			event.source.itemScope.item.used = true;
		},
		clone: true // clone this side
	}

	c.selectedAs = {
		itemMoved: function (event) {
			// moved back to available
			var item = event.source.itemScope.item;
			moveToAvailable(item);
			removeItem(c.data.all, item);
		},
		dragStart: function () {
			c.availableAs.clone = false;
		},
		dragEnd: function () {
			c.availableAs.clone = true;
		}
	}

	// double click moves from left bucket to right
	c.onDblClick = function(item) {
		var t = angular.copy(item);
		item.used = true; // original is now used
		c.selected.push(t);
	}

	// double on selected removes and unsets Available used
	c.onSelDblClick = function(item) {
		moveToAvailable(item);
		removeItem(c.selected, item);
	}

	function removeItem(array, item) {
		var n = array.indexOf(item);
		if (n !== -1)
			array.splice(n, 1);		
	}
	
	function moveToAvailable(item) {
		var t = item.sys_id.value;
		angular.forEach(c.data.all, function(target) {
			if (target.sys_id.value == t)
				target.used = false;
		})		
	}
	
	c.saveMySongs = function(){
		//pass the data to server in order to save
    c.sever.get(
			{
      action:'saveSongs',
      songs: c.selected,
			gigsys_ID: data.gigSysId
      }
		)
			.then(function(r){
    //pass whatever you need here
			  c.data.sys_id = r.data.sys_id;
				c.data.number = r.data.number;
			  c.data.u_song_title = r.data.u_song_title;
		

     });

	}
	


}


















SERVER

(function () {
	// grab the filtered songs
	data.all = [];
	var filterQuery = "u_perform_statusIN1,2,3^u_song_status=2";
	var gr = new GlideRecord('x_120100_band_songs');
	gr.orderBy('u_song_title');
  gr.addEncodedQuery(filterQuery);
	gr.query();
  while (gr.next()) {
    var t = $sp.getRecordElements(gr, 'u_song_title,sys_id');
		data.all.push(t);
	}
		
	// get the sys_id of the current gig from the URL
	data.gigSysId = $sp.getParameter("sys_id");
	
	// save the song to the gig in the m2m table
	if (input){
			var addSong = new GlideRecord('x_120100_band_m2m_set_list');
		  if (addSong.get(input.saveMySongs)) {
				if (input.action == 'saveSongs') {
					data.songs = {};
					$sp.getRecordValues(data.title, sys_id, "u_song_title, sys_id");
				}
			}	
		}
	
})();

Anyone with any ideas on where I am going wrong would be appreciated. 

Thank you!

Cam

 

 

 

 

1. could you print out  

 console.log(input.songs); // should be your list of sys_ids

console.log(input.gigsys_ID); // sys_is of your record

2. since you have 

data.gigSysId

you do not need to pass this from controller

3. when you retrieve your data on server side you should have something similar to:

if (input.action == 'saveSongs' ){
 for(var indx in input.songs){
	var addSong = new GlideRecord('x_120100_band_m2m_set_list');
            addSong.initialize(); 
            addSong.gig=data.gigSysId; 
            addSong.song=input.songs[indx]; 
            addSong.insert();
	
	}
}

after you create new relationships between songs and gig you can return something so  you will know that it was successful 

Let me know if this makes sense to you