is it possible to lazily use JS libs with Dart? - dart

I am using chartjs (with the dart interface https://pub.dartlang.org/packages/chartjs) and trying to make it deferred by injecting a <script src="chartjs.js"></script> into the head section and awaiting it's load event to then use the lib.
I am getting this exception: Cannot read property 'Chart' of undefined.
It does not happen when the script is within the head of the html before dart.
So, is it possible to load a JS lib after Dart loaded?

this is a problem in DDC.
It addeds require.js to the HTML and conflicts with other libs.
https://github.com/dart-lang/sdk/issues/33979
The solution I've found is to manually remove the header section that uses requirejs from the third-party lib you want to use.
For example, take chartjs: https://cdn.jsdelivr.net/npm/chart.js#2.8.0/dist/Chart.js
You remove this two lines:
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) :
typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) :
Then the file can be lazily added to the DOM without conflicts.
This is my code to lazily fetch scripts:
class ClientUtils {
static final _scriptFetched = <String, Future<bool>>{};
static ScriptElement _scr(String url) => new ScriptElement()
..async = true
..type = 'text/javascript'
..src = url;
static Future<bool> fetchScript(String url,
{String contextCheck}) async {
bool shouldCheck = contextCheck?.isNotEmpty == true;
hasContext() => js.context.hasProperty(contextCheck) &&
js.context[contextCheck] != null;
if (shouldCheck && hasContext())
return true;
if (!_scriptFetched.containsKey(url)) {
Completer<bool> c = new Completer<bool>();
if (!shouldCheck) {
ScriptElement s = _scr(url)
..onLoad.forEach((Event e) {
c.complete(true);
});
document.body.children.add(s);
} else {
Timer.periodic(Duration(milliseconds: 300), (t) {
if (hasContext()) {
t.cancel();
}
c.complete(true);
});
document.body.children.add(_scr(url));
}
_scriptFetched[url] = c.future;
}
return _scriptFetched[url];
}
}

found a better way!
lets remove the define variable after dart loads, then any third-party lib works when added async :D
add this to your main():
import 'dart:js';
void main() {
context.callMethod('fixRequireJs');
}
and in your index.html:
<script type="text/javascript">
window.fixRequireJs = function()
{
console.log('define is ', typeof define);
if (typeof define == 'function') {
console.log('removing define...');
delete define;
window.define = null;
}
}
</script>

You can try the deferred as syntax:
import 'package:chartjs/chartjs.dart' deferred as chartjs;
void main() {
chartjs.loadLibrary().then(() { ... });
}

Related

Use regular PropTypes checks inside a custom validator

