Run imported function via another function - javascript

Starting point: With this code I trigger func2 via func1. This works fine so far.
window.addEventListener("click", function (event) {
func1("func2()")
})
function func1(functionAsString) {
eval(functionAsString)
}
function func2() {
alert("success!")
}
The problem:
As soon as I import func2 from another javascript file. I can't run func2 with this code anymore....
I have tried it with both import variants
import * as myfunc from "./xy" // myfunc[functionAsString]
as well as
import {func2} from "./xy" // eval(functionAsString)
and unfortunately I do not get func2 addressed.
Can someone give me the decisive tip? Gladly without .eval()

I recreated your problem in a local environment and it works fine
I also did that on codesanbox and it didn't work here
The problem is that in the codesandbox environment parcel was used which transpiles the files and changes the names of the functions hence it couldn't find the name
Anyway, this should work fine in either case:
import * as myfunc from "./xy"
myfunc['func2']()

Related

Namespace Collision when file name and variable name match?

I'm running into a bug that only happens intermittently.
Let's say I'm importing a function from a file boats.js
// boats.js
export function inspectBoat(boats) {
console.log(boats);
return true;
}
// ocean.js
import { inspectBoat } from './boats'
export default function ocean() {
const boats = 'many boats';
return inspectBoat(boats);
}
The Babel transpiled version of the last line reads
return (0, _boats2.inspectBoat)(_boats);
On occasion, I'll hit a bug that reads (0, _boats2.inspectBoat) is not a function. The bug goes away when I refresh the page and it'll work perfectly fine.
Is the fact that I have both a boats file and boats variable contributing to this intermittent bug? Or is it an issue with the import statement?
I could try changing my const boats variable name, but due to the intermittent nature of this bug, I wouldn't know if it truly solves the issue. Hoping someone could share some relevant background knowledge on this.
The issue is with the function call in the ocean function. The imported function is named inspectBoat, but the function call is using inspectBoats, which is not defined. Changing the function call to use the correct function name should fix the issue:
// boats.js
export function inspectBoats(boats) {
console.log(boats);
return true;
}
// ocean.js
import { inspectBoats } from './boats'; // exported as "inspectBoats"
export default function ocean() {
const boats = 'many boats';
return inspectBoats(boats);
}

JavaScript method isn't working on view in Rails 6+

I want to declare a method in a JS file and use it in the .html.erb like this below method
const getSetHtml = () => {
console.log("Hello World");
}
and in the view .html.erb like this below code
onclick="getSetHtml()"
but it's showing
Uncaught ReferenceError: getSetHtml is not defined
I imported files like below
// /app/javascript/application.js
import "#hotwired/turbo-rails";
import "controllers";
import "trix";
import "#rails/actiontext";
import "trix";
import "#rails/actiontext";
import "./bootstrap.bundle.min";
import "./editor"; // this file
Note: Others JS library is working fine but the custom method isn't working.
Thanks
Why do you want to set the function with const? If it fits with your specific use case, cool! Otherwise I would personally just go ahead and define it regularly, either way here is the syntax for what you're trying to do.
const getSetHtml = function() {console.log("Hello World")};
Your current function returns the function itself rather than returning the return value of the function(the console.log("Hello World")).
I can't be entirely sure but I believe in your application.js you can just import the file like this I would try the other thing first: import "editor";
I hope this helps even a little, good luck. Let me know how it goes!
You can expose your function by adding it to the global object
global.getSetHtml = () => {
console.log("Hello World");
}

Using babel-plugin-rewire to test private non-referenced functions

