Adding extern SOAP libray to WAB Custom Widget

1779
10
07-02-2020 07:10 AM
GerardMartin
Occasional Contributor

I am trying to use `tinysoap` library (https://github.com/mhzed/tinysoap) in an Web AppBuilder Custom Widget. 

I am able to use this library in WebAppBuilder application, as I show on the following sample code (real code included in HTML file). All my functions are included in a class called `myClass` in `customLibrary.js` file. Most functions in `customLibrary.js` call `tinysoap` functions, and everything works well:

 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
 <script src="node_modules/tinysoap/tinysoap-browser-min.js"></script>

 <script src="node_modules/lib/customLibrary.js"></script>
 <script src="https://js.arcgis.com/4.15/"></script>
 ...
 <script>
    var tinySoap = this.tinysoap;
    require([], function(){
        //code goes here
        myClass.myfunction(){...}; 
    });
 </script>

Now I have to implement the same functionality in a Web AppBuilder Custom Widget.

I have had to modify javascript code from `customLibrary.js` class to `myModule.js`, and now I have this content (sample code):

# myModule.js

define([], function () {

 var myModule = {};
 const url = "http://example.com/wsdl?wsdl";

 myModule.myFunction = function(userTxtValue, callback) {
  if (userTxtValue){
    console.log('1. myModule.myFunction:', userTxtValue);
 
    var args = {name: userTxtValue};
    tinySoap.createClient(url, function(err, client){
       client.soapClientFunction(args, function(err, result){
          return callback(result);
       });
    });
   }
 };
 return myModule;
});

# Widget.js
So, I have `Widget.js` file with reference to `myModule.js`, without problems, using in this way:

define(['./myModule'], function(customModule) {
   customModule.myFunction( userTxtValue, function(result) { 
      console.log("widget.js, result:", result);
    });
 });

Also, I have tried to insert in  init.js (its is not desirable situation) but it doesn't work

dojoConfig.packages = [{
 name: "widgets",
 location: window.path + "widgets"
 }, {
   name: "jquery",
   location: "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2",
   main: "jquery"
 }, {
   name: "tinysoap",
   location: window.path + "widgets/myWidget/node_modules/tinysoap/lib",
   main: "soap"
 },
 ...
 }]

None of these test works.

So, what is the proper way to add `tinysoap` library to my widget?

Thank you in advance !

0 Kudos
10 Replies
shaylavi
Esri Contributor

Hi Gerard,

Why adding the library into the init is not a desirable solution?

ESRI recommends it as one of 3 ways to add external libraries, just make sure you're implementing it right
Use other libraries—ArcGIS Web AppBuilder (Developer Edition) | ArcGIS for Developers  

Try putting the library under the libs folder and inside init.js you should have something like -

window.path + 'libs/tinysoap/tinysoap.js'

Inside your custom widget, the library should already be available as part of your global scope -

