dom.setInterval not available on ScalaJS - scala.js

I'm following tutorial http://www.lihaoyi.com/hands-on-scala-js/#MakingaCanvasApp and it provides code :
dom.setInterval(() => run, 50)
But this causes a compiler error :
object setInterval is not a member of package org.scalajs.dom
How to set the interval on the dom ? It appears this method has been removed instead of deprecated ?

It is still available under dom.window.setInterval, I think.
However, you should use scala.scalajs.js.timers.setInterval instead:
import scala.scalajs.js
js.timers.setInterval(50) {
run
}

Related

Open Web Components Testing / Lit - component not being rendered?

I'm trying to test my Lit components with #open-wc/testing. Lit has an example repo here, with this test:
https://github.com/lit/lit-element-starter-ts/blob/main/src/test/my-element_test.ts#L44
When I try to render my element like they do in their example, I get this error:
jtests/components/coding-editor.test.ts:
🚧 Browser logs:
HTMLElement: <coding-editor></coding-editor>
❌ renders
TypeError: Cannot read properties of null (reading 'querySelector')
at o.<anonymous> (jtests/components/coding-editor.test.ts:16:30)
My component works in the browser and uses the name "coding-editor". It's as if this test renderer has no idea that I'm using a custom component though. I don't know why shadowRoot is null in my case.
My code is roughly this:
import { CodingEditor } from '../../app/javascript/components/coding-editor';
import {expect, fixture} from '#open-wc/testing';
import {html} from 'lit/static-html.js';
it('renders', async () => {
const el = await fixture(html`
<coding-editor></coding-editor>
`) as CodingEditor;
console.log(el);
const text = el.shadowRoot!.querySelector('.table-constrainer');
// expect(text).to.not.be.null
});
How can I get my test to render this properly, with the shadowRoot populated?
This is likely due to TypeScript removing the CodingEditor import that's only used as a type so the side effect of defining the custom element is not happening.
You can either set the TS compiler option importsNotUsedAsValues to preserve (See https://www.typescriptlang.org/tsconfig/#importsNotUsedAsValues) or add another import line for the side-effect like
import '../../app/javascript/components/coding-editor';
Additional explanation here too: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-imports-being-elided-in-my-emit
As a side-note, in the starter example you linked to, the imported class is used in assert.instanceOf as a value so it does not get elided by TypeScript.

#JSGlobalScope in scala.js 1.0 (JavaScriptException, ReferenceError, var is not defined)

After migrating from scala.js 0.6.x to 1.0, I've got some code related to #JSGlobalScope broken.
My use case is like this:
there's a 3rd-party library that expects some global var to be set to a function
when loaded and ready, it calls this function (by name)
I set this function in global scope from scala.js
The code looks like this:
#js.native
#JSGlobalScope
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
then I set this var like this:
Globals.callbackFunctionFor3rdPartyLib = () => {
// do things
}
and then I add the script into the DOM.
This was working with scala.js 0.6.x, but with 1.0 I'm getting an exception like the following:
scala.scalajs.js.JavaScriptException: ReferenceError: callbackFunctionFor3rdPartyLib is not defined
In the changelog for 1.0.0 there's a "Breaking changes" section that mentions this:
Accessing a member that is not declared causes a ReferenceError to be thrown
...
js.Dynamic.global.globalVarThatDoesNotExist = 42
would previously create said global variable. In Scala.js 1.x, it also throws a ReferenceError.
My question is:
what is the right way to do something like this (create a new global var) in scala.js 1.0?
If you know you'll always be in a browser context, you can use #JSGlobal("window") instead of #JSGlobalScope on your Globals, which will then be equivalent to doing window.myGlobalVarFor3rdPartyLib in JS. So that will work.
#js.native
#JSGlobal("window")
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
If not, but you are using a script (so not a CommonJS nor an ES module), the best thing is actually to use
object Globals {
#JSExportTopLevel("myGlobalVarFor3rdPartyLib")
var foo: js.Function[Unit] = ...
}
Note that Globals is a normal Scala object now, not a JS one.
The #JSExportTopLevel creates a top-level var myGlobalVarFor3rdPartyLib at the top of the script, and then assigning Globals.foo will also assign that top-level var.
If you're not using a script nor know that you're going to always be in a browser, then you need to figure out the global object yourself. Scala.js 0.6.x tried to do that for you, but could fail, so we don't do that anymore. You can at least follow the "instructions" on the documentation of js.special.fileLevelThis to reproduce what Scala.js 0.6.x was doing. I repeat the instructions here:
Using this value should be rare, and mostly limited to writing code
detecting what the global object is. For example, a typical detection
code--in case we do not need to worry of ES modules--looks like:
val globalObject = {
import js.Dynamic.{global => g}
if (js.typeOf(g.global) != "undefined" && (g.global.Object eq g.Object)) {
// Node.js environment detected
g.global
} else {
// In all other well-known environment, we can use the global `this`
js.special.fileLevelThis
}
}
Note that the above code is not comprehensive, as there can be JavaScript
environments where the global object cannot be fetched neither through
global nor this. If your code needs to run in such an environment, it
is up to you to use an appropriate detection procedure.

Using TypeScript with Dojo - import/export issues

In the company I work for we are using Dojo framework and lately I have started pushing to use it with TypeScript.
I took a look around and found this great article on how this topic, you can find it here:
https://gregwiechec.com/2016/01/creating-dojo-widget-in-typescript/
The last 2 lines in this solution are:
var exp = _declare("alloy.editors.StringListTs", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin, _ValueRequiredMixin], new StringList());
export = exp;
I followed the same pattern and it works great, except for 2 issues that I cant seem to have a good solid solution:
1. If you look at the solution, when calling dojo declare method, the class created needs to be instantiated (this is because dojo looks at properties and not prototype).
2. The more problematic issue, is the fact that I am exporting out the dojo declared object, and not the class it self. This is problematic when you try to import the class (typescript import), and even if I declare the variable exp as the class type, I get an error saying that there is no constructor to what i am trying to import.
I faced your same problem, and for me has been a transpiler issue.
TL;DR if you import something using the import * as WidgetName from '<path>' syntax and don't use WidgetName in the js (like you would do for a widget in a template), the transpiler won't import it. Use import '<path>' instead.
Long answer: It's my first time with TypeScript and, similarly you, I'm converting a dojo project to TypeScript. Hoping to help more people, I'll put few steps which helped me to import modules correctly.
Step 0 dojoConfig packages
Defining you own package on dojoConfig won't work, modules must be referenced using relative path.
To be clear, with this example:
dojoConfig = {
...
packages : [ { name : "myproject", location : "js/myproject" },
{ name : "dojo", location : dojoBase+"dojo" },
{ name : "dijit", location : dojoBase+"dijit" },
{ name : "dojox", location : dojoBase+"dojox" } ]
};
It's not possible to have widget importing each other with import * as WidgetName from 'myproject/WidgetName' approach, but rather you have to import * as WidgetName from './WidgetName' (notice the '.' vs 'myproject').
Step 1: importing dojo declaration (not mandatory, as far as I noticed)
I'm relying on node, I pulled dojo-typing using npm install dojo-typings --save-dev. In the files property I specified [ "src/js/**/*.ts", "src/js/**/*.js", "node_modules/dojo-typings/dojo/1.11/index.d.ts", "node_modules/dojo-typings/dojo/1.11/modules.d.ts", "node_modules/dojo-typings/dijit/1.11/index.d.ts", "node_modules/dojo-typings/dijit/1.11/modules.d.ts" ].
Step 2: using the correct options on the transpiler:
{
"target": "es5",
"allowJs": true,
"module": "amd",
"moduleResolution": "classic",
"noImplicitUseStrict" : true
},
noImplicitUseStrict solved the error dojo/parser::parse() error TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them. allowJs allowed me to mix TypeScript and native dojo together.
Step 3: returning using export
This is an example of a very minimal module which doesn't require/import anything, but only exports a log method (file would be 'toast.ts'):
const log = function(message) {
window['dojo'].publish("mainTopic",
[{
message: "<span style='font-size: 12px;'>" + message +"</span>",
type: 'info',
duration: 3000
}]
);
}
export { log }
For completeness, in pure Dojo you would have written something like (file would be 'toast.js'):
define([ ],
function(){
var log = function(message) {
dojo.publish("mainTopic",
[{
message: "<span style='font-size: 12px;'>" + message +"</span>",
type: 'info',
duration: 3000
}]
);
};
return { log : log };
});
Step 4: rewrite your widget and import it correctly
I use a Main widget which takes all the body, here the content of Main.ts:
/// <amd-dependency path="dojo/text!./Main.html" name="template" />
declare var template: string;
import * as _Widget from 'dijit/_Widget';
import * as _TemplatedMixin from 'dijit/_TemplatedMixin';
import * as _WidgetsInTemplateMixin from 'dijit/_WidgetsInTemplateMixin';
import * as dojoDeclare from 'dojo/_base/declare';
import './MyVanillaJavascriptWidget';
import './MyModule';
import 'dojox/widget/Toaster';
import toast = require('./utility/toast');
export default dojoDeclare("mm.Main", [ _Widget, _TemplatedMixin, _WidgetsInTemplateMixin ], {
templateString : template,
});
There are different types of imports:
the first is the one I struggled the most, for the template
import * as ... is a sequence of dojo objects I use in the class
import '<path>' is used for widget I declared in the template using data-dojo-type
import name = require('<path>') is another way to require modules
For completeness, this would be the original Main.js file:
define([ "dijit/_Widget", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin",
"dojo/_base/declare", "dojo/text!./Main.html",
"dojox/widget/Toaster", "./MyVanillaJavascriptWidget", "./MyModule"
],
function( _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, declare, mainTemplate, toast) {
return declare("mm.Main", [ _Widget, _TemplatedMixin, _WidgetsInTemplateMixin ], {
templateString : mainTemplate,
});
});
Conclusion: The missing constructor most probably comes from the way you import a module, checking the transpiled code helped me to understand where the issue was. To succeed with the import, a bit of accuracy is needed here and there (all the steps above should give an overview).
I'm still in the process of converting my project (other issues may come out), but I hope this helps also other poor souls trying to use TypeScript with Dojo!