I started out with a very simple PropTypes validation:
name: PropTypes.string.isRequired,
Now I need to only have string be required if a certain other condition is false, so:
name: ({name, otherProp}) => {
if (otherProp === 'foo') return null;
if (typeof name === 'string') return null;
return new Error('Error');
}
Which is fine in this case, but what I'd really like to be able to do after the first check is run the regular PropTypes check for string.isRequired inside my custom validator, like maybe:
name: ({name, otherProp}) => {
if (otherProp === 'foo') return null;
return PropTypes.string.isRequired;
}
It seems like there should be a way to do this with PropTypes.checkPropTypes, but I can't figure it out.
I discovered while poking around the code for Material UI a clever way to do something equivalent to this: parallel prop-types checks. They have a very simple utility function chainPropTypes which goes like this:
export default function chainPropTypes(propType1, propType2) {
if (process.env.NODE_ENV === 'production') {
return () => null;
}
return function validate(...args) {
return propType1(...args) || propType2(...args);
};
}
You use this to do a custom prop types check along with a standard check. For example like this:
anchorEl: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.func]), (props) => {
if (props.open && (!props.anchorReference || props.anchorReference === 'anchorEl')) {
// ...
You can write your own custom PropType which can take a PropType as an argument.
export const myCustomPropType = (propType, otherProp) => {
return function validate(props, propName, ...rest) {
const originalPropType = propType(props, propName, ...rest);
if (props[otherProp] === 'foo') return originalPropType;
if (typeof props[propName] === 'string') return originalPropType;
return new Error('Error');
};
};
Usage
MyComponent.propTypes {
myProp: myCustomPropType(PropTypes.string.isRequired, 'otherPropName')
}

Setting React Props, state from Jquery Tree event

I am new to "React" world. On my project, now I started using react on my exisiting .NET MVC projects. The issue I face now is, I have Jquery/JS based tree which is populated from a react class component's "ComponentDidMount()" . There is an "on select" event for this JS tree(third party JS plugin), from which i need to set the "state" and "props" of that component. Some how I unable to call React component's eventHandler from JQuery tree's "select" event. Please help.
import React, { Component } from 'react'
class ReportsTree extends React.Component {
constructor(props) {
super(props)
this.handleTreeNodeSelect = this.handleTreeNodeSelect.bind(this);
}
componentDidMount() {
var tree = this.loadReportsTree();
tree.on('nodeDrop', function (e, id, parentId, orderNumber) {
// console.log("ID : " + id + " Parent :" + parentId + " OrderNumber :" + orderNumber);
if (parentId != undefined && parentId.indexOf('R') > -1) {
return false;
}
});
tree.on('nodeDataBound', function (e, node, id, record) {
//console.log(record)
if (record.parentid === null || record.parentid === '') {
tree.expand(node);
}
});
tree.on('select', function (e, node, id) {
this.handleTreeNodeSelect(id); //THIS LOC IS NOT WORKING - Unable to find handleTreeNodeSelect event handler
});
}
state = {
id: ''
}
handleTreeNodeSelect = (_id) => {
this.props.NodeTypeDetails(this.state.id = _id);
this.setState(() => { return { id: _id } });
};
loadReportsTree() {
var tree = $('#ssrTree').tree({
uiLibrary: 'bootstrap',
dataSource: '/GetReportItems',
primaryKey: 'id',
imageUrlField: 'imageUrl',
dragAndDrop: true,
});
return tree;
}
render() {
return (<div className="reportsTreeContainer">
<div id="ssrTree"></div>
</div>)
}
}
export default ReportsTree
I think it is not recommended to mix react and jquery. but it is a must for you then you can change your componentDidMount like this
componentDidMount() {
const self = this;
var tree = self.loadReportsTree();
...
tree.on('select', function (e, node, id) {
self.handleTreeNodeSelect(id); // the difference is to use self instead of this
});
}
the key thing here is to save a reference to your "this" which is original a react component then at your event handler onSelect it can have the expected this.
tree.on('select', this.handleTreeNodeSelect);
I passed the event handler from tree to react as above. Sorry folks, I didnt tried that earlier.

React uncaught reference error: exports is not defined

I am trying to add React to an existing Rails 5.1 application, but I'm getting this error: Uncaught ReferenceError: exports is not defined.
I'm using webpacker. This is the contents of my application.js file:
//= require react
//= require react_ujs
//= require components
In my components directory, I have the file register.jsx:
class Register extends React.Component {
render(){
return(
<div>
<h1>Register a Group</h1>
</div>
)
}
}
export default Register
This file processes to this, as viewed in Chrome developer tools:
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// import React from 'react';
var Register = (function (_React$Component) {
_inherits(Register, _React$Component);
function Register() {
_classCallCheck(this, Register);
_get(Object.getPrototypeOf(Register.prototype), "constructor", this).apply(this, arguments);
}
_createClass(Register, [{
key: "render",
value: function render() {
return React.createElement(
"div",
null,
React.createElement(
"h1",
null,
"Register a Group"
)
);
}
}]);
return Register;
})(React.Component);
exports["default"] = Register;
module.exports = exports["default"];
The uncaught reference is being thrown at the very first line.
Any ideas are greatly appreciated!
React should be loaded from your application pack in app/javascripts/packs/application.js, not from app/assets/javascripts/application.js, as the default JavaScript compressor that Sprockets uses doesn't support ES6.
Your React components should be referenced from app/javascripts/components as well.
Also make sure you're importing React correctly in your component files:
import React from 'react-dom'
export default class Register extends React.Component {
render() {
return (
<div>
<h1>Register a Group</h1>
</div>
)
}
}

How to handle TypeORM entity field unique validation error in NestJS?

I've set a custom unique validator decorator on my TypeORM entity field email. NestJS has dependency injection, but the service is not injected.
The error is:
TypeError: Cannot read property 'findByEmail' of undefined
Any help on implementing a custom email validator?
user.entity.ts:
#Column()
#Validate(CustomEmail, {
message: "Title is too short or long!"
})
#IsEmail()
email: string;
My CustomEmail validator is
import {ValidatorConstraint, ValidatorConstraintInterface,
ValidationArguments} from "class-validator";
import {UserService} from "./user.service";
#ValidatorConstraint({ name: "customText", async: true })
export class CustomEmail implements ValidatorConstraintInterface {
constructor(private userService: UserService) {}
async validate(text: string, args: ValidationArguments) {
const user = await this.userService.findByEmail(text);
return !user;
}
defaultMessage(args: ValidationArguments) {
return "Text ($value) is too short or too long!";
}
}
I know I could set unique in the Column options
#Column({
unique: true
})
but this throws a mysql error and the ExceptionsHandler that crashes my app, so I can't handle it myself...
Thankx!
I can propose 2 different approaches here, the first one catches the constraint violation error locally without additional request, and the second one uses a global error filter, catching such errors in the entire application. I personally use the latter.
Local no-db request solution
No need to make additional database request. You can catch the error violating the unique constraint and throw any HttpException you want to the client. In users.service.ts:
public create(newUser: Partial<UserEntity>): Promise<UserEntity> {
return this.usersRepository.save(newUser).catch((e) => {
if (/(email)[\s\S]+(already exists)/.test(e.detail)) {
throw new BadRequestException(
'Account with this email already exists.',
);
}
return e;
});
}
Which will return:
Global error filter solution
Or even create a global QueryErrorFilter:
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter {
public catch(exception: any, host: ArgumentsHost): any {
const detail = exception.detail;
if (typeof detail === 'string' && detail.includes('already exists')) {
const messageStart = exception.table.split('_').join(' ') + ' with';
throw new BadRequestException(
exception.detail.replace('Key', messageStart),
);
}
return super.catch(exception, host);
}
}
Then in main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();
This will give generic $table entity with ($field)=($value) already exists. error message. Example:
I have modified my code. I am checking the uniqueness of username/email in the user service (instead of a custom validator) and return an HttpExcetion in case the user is already inserted in the DB.
The easiest solution!
#Entity()
export class MyEntity extends BaseEntity{
#Column({unique:true}) name:string;
}
export abstract class BaseDataService<T> {
constructor(protected readonly repo: Repository<T>) {}
private async isUnique(t: any) {
const uniqueColumns = this.repo.metadata.uniques.map(
(e) => e.givenColumnNames[0]
);
for (const u of uniqueColumns) {
const count = await this.repo.count({ where: { [u]: ILike(t[u]) } });
if (count > 0) {
throw new UnprocessableEntityException(`${u} must be unique!`);
}
}
}
async save(body: DeepPartial<T>) {
await this.isUnique(body);
try {
return await this.repo.save(body);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
async update(id: number, updated: QueryDeepPartialEntity<T>) {
await this.isUnique(updated)
try {
return await this.repo.update(id, updated);
} catch (err) {
throw new UnprocessableEntityException(err.message);
}
}
}
An approach that works for modern version of NestJS which is based in Daniel Kucal's answer and actually returns the error to the frontend when calling the JSON API is the following:
import {
Catch,
ArgumentsHost,
BadRequestException,
HttpException,
} from '#nestjs/common';
import { BaseExceptionFilter } from '#nestjs/core';
import { QueryFailedError } from 'typeorm';
type ExceptionType = { detail: string; table: string };
#Catch(QueryFailedError)
export class QueryErrorFilter extends BaseExceptionFilter<
HttpException | ExceptionType
> {
public catch(exception: ExceptionType, host: ArgumentsHost): void {
const { detail = null } = exception || {};
if (
!detail ||
typeof detail !== 'string' ||
// deepcode ignore AttrAccessOnNull: <False positive>
!detail.includes('already exists')
) {
return super.catch(exception, host);
} // else
/**
* this regex transform the message `(phone)=(123)` to a more intuitive `with phone: "123"` one,
* the regex is long to prevent mistakes if the value itself is ()=(), for example, (phone)=(()=())
*/
const extractMessageRegex =
/\((.*?)(?:(?:\)=\()(?!.*(\))(?!.*\))=\()(.*?)\)(?!.*\)))(?!.*(?:\)=\()(?!.*\)=\()((.*?)\))(?!.*\)))/;
const messageStart = `${exception.table.split('_').join(' ')} with`;
/** prevent Regex DoS, doesn't treat messages longer than 200 characters */
const exceptionDetail =
exception.detail.length <= 200
? exception.detail.replace(extractMessageRegex, 'with $1: "$3"')
: exception.detail;
super.catch(
new BadRequestException(exceptionDetail.replace('Key', messageStart)),
host,
);
}
}
Also, not forgetting main.ts:
async function bootstrap() {
const app = await NestFactory.create(/**/);
/* ... */
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new QueryErrorFilter(httpAdapter));
/* ... */
await app.listen(3000);
}
bootstrap();

