Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RequireJS - Cannot Access External Module Function

I'm having an issue with RequireJS. Essentially, I'm not able to access a function defined inside another file from another one.

I need to do that because I want to export a given subset of functions like

define('submodule', [], function() {

    let myFunction1 = function(){ return "Hello"; }
    let myFunction2 = function(){ return " From"; }
    let myFunction3 = function(){ return " Submodule!"; }

    return {
             myFunction1 : myFunction1,
             myFunction2 : myFunction2,
             myFunction3 : myFunction3,
    };

});

And accessing them from another file

define('main', ['config', 'sub1', 'sub2', 'submodule'],   
        function(config, sub1, sub2, submodule) {

  //Config
  alert(config.conf);

  //Submodule
  let callSubmodule = function() {
    alert(submodule.myFunction1() + 
          submodule.myFunction2() + 
          submodule.myFunction3());
  }

  //sub1
  let callSub1 = function() {
    alert(sub1.myFunction1());
  }

  //sub2
  let callSub2 = function() {
    alert(sub2.myFunction1());
  }

});

The fact is that usually I'm able to do this with sub1 and sub2, but, with submodule, I simply can't. I think it's somehow caused by the dependencies in require.config.js.

My require.config.js:

require(['common'], function () { //contains vendors
    require(['config'], function () { //contains a js config file
        require(['main'], function () { //main file
            require(['sub1', 'sub2'], function () { //some subfiles
                require(['submodule']);
            });
        });
    });
});

For submodule.myFunction1() and othe two related functions I'm getting:

Uncaught (in promise) TypeError: Cannot read property 'myFunction1' of undefined

This is weird since I'm able to do that in other situations and I really can't understand why this is happening. For instance, I'm able to call sub1 and sub2 functions from main and other files but not submodule in particular.

Index.html

//Taken from Plunker
. . .
<script data-main="common"  data-require="[email protected]" data-semver="2.1.20" src="http://requirejs.org/docs/release/2.1.20/minified/require.js"></script>
<script src="require.config.js"></script>

. . .
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>

common.js contains vendors, here's just an example

requirejs.config({
   baseUrl : "",
    paths : {
             "jquery" : "http://code.jquery.com/jquery-latest.min.js"
    }
});

sub1.js

define('sub1', ['submodule'], function(submodule) {

    let myFunction1 = function(){ return "called sub1"; }

    return {
             myFunction1 : myFunction1
    };

});

sub2.js

define('sub2', ['submodule'], function(submodule) {

    let myFunction1 = function(){ return "called sub2"; }

    return {
             myFunction1 : myFunction1
    };

});

I set up a Plunker with @SergGr help that tries to replicate application's structure but all the modules get undefined on click. On the real application this does not happen.

How can I solve this?

like image 662
AndreaM16 Avatar asked Feb 23 '17 16:02

AndreaM16


3 Answers

This is your code:

define('main', ['submodule'], function(submod) {
   console.log(submodule.myFunction());
});

You have submod in the parameter list. But you then try to access submodule. Note that you return the function straight from your module (return myFunction), so your module has the value of the function myFunction and thus the module is what you should call. The code should be:

define('main', ['submodule'], function(submod) {
   console.log(submod());
});
like image 195
Louis Avatar answered Oct 25 '22 06:10

Louis


The way you've named your modules I would expect they all came from a require config file. I would not expect that requirejs would know how to load those files without some sort of explicit compilation process. I also suspect that your server is returning something due to a 404 that JS is almost able to interpret without exploding.

Your setup seems and naming scheme seems quite strange. If you have the ability to start from scratch below are my recommendations.

Recommendations:

  • I'm noticing that you're using absolute paths. I highly recommend using relative paths for everything. There are many reasons for this.
  • Your data-main should be what you call "require.config.js". Your common.js is actually a require.config.js.
  • You load require.config.js (which is your main) separately using a script tag. You can do this but it's strange.
  • You can use the "commonjs" style syntax to require files without needing to use the array to define all your dependencies. I recommend that.

This is my recommendation for a set-up:

index.html

<script src="/js/config.js" />
<script src="http://requirejs.org/docs/release/2.1.20/minified/require.js" />
<script>
      require('/js/main', function(main) {
          main({});
      });
</script>

/js/config.js

// setting requirejs to an object before its loaded will cause requirejs to use it as the config
window.requirejs = {
   baseUrl : "/",
    paths : {
        "jquery" : "http://code.jquery.com/jquery-latest.min.js"
    }
};

/js/main.js

define(function(require) {
    const sum = require('./sum');
    return (a, b) => sum(a, b);
});

/js/sum.js

define(function(require) {
    return (a, b) => a + b;
});
like image 38
Parris Avatar answered Oct 25 '22 07:10

Parris


I Managed to solve this issue. Essentially, it was caused by a circular-dependency between the modules. So, a needed b and b needed a leading to one of them being undefined on the dependency resolution.

I found a solution to that on the answer provided by @jgillich at requirejs module is undefined.

So, I managed to solve using, in main

define('main', ['config', 'sub1', 'sub2', 'require'],   
    function(config, sub1, sub2, submodule, require) {

  //Config
  alert(config.conf);

  //Submodule
  let callSubmodule = function() {
    alert(require('submodule').myFunction1() + 
          require('submodule').myFunction2() + 
          require('submodule').myFunction3());
  }

});

As @jgillich said:

If you define a circular dependency ("a" needs "b" and "b" needs "a"), then in this case when "b"'s module function is called, it will get an undefined value for "a". "b" can fetch "a" later after modules have been defined by using the require() method (be sure to specify require as a dependency so the right context is used to look up "a"):

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

http://requirejs.org/docs/api.html#circular

like image 20
AndreaM16 Avatar answered Oct 25 '22 07:10

AndreaM16