Dynamically evaluating code at runtime

Is it possible to take a string/AST of source code and evaluate it (like eval()) at runtime in Fantom? I found some suggesting features in the documentation but not obvious evidence.
It's not as easy as calling an eval() function, but it is possible. You need to first compile your Fantom code into a class before you can execute it.
Plastic, a library from Alien-Factory, does just that. Example:
using afPlastic
class Example {
Void main() {
eval("2 + 2") // --> 4
}
Obj? eval(Str code) {
model := PlasticClassModel("MyClass", true)
model.addMethod(Obj?#, "eval", "", code)
myType := PlasticCompiler().compileModel(model.toFantomCode)
return myType.make->eval()
}
}
The PlasticCompiler class does the job of compiling Fantom code into a usable Type.
It uses the Fantom compiler library and is based on code found in Fansh - a Fantom shell, part of the Fantom distribution.

Scala problem with jMock expectations and returning a value from mock

Solved. IntelliJ didn't highlight the fact that my imports were incomplete.
Hi,
I have a simple Scala program that I'm trying to develop using jMock. Setting basic expectations works nicely but for some reason Scala does not understand my attempt to return a value from a mock object. My maven build spews out the following error
TestLocalCollector.scala:45: error: not found: value returnValue
one (nodeCtx).getParameter("FilenameRegex"); will( returnValue(regex))
^
And the respective code snippets are
#Before def setUp() : Unit = { nodeCtx = context.mock(classOf[NodeContext]) }
...
// the value to be returned
val regex = ".*\\.data"
...
// setting the expectations
one (nodeCtx).getParameter("FilenameRegex"); will( returnValue(regex))
To me it sounds that Scala is expecting that the static jMock method returnValue would be a val? What am I missing here?
Are you sure about the ';'?
one (nodeCtx).getParameter("FilenameRegex") will( returnValue(regex))
might work better.
In this example you see a line like:
expect {
one(blogger).todayPosts will returnValue(List(Post("...")))
}
with the following comment:
Specify what the return value should be in the same expression by defining "will" as Scala infix operator.
In the Java equivalent we would have to make a separate method call (which our favorite IDE may insist on putting on the next line!)
one(blogger).todayPosts; will(returnValue(List(Post("..."))))
^
|
-- semicolon only in the *Java* version
The OP explains it himself:
the returnValue static method was not visible, thus the errors.
And the will method just records an action on the latest mock operation, that's why it can be on the next line or after the semicolon :)
import org.jmock.Expectations
import org.jmock.Expectations._
...
context.checking(
new Expectations {
{ oneOf (nodeCtx).getParameter("FilenameRegex") will( returnValue(".*\\.data") ) }
}
)