How to setState in AsyncStorage block with react native?

I want setState in AsyncStorage block but there is a error: undefined is not an object(evaluating 'this.setState').
constructor(props){
super(props);
this.state={
activeID:this.props.data.channel[0].id
};
}
componentDidMount() {
AsyncStorage.getItem(this.props.data.type,function(errs,result){
if (!errs) {
if (result !== null) {
this.setState({activeID:result});
}
}
});
}
_buttonPressed = (id,name,index) => {
this.setState({activeID:id});
AsyncStorage.setItem(this.props.data.type,id,function(errs){
if (errs) {
console.log('error');
}
if (!errs) {
console.log('succeed');
}
});
}
Any help will be appreciate, thanks.
This is a binding issue. Try the following:
componentDidMount() {
AsyncStorage.getItem(this.props.data.type, (errs,result) => {
if (!errs) {
if (result !== null) {
this.setState({activeID:result});
}
}
})
}
The reason for this is that functions declared with the function specifier create a context of their own, that is, the value of this is not the instance of your component. But "fat arrow" functions do not create a new context, and so you can use all methods inside. You could as well bind the function in order to keep the context, but in this case I think that this solution is much cleaner.
I find another solution, but martinarroyo's answer is much more cleaner.
componentDidMount() {
this._loadInitialState().done();
}
async _loadInitialState(){
try{
var value=await AsyncStorage.getItem(this.props.data.type);
if(value!=null){
this._saveActiveID(value);
}
} catch(error){
}
}
_saveActiveID(id) {
this.setState({activeID: id});
}

Resources