define([
    'dojo/_base/declare',
    'jimu/BaseWidget'
], function (declare, BaseWidget) {
     return declare([BaseWidget], {
          startup: function() {
               // ** library should be accessible at this point **
          }
     });
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Shay

Shay
GerardMartin
Occasional Contributor

Shay, thank you very much for your answer !

But, although I have followed your suggestions the code still doesn't work

Step 1. I have copy tinysoap folder to ./client/stemapp/libs folder

This is the content of tinysoap [1] folder. As you can see, it is not an AMD module.

[1] GitHub - mhzed/tinysoap: soap client in pure javascript, runs in browser 

Step 2.1. I have included this sentence in init.js:

And I got this error ( You can see ./CustomWidget/widget.js code )

Step 2.2. So I try with this sentence in init.js:

And I got this error,

 

Step 3. Finally, I have included \tinysoap\ in .stemapp\libs\packages.js, but I got the same error in step 2.2

So, I'm sure I'm making a mistake, but I am not a JS developer and I can't see where is the problem ...

Maybe, I have to read this library differently? 

Many thanks for your help !

0 Kudos
shaylavi
Esri Contributor

I would try to break down the problem by using just your own custom javascript file that doesn't do much, maybe contains a test function, and a few properties, just to see that you are able to add an external resource.

Once you got through that part, you can try to add an actual library that was already proven to be supported and possible to be added - maybe even find a sample code that implements that external resource to see the syntax and learn from it.

Once you managed to do the above, you know that you are adding the external resource right and you know that it's possible to add an external resource.  At this point, you can surely know that tinysoap cannot be added and it's not your fault. My guess is that it's dependant on other files and cannot be initialized like that without a proper install through NPM, but all of this cannot be proven until you follow the above approach and if you're not a javascript developer, I guess that doesn't make things easier.

Shay.

Shay
0 Kudos
GerardMartin
Occasional Contributor

Many thanks for your quick answer Shay !

1. I have done exactly what you suggest: I have used ESRI Custom widget to simplify the problem, It is the code of widget.js that I showed in examples.

2. Also, I have used tinysoap in an Web App Builder App succesfully (see first message).

3. In reference to NPM packages, I have been looking for tinysoap in NPM repository (https://www.npmjs.com) but it was not there, only in github:  GitHub - mhzed/tinysoap: soap client in pure javascript, runs in browser

You're right that there are dependencies.

So, the qüestion is, how to use a third party library (not implemneted as AMD module) in a ESRI widget, only as nmp package? 

Thanks in advance for your time !

0 Kudos
shaylavi
Esri Contributor

No worries, Gerard. Seems like you've done an impressive job there for someone who's not a JS developer.

To use libraries like tinysoap that has dependencies, you would probably have to switch to work with the newer versions of javascript (es6), where you are able to use NPM as well.

You can use ESRI's Yeoman tool to create an empty widget for you in es6 -

GitHub - Esri/generator-esri-appbuilder-js: Yeoman generator to help customize Esri's WebAppBuilder 

Then it should be straight forward on how to install tinysoap, but the rest of your code will need to be rewritten, so I'm not sure if it's worth it over finding a different solution.

I just recently answered someone with a resource on how to code in es6 a custom widget. You can here's a live demo by ESRI on how to code a widget in modern javascript + typescript -

Web AppBuilder for ArcGIS: Advanced Development tools and Techniques - YouTube 

Might be good preparation for you when starting to work with version 4 of the javascript API

Shay.

Shay
0 Kudos
GerardMartin
Occasional Contributor

THANK YOU FOR YOUR HELP Shay !

I need some time to process all this information , thanks again !

GerardMartin
Occasional Contributor

Thank you Shay, ESRI's Yeoman tool is a good widget generator

but it is not a solution for my problem: too much work rewrite all the code.

I keep looking how to solve this issue ...

 

Raul_Jimenez
Esri Contributor

Hi Gerard Martin and Shay Lavi ,

By recommendation of Gavin Rehkemper I have been trying using "jimu/loaderplugins/jquery-loader!" as explained here, this is the code I've used:

define([
        'dojo/_base/declare', 
        'jimu/BaseWidget',
        'jimu/loaderplugins/jquery-loader!./widgets/CustomTinySoap/libs/jquery.min.js, ./widgets/CustomTinySoap/libs/tinysoap-browser-min.js'
    ],
function(declare, BaseWidget, $, tinySoap) {
  return declare([BaseWidget], {

    baseClass: 'custom-tiny-soap',

    postCreate: function() {
      this.inherited(arguments);
      console.log('CustomTinySoap::postCreate');
      var userTxtValue = "rossinyol";
      
      const url = "" // SOAP Service

      tinysoap.createClient(url, function(err, client){
                client.setSecurity( tinysoap.WSSecurity('','') ); 
                client.localitzaToponim(argsToponim, function(err, result){
                    console.log('localitzaToponim: ', result['item']);
                    return callback(result['item']);

                }); //END client.localitzaToponim();
            });// END tinySoap.createClient();            
    }

  });

});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Then, I noticed the libraries were loaded properly, but the error was still there:

// ERROR: Uncaught Error: undefinedModule at tinysoap-browser-min.js? Wab_dv = 2.13: 1‍

And then I thought.... maybe the issue is not with Web AppBuilder itself but of the library. So I prettified the tinysoap-browser-min.js (because I couldn't find the regular version) and I noticed the problems started at the beginning of the library, while the library is checking if the function "require" exists (line 5):

! function(t, e) {
	e = {}
    var n = function() {
        var j = {};
        if (typeof require === "undefined") j.__sr = function() {};
        else j.__sr = require;
        return j.__r = function(t) {
            var e = j[t];
            return null === e.sts && e.load.call(), e.mod.exports
        }, j.https = {
            sts: 1,
            mod: {
                exports: j.__sr("https")
            }
        }, j.http = {
            sts: 1,
            mod: {
                exports: j.__sr("http")
            }
        }, j.util = {
            sts: 1,
            mod: {
                exports: j.__sr("util")
            }
        }, j.fs = {
            sts: 1,
            mod: {
                exports: j.__sr("fs")
            }
        }, j.crypto = {
            sts: 1,
            mod: {
                exports: j.__sr("crypto")
            }
        }, j.a = {‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I found it's failing on lines 13, 18, 23, 28, ... I guess it is because... the code is prepared to run also on NodeJS? and the condition is just trying to determine if you're running the code on NodeJS but it is not considering that you can have another "require" function, in this case, RequireJS library so when it tries to load the libraries ... RequireJS fails, right?

I just tried commenting all those lines, and ... ta-da! it didn't fail .... at that point xDDD. 

For some reason the "tinysoap" was not initialized under the scope of the widget, so I also did one more change, I added this line before the end of the script:

window.tinysoap = n, "object" == typeof exports && "undefined" != typeof module && (module.exports = n)

Of course, doing that, I was able to use the library within the widget.

Here you can find my modified tinysoap-browser-min.js file.

Which is my problem now?, well, first I'm having a CORS problem, to avoid that during development I have used Moesif Orign & CORS Changer chrome extension.

But after that I had another issue...

--- SHORT BREAK FROM PREVIOUS CODE --

Before keep looking on WAB I decided to check if libraries work the same way even after my changes. Here you can see two vanilla examples using the tinysoap library:

And as you can see it seems they don't.

--- END SHORT BREAK FROM PREVIOUS CODE --

And for now... this is everything I can say xD, I hope my tests can help us find a solution to the problem.

Best regards,

Raul

0 Kudos
shaylavi
Esri Contributor

Impressive work Raul, thanks for sharing. It's important to keep in mind that this isn't a sustainable solution.

I would consider it as a temporary patch (which can last for a few years as well), but it's hard to maintain and pass-over to other developers if/when necessary. Updates of the application/library might break it as well.

Shay.

Shay
0 Kudos