How to use functions exported using scalajs in reactjs components? - javascript

I have tried exporting some functions to the main.js file in scalajs using #JSExportTopLevel annotation, as explained in the Export Scala.js APIs to JavaScript scalajs documentation and building the main.js as explained here.
This results in main.js from which I am able to make use the functions exported in the scalajs code.
Now I want to use these exported functions in my reactjs components. For this I tried following steps:
Copy the main.js file in the public folder
Include the javascript file in the index.html, like so:
<script type="text/javascript" src="./main.js"></script>
Now, if I load the app in the browser and try to use these functions in the browser console, it works fine:
console.log(foo());
But I am not the utilise these functions in reactjs components:
import React from 'react';
const RuleEditor = () => {
console.log(foo());
return (
<>
</>
);
};
export default RuleEditor;
I always get the following compilation error:
foo is not defined no-undef
I do understand that reactjs is not able to recognise the function as I haven't really specified where to get that function from but I am not really sure how to achieve it. I have gone to couple of other stackoverflow posts where some suggestions where to look for it in window object but I didn't find those functions there.
Please suggest a proper way to make use of functions exported from scalajs in reactjs components. TIA.

Exporting objects/classes/functions as top level exports put them in Javascript global scope.
class Foo(val x: Int) {
#JSExport
def square(): Int = x*x // note the (), omitting them has a different behavior
#JSExport("foobar")
def add(y: Int): Int = x+y
}
You can utilise these functions in the scripts embedded in html or in scalajs as shown here.
But if you need to use these functions in a nodejs app, you need to export these functions from a module. We need to add the following in the build.sbt. Read more here.
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) }
Now, we can make use of the exported functions in a nodejs app by importing them like so:
import { square, add } from './main'
If you wish to export these functions from a module named other than main, provide module id for each exported function like so:
class Foo(val x: Int) {
#JSExport("square", "my-math")
def square(): Int = x*x // note the (), omitting them has a different behavior
#JSExport("foobar", "my-math")
def add(y: Int): Int = x+y
}
and use it like so:
import { square, add } from './my-math'

Related

Names spacing JavaScript imports from native modules

I am migrating code that used to not use native JS modules.
// File: form.js
// `lc` is a global object I attach each file's functions to.
let lc = window.lc || {}
lc.form = {};
lc.form.add = function() {
// add a form
}
And then would be called as lc.form.add(), which is nice because the word add has context.
However now that I am changing the code to use JS modules:
// File: form.mjs
export function add() {
// add a form
}
And then would be called as add(), which has no context. Now I understand I could do:
import {add as formAdd} from '/form.mjs'
But thats like a step backwards to start smurf naming all imported things.
Is there a way to import the module like in Python, and then call functions of the modules. E.g. use dot etc notation to access it's function, something like this:
import '/form.mjs' as form
form.add()
You're looking for a namespace import:
import * as form from '/form.mjs';
form.add();

How do I call a function in an external js file using typescrpt

