Passing Dart objects to js functions in js interop - dart

I've been playing around a bit with the new js interop of dart. So far everything was very straight-forward. But one thing I'm not sure about is how to deal with js stuff like this:
MathJax.Hub.Config({
showProcessingMessages: false,
showMathMenu: false
.. many other different options
});
I can translate the MathJax.Hub.Config part:
#JS('MathJax') external MathJaxClass get MathJax;
class MathJaxClass {
external HubClass get Hub;
}
#JS('MathJax.Hub')
class HubClass {
external void Config(options);
}
But now I would like to have the options argument of Config function to be a Dart Object. I'm not sure how to do this. The only way, I can get something working is with a Map:
MathJax.Hub.Config(new JsObject.jsify({
'showProcessingMessages': false,
'showMathMenu': false
}));
But this is surely not ideal. Any ideas?

The syntax is as follows:
#anonymous
#JS()
class Config {
external bool get showProcessingMessages;
external bool get showMathMenu;
external factory Config({bool showProcessingMessages, bool showMathMenu});
}
Here the Config name is not matching any javascript name, so you can name it whatever you want. You can then call it like this:
MathJax.Hub.Config(new Config(
showProcessingMessages: false,
showMathMenu: false
));
The object passed to the js function, will be a regular javascript object:

Since a recent update the #anonymous annotation is used to create JS objects from Dart classes instead of a factory constructor.
#JS()
#anonymous
class Config {
external bool get showProcessingMessages;
external set showProcessingMessages(bool value);
external bool get showMathMenu;
external set showMathMenu(bool value);
}
MathJax.Hub.Config(new Config()
..showProcessingMessages= false
..showMathMenu = false
}));

Related

How do I run some code only once in Dart?

I wonder if there's a language sugar/SDK utility function in Dart that allows to protect a certain code from running more than once?
E.g.
void onUserLogin() {
...
runOnce(() {
handleInitialMessage();
});
...
}
I know I can add a global or class static boolean flag to check but it would be accessible in other functions of the same scope with a risk of accidental mixup in the future.
In C++ I could e.g. use a local static bool for this.
There is no built-in functionality to prevent code from running more than once. You need some kind of external state to know whether it actually did run.
You can't just remember whether the function itself has been seen before, because you use a function expression ("lambda") here, and every evaluation of that creates a new function object which is not even equal to other function objects created by the same expression.
So, you need something to represent the location of the call.
I guess you could hack up something using stack traces. I will not recommend that (very expensive for very little advantage).
So, I'd recommend something like:
class RunOnce {
bool _hasRun = false;
void call(void Function() function) {
if (_hasRun) return;
// Set after calling if you don't want a throw to count as a run.
_hasRun = true;
function();
}
}
...
static final _runOnce = RunOnce();
void onUserLogin() {
_runOnce(handleInitialMessage);
}
It's still just a static global that can be accidentally reused.

I passed a class to my front-end project through electron's preload, but I couldn't create an instance?

the error displayed:
Can't the class constructor ABC be called without "new"
frontend:
"vite": "^2.1.2",
"vite-plugin-html": "^2.1.0",
"vite-plugin-vue2": "^1.4.2"
vue2
electron 14
I create a class and use preload like this:
const { contextBridge } = require('electron')
class ABC {
constructor() {
this.item = {}
}
update() {
console.log(`<<<<2021年09月16日 13:56:33>>>>`, this)
}
}
contextBridge.exposeInMainWorld('$electron', {
ABC
})
and I get ABC in my frontend:
const {ABC } = window.$electron
const abc = new ABC()
But console throw an error that Class constructor ABC cannot be invoked without 'new'
You cannot expose complex types like Object via the contextBridge.
Only object properties that are considered 'simple' or a function are made available. The documentation states:
The api provided to exposeInMainWorld must be a Function, string, number, Array, boolean, or an object whose keys are strings and values are a Function, string, number, Array, boolean.
The contextBridge will gut your class of methods and only retain properties. The documentation also states:
Function values are proxied to the other context and all other values are copied and frozen. Any data / primitives sent in the API become immutable and updates on either side of the bridge do not result in an update on the other side.
This is another reason why classes will not work, they're typically passed by reference.
See the table of supported parameters, errors and return types.

Returning anonymous JS function which returns javascript plain object from Dart

