Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Calling widget's client controller from Body HTML script tag

Nisar2
Mega Guru

I've the following in the Body HTML template of widget.

 

Nisar2_0-1698236053729.png


The alert is working. But I've not been able to figure out how to pass that info to the client controller or server side. Any suggestions?

1 ACCEPTED SOLUTION

I have never tried pulling in React in Service Portal, but the following might work.

In the client controller define some variables on $scope or c.

api.controller = function($scope){
    var c = this;
    $scope.geoLoc = {
        "lat": "",
        "long": ""
    };
    
    //to test if this works watch the variables for changes and log out values
    $scope.$watch( 'geoLoc ', function (newValue, oldValue ) {
        if(newValue.lat || newValue.long)
            console.log(newValue)
    }, true)
...
}

 

Since your React script is outside AngularJS scope you'll need to grab it within the script tags in the HTML template. You'll also want to wrap within a setTimeout to ensure the elements are present before the script runs.

 

  <script>
    
    setTimeout(function(){
        var scope = angular.element('#Geo_Map_root').scope();
        let locTimer;
        const loadLocationMap = () => {
          const { LocationMap } = window.geoJsLib;
          const root = ReactDOM.createRoot(document.getElementById('Geo_Map_root'));
          const onSubmit=(location) => {
            scope.geoLoc.lat = location.latitude.toString();
            scope.geoLoc.long = location.longitude.toString();
          }
          root.render(React.createElement(LocationMap, { onSubmit }, null));
        }
    },500);
    
  </script>

 

I know that doing this works without using React, so theoretically it should work with it.

react_test.gif

View solution in original post

8 REPLIES 8

Watch() triggers when the value is actually changed on the variables it's watching. I have never used React mix with AngularJS and in the Service Portal; so I don't know if that is the issue. However, the watch() isn't needed unless you really are needing it to do something once the value changes. I put that in the script just to have it console log out.


@ChrisBurks wrote:

However, the watch() isn't needed unless you really are needing it to do something once the value changes


Unfortunately, I do need something to happen once the lat/long is selected. I make a call to the server script to update those values in the backend. So I'm assuming $watch() is the only way to do it in this case?

No, $watch() is not the only way. When the script reaches into the scope, if the call to the server script is created on $scope or c, then it is accessible just like the other variables. In this case don't use the $watch(), just pass the data straight to the script that calls to the server.

 

For example I'll replace the $watch() with a script that calls and passes data to the server.

api.controller = function($scope){
    var c = this;
    $scope.geoLoc = {
        "lat": "",
        "long": ""
    };
    
    //pass the longitude and latitude to server
    c.doAnUpdate = function (geoLoc) {
        c.server.get({
            "action":"UPDATE",
            "geoLoc": geoLoc
        })
    }
   //a .then() can be used after teh c.server.get() if you need to bring something back to the client after the server has done. it's job
...
}

 

Now in the HTML <script> use that function

  <script>
    
    setTimeout(function(){
        var scope = angular.element('#Geo_Map_root').scope();
        let locTimer;
        const loadLocationMap = () => {
          const { LocationMap } = window.geoJsLib;
          const root = ReactDOM.createRoot(document.getElementById('Geo_Map_root'));
          const onSubmit=(location) => {
            scope.geoLoc.lat = location.latitude.toString();
            scope.geoLoc.long = location.longitude.toString();
            //New line added below
            scope.c.doAnUpdate(scope.geoLoc);
          }
          root.render(React.createElement(LocationMap, { onSubmit }, null));
        }
    },500);
    
  </script>

 

Server Script:

(function() {
  
	if(input && input.action == "UPDATE"){
		//do what is needed with your values input.geoLoc
	}
})();

 

 

That's great. Thanks. I just wan't sure if I could pass that from the <script> tag. Looks like we can.