I am using babel-plugin-rewire (https://www.npmjs.com/package/babel-plugin-rewire) in main.test.js to test non-exported functions in main.js. This has been working except in the case where the function is not referenced in main.js; in this case I get the following error: TypeError: _get__(...) is not a function.
Only after I add a reference to the function in main.js I am able to access it in the test file (it works even if I don't actually call the function). However I do not want to make any changes to main.js. Is this the expected behavior of babel-plugin-rewire, and is there a workaround for this?
//main.js
function test123() {
return true;
}
test123; //Cannot access function in test file unless I add this reference!
//main.test.js
const test123 = require('./main').__get__('test123');
test('test123', () => {
expect(test123()).toEqual(true);
});
You can test non-exported functions in main.js, but the non-exported functions need to be used in at least one exported function in the same file.
In your case, this will work without a reference.
//main.js
export default function foo() {
test123();
}
function test123() {
return true;
}

Calling an exported function by a string value of the function name

This is a very simple example of what I am trying to achieve, basically I want to call a function by a string value of it's name, e.g. "hello" should call hello()
I have a helper.js file which contains an exported function e.g.
export function hello() {
console.log('is it me you`re looking for?');
}
I am importing this into another js file for usage
import {hello} from './helper';
I have tried using eval, window and new Function to call my function but no luck
//getting console error "hello is not defined"
eval('hello()');
var fn = window['hello()'];
fn();
var fn = new Function('hello()');
fn();
If I wrap the function like so, the eval fires the wrapper.
function helloWrapper() {
hello();
}
eval('helloWrapper()');
I just cant seem to fire the exported hello() function directly. I have around 10 functions I'll need to fire so having a wrapper for each seems a bit hacky and wondering if there is a way I can achieve this?
Any help would be greatly appreciated if anyone can point me in the right direction.
Thanks in advance
eval("hello()") should work just fine -- but it's not how you should do this. :-)
Instead, build an object containing the functions:
import {hello} from './helper'; // In some environments, these need the
import {groot} from './groot'; // .js on the filenames.
// ...
const functions = {hello, groot/*, ... */};
and then call them like this:
functions[name]();
Live example on plnkr.co
Generally, referring functions by their names is unsafe in client-side code - or any other that can be minified. The approach explained in the question will work only because hello isn't just function name but an import. Due to how ES modules work, import names will be preserved on minification.
In order for a function to be referred by its name, it should be object property. In case of imports there's already such object, it's module export:
import * as helper from './helper';
helper['hello']();
In case there are multiple modules where functions may originate from, there should be intermediate module that re-exports them.
export * from './helper';
export * from './another-helper';
All functions from underlying modules will be available as properties when it's imported as *:
import * as helper from './reexported-helpers';
helper['hello']();
helper['bye']();

Convert closure to es6 module

I'm using a javascript build environment that supports es6 modules (using es6-module-transpiler) so you can simply import stuff across different files.
Now I got a third party library that I'd like to be "importable".
The library populates its functionality like this:
(function () {/*...*/}).call(this);
Would it be safe to omit the closure and convert it to:
export default function () {/* ... */};
Or is there a better way?
Thanks in advance!
The original code you show invokes the anonymous function, which to make any sense must define a global variable, whereas the second code fragment you show merely exports the function, which is a different thing.
For purposes of discussion, let's assume the original code defines a global like this:
// my-third-party-module.js
(function() {
let myVar = 22;
window.MyThirdPartyModule = { log: function() { console.log(myVar); } };
}.call(this);
and you are using is as so:
// app.js
MyThirdPartyModule.log();
You could rewrite this as
// my-third-party-module.js
let myVar = 22;
export default { log: function() { console.log(myVar); } };
// app.js
import MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
Note that we have moved the variable myVar which was local to the anonymous function to the top module level.
However, depending on your preferences, rather than exporting a big object, which is sort of a pre-module mentality, you might want to export its APIs individually:
// my-third-party-module.js
let myVar = 22;
export function log { console.log(myVar); }
// app.js
import {log} from `my-third-party-module';
log();
or if you prefer
// app.js
import * as MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
However, all of these approaches assume you are able and willing to edit the source of the third party library. If that is not the case, you could write a little piece of glue code, such as
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
export default MyThirdPartyModule; // Export the global it defined.
// app.js
import MyThirdPartyModule from 'my-third-party-module-interface';
If you would prefer again to export individual APIs, you could extend the glue to re-export each of them:
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
const {log, otherAPI, ...} = MyThirdPartyModule;
export {log, otherAPI, ...};
// app.js
import {log} from 'my-third-party-module-interface';
The conversion of legacy dependencies is still an issue. And the horrible workflow they use makes things a lot harder, prefixing the actual code with browserify and webpack silliness.
So what to do? Existentially, the library is guaranteed only to deposit a global in window but by obscure and weird ways. And all slightly different.
So let the legacy simply do what it is supposed to do for you, but wrapped in a module so that you can import it rather than use a script tag:
https://medium.com/#backspaces/es6-modules-part-2-libs-wrap-em-up-8715e116d690

Categories

Resources