Dart Code
import 'dart:html' as html;
import 'dart:js' as js;
import 'package:js/js.dart';
void main() {
var data = new AddLocationData(locationName: "Location1", locationPath: "ThisFolder");
var func = () => data;
html.window.console.log(func);
html.window.console.log(func());
}
#JS("")
#anonymous
class AddLocationData {
external String get locationName;
external String get locationPath;
external factory AddLocationData({String locationName, String locationPath});
}
You would assume that func will be a js function but its not. Its type/name is main_closure. See the screenshot
So the first two lines were printed from Dart code then I used Chrome Inspect Element window and right clicked on main_closure' and selected "Store as global variable" which then printedtemp1` and then I used it to display some information about the generated code.
So it is clear Dart returned an object and not a js function and so is the reason of asking this question.
So I want temp1 to be a function instead of temp1.call$0 so that I can get the data by calling temp1() and not temp1.call$0().
See js package doc:
Passing functions to JavaScript.
If you are passing a Dart function to a JavaScript API, you must wrap it using allowInterop or allowInteropCaptureThis.

Inject ServiceLocator in Validator

How can I inject the "normal" ServiceManger into a custom validator used for REST calls (Use without Form). ZF 2.2.7 Used to inject an instance of external library into an validator.
I have tried the following, and nothing works:
Inject it with the ValidationPluginManager, service not found
Inject it via factory, factory will not be loaded in validator chain
Inject it via validator options, not possible because the "ServiceManager" is an instance of ValidationPluginManager with the asme result as mentioned in #1
Is there any concept how to solve this problem, or do i have to give up and link all libraries statically?
Not tested this and have never done with with ValidationPluginManager but works with ControllerManager, FormElementManager etc
// GetServiceLocator call should return Instance of ServiceManager
// Then retrieve the service, Yay!
$validationPluginManager->getServiceLocator()->get('SomeService')
There has been a discussion on github about a somewhat similar problem here. They suggested to use Zend\Form\FormAbstractServiceFactory and tinker with dependencies there (weierophinney before closing the topic).
In your post you mention you are not using a form did you mean you are not using the form in a classic kind of way or are you bypassing the whole form in particular?
It simply seems off to me to use a validator if there isn't a form present. Could you elaborate more on that?
EDIT: To my understanding zf2 requires that your input filters have form elements like 'inputs' etc. You did not post any code and I simply do not know if/or your able to bypass this somehow. I still do not understand why you'd still want to use validators in combination of input filters. I would simply skip the input filters and write the custom validator.
My personal preferences is to write factories instead of anonymous functions within module.php files. But this also could work the anonymous function way.
I would then simply resolve the dependencies within the customValidatorFactory and get the factory within my controller or whatever place I would need it.
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use CustomValidator;
class CustomValidatorFactory implements FactoryInterface
{
/**
* Create Service Factory
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$sm = $serviceLocator->getServiceLocator();
$customService = $sm->get('Application\Service\Geocoding');
$validator= new CustomValidator();
$validator->setCustomService($service);
return $validator;
}
}
// CustomValidator.php
class CustomValidator extends Zend\Validator\AbstractValidator
{
public function setCustomService($service)
{
$this->service = $service;
}
public function isValid($value)
{
$customService = $this->service;
if ($customService->customMethod() == true) {
return true;
}
return false;
}
}
//module-config.php
'service_manager' => array(
'factories' => array(
'custom\ValidatorFactory' => 'Namespace\To\CustomValidatorFactory',
),
),
//yourController or whatever.php will require access to the service manager
$customValidation = $sm->get('custom\ValidatorFactory');
// should return true or false now
$state = $customValidation->isValid($someValue);

Under Rails3.1.1/Coffeescript - not always getting the function safety wrapper

I have recently started using coffeescript with Rails and I am finding that sometimes the generated javascript does not get the function safety wrapper.
Here is a sample project demonstrating it.
For example, this CS code, in index.js.coffee:
class Foo
afunc: ->
alert("afunc")
Correctly becomes:
(function() {
var Foo;
Foo = (function() {
function Foo() {}
Foo.prototype.afunc = function() {
return alert("afunc");
};
return Foo;
})();
}).call(this);
But this code, from other.js.coffee:
class App.Func
ouch: ->
alert("ouch")
becomes this un-wrapped version
App.Func = (function() {
function Func() {}
Func.prototype.ouch = function() {
return alert("ouch");
};
return Func;
})();
It seems to be due to the "App." prefix - which I can see affects naming/scope - but why is coffeescript compiling it differently...
App is defined in setup.js.coffee, like this:
window.App =
Models: {}
Which also does not get wrapped, unless I add a class into that file too.
I am sure it must be my misunderstanding - so thanks in advance for the pointers to the manual :).
EDIT:
I created this question as I thought it might be behind some issues I was having with my backbone/coffeescript app, but it seems that it was not. As the class is linked to a public/global thing "App", it seems to work wrapped or not. Still would be useful to know why its happening - is it by design?
The "function safety wrapper" feature you are using works to prevent local variables from being set on the global namespace. Since setting an object property (App.Func) doesn't affect the global namespace, the declaration is not wrapped in a function.

Resources