We are trying a POC of adding Typescript and Webpack to our Angularjs project.
I am able to get my webpack bundle to generate, however at runtime the program cannot find the various functions in my validator.js. Can you please offer some advice?
login-view.components.ts
declare var findFormNode: any; //function in validator.js
//LogInUser
self.login = function ($event, command) {
if (findFormNode($event.target.id)) {
...
}
}
main.ts is importing the file
import "./../../../CommonStaticFiles/include/js/Validators.js";
bundle.js
eval("/* WEBPACK VAR INJECTION */(function($) {/*\r\n\r\n VALIDATORS\r\n\r\n ... n\n\nfunction findFormNode(
error
ReferenceError: findFormNode is not defined
at LoginController.self.login (login-view.component.ts:28)
at fn (eval at compile (angular.js:NaN), <anonymous>:4:267)
at callback (angular.js:29019)
In order for your functions to be properly imported, there are few things that you have to make sure of.
First, make sure you are exporting your functions correctly. Here's an example of how to export a function from Validator.js:
export const validateFunc1 = ():void => {};
Next, you have to make sure you are using proper import syntax. In order to import the function above, you would do the following:
import {validateFunc1} from "./../../../CommonStaticFiles/include/js/Validators.js";
Alternatively, if you want to import all exported functions at once, then you can use this syntax:
import * as validatorFuncs from "./../../../CommonStaticFiles/include/js/Validators.js";
Lastly, check that the location of Validators.js is correct. It's a common mistake to be looking in the wrong directory. Your code editor can usually help you find the right path to use.

When using dynamic import in a function, how can I specify type info in global variable?

My simplified server code looks like below.
server.ts
import google from "googleapis";
const androidPublisher = google.androidpublisher("v3");
app.use('something', function(req, res, n){
...
})
...(only one of the dozens of other methods use androidPublisher)
I am importing googleapis library in order to setup androidpublisher variable. However, this googleapis library is big and it takes 400ms~700ms to fully import file, when it takes 10ms~30ms to import other library files.
Because my environment is serverless architecture (firebase functions), AND because approximately 1 out of 100 requests actually need androidPublisher, I want to take advantage of dynamic import to import googleapis when it is necessary. Otherwise, above setup actually adds 400ms/700ms latency to every request that spins up new serverless instance, even when androidPublisher is not needed.
So I have made changes like below.
server.ts
let androidPublisherInstance:any;
async function getAndroidPublisher() {
const googleapis = await import("googleapis");
if (androidPublisherInstance === undefined) {
const ap = googleapis.google.androidpublisher("v3");
androidPublisherInstance = ap;
}
return androidPublisherInstance;
}
...(one of methods use getAndroidPublisher() to get androidPublisher instance)
with above setup where I am using global variable & helper function to initialize androidPublisher only when needed. This works as intended and 400ms~700ms latency gets added when androidPublisher is needed for the first time. However, I ended up with type of androidPublisherInstance to be any. I couldn't correctly define the type because type definition is available inside of googleapis and that is residing inside of getAndroidPublisher function.
Thus, I lose all benefit of using typescript and have to play guess games while using methods/properties when I use androidPublisherInstance.
And I think I must use global variable, because I do not want to initialize androidPublisher multiple times (googleapis.google.androidpublisher("v3")) whenever a function calls getAndroidPublisher()
Am I missing something? Is there a way to use dynamic import & let client to be initialized only once without needing to use global variable?
You can just import the type. As long as you use it only in type definitions, not in value expressions, the compiled JavaScript will never load the module:
import { androidpublisher_v3 } from "googleapis";
let androidpublisher: androidpublisher_v3 | undefined;
Alternatively, to make sure you don't accidentally reference it in the wrong place, use only import types:
let androidpublisher: import("googleapis").androidpublisher_v3 | undefined;

Document function that returns object property by parameter

I'm making an Api repository for my Vue.js application, following this article.
Thing is, I like to document my functions so I have better code completion on VSCode. I typically use jsDoc for this.
I'm stuck here:
import DiarioEscolarRepository from './diarioEscolarRepository';
import PeriodoAvaliativoRepository from './periodoAvaliativoRepository';
import AtividadeAvaliativaRepository from './atividadeAvaliativaRepository';
import CorteEtarioRepository from './corteEtarioRepository';
const repositories = {
diarioEscolar: DiarioEscolarRepository,
periodoAvaliativo: PeriodoAvaliativoRepository,
atividadeAvaliativa: AtividadeAvaliativaRepository,
corteEtario: CorteEtarioRepository,
};
export default const RepositoryFactory = {
get(name){
return repositories[name];
}
};
I need to make it so the editor understands that the get function is a simple acessor to the repositories object.
I tried using #typedef and #type, but none of them worked properly.
I tried something like #returns {repositories.name}, but is also does not work.
Is there a way to document this?
I also thought about using a typescript definition file but I never did it, so I don't know where to begin.

TypeScript: access global var from a class without import

I got this module like this:
module MyModule {
export class Constants {
public static WIDTH:number = 100;
public static HEIGHT:number = 100;
....
}
}
export = MyModule;
Now I need to use MyModule.Constants.WIDTH in another class but I can't use import (I need to deliver this class js to a third party, and they don't use requirejs). Right now I use reference to get code checking but it keep giving this error (at transpilling time)
error TS2095: Could not find symbol 'MyModule'
What should I do now so I can use autocomplete and get rid of this error?
I hope you're not mindplay on the TypeScript forum otherwise I'm about to repeat myself.
export and import work together. You should either be using both or neither. If you check what the generated code looks like with and without the export keyword you'll see that export causes a module to be built. Since the third party can't use RequireJS I don't think this is what you want.
I would structure my classes like the following:
// file pkg/Foo.ts
module company.pkg {
export class Foo {}
}
// file pkg2/Bar.ts
module company.pkg2 {
export class Bar{}
}
Putting everything into the name space of your company minimizes the chance of a conflict with another library. Classes know about each other using a reference /// <reference path="..." /> which will allow it to compile.
Since you're not doing modules I would also compile to a single file using --out filename.js. That gets all the files included in (usually) the right order.

Categories