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.
Related
I need to set the property to DataSet during onInit, to change the visiblity of some controls in my View. I could solve this problem with setting the visibility dynamically in controller. But I want to use the expression binding and the visible property in the View to set visibilites.
I'm getting an error in the function OnStartSetVisibilites. self.getView().getBindingContext() returns UNDEFINED. Without the sPath, I can't use setProperty. And without setProperty, my View-Controls don't know the visible-value.
How to fix this?
View:
<uxap:ObjectPageSubSection visible="{= ${Responsible} === '1'}" id="leader">
</uxap:ObjectPageSubSection>
OnInit in View-Controller:
onInit: function () {
var startupParameters = this.getOwnerComponent().getComponentData().startupParameters;
var sWorkitem = startupParameters.TASK[0];
this.setModel(this.getOwnerComponent().getModel());
this.getModel().metadataLoaded().then(function () {
var sObjectPath = this.getModel().createKey("DataSet", {
Workitem: sWorkitem
});
this.getView().bindElement({
path: "/" + sObjectPath
});
}.bind(this));
var self = this;
var model = this.getOwnerComponent().getModel();
this.getModel().read("/CharSet", {
success: function (response) {
$.sap.Chars = response.results;
self.onStartSetVisibilities(model, self);
}
});
// self.getView().attachAfterRendering(function () {
// self.onStartSetVisibilities(model, self);
// });
}
OnStartSetVisibilities:
onStartSetVisibilities: function (model, self) {
var char = model.getProperty(ā€˛GeneralData/Char");
if (char !== "" || char !== null) {
model.setProperty(self.getView().getBindingContext().sPath + "/Responsible",
this.getResponsibleForChar(char));
}
}
I put together some code which might solve your issue (it's untested so it may contain syntax errors!).
I introduced the concept of Promises which simplifies the asynchronous behavior of JS. I also replaced some of the inner functions with Arrow functions so you don't have to deal with that or self. Arrow functions basically use the this of the scope they are defined within.
Your app should now have a proper flow.
First you bind the view.
After the view is bound you read the CharSet.
After the data is read you set the visibility stuff
onInit: function () {
const startupParameters = this.getOwnerComponent().getComponentData().startupParameters;
const sWorkitem = startupParameters.TASK[0];
this._bindView(sWorkitem)
.then(() => this._readCharSet())
.then(() => this._setVisibilities())
},
_bindView: function (sWorkitem) {
return new Promise((resolve) => {
const oModel = this.getOwnerComponent().getModel();
oModel.metadataLoaded().then(() => {
const sPath = "/" + oModel.createKey("DataSet", {
Workitem: sWorkitem
});
this.getView().bindElement({
path: sPath,
events: {
change: resolve,
dataReceived: resolve
}
});
});
});
},
_readCharSet: function () {
return new Promise((resolve) => {
const oModel = this.getOwnerComponent().getModel();
oModel.read("/CharSet", {
success: resolve
});
});
},
_setVisibilities: function () {
const oModel = this.getOwnerComponent().getModel();
const sChar = oModel.getProperty("GeneralData/Char");
if (sChar) {
// ...
}
}
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(() { ... });
}
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>
)
}
}
I am getting an unexpected token error whenever I add any function before render().
For example:
var App = React.createClass({
constructor() {
super();
this.state = {
notebooks: {}
};
}
render: function() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
});
This component compiles fine when I delete the constructor() function, but with constructor(), it throws the unexpected token error pointing at render: function(). Anyone have any ideas on how to fix this?
You're confusing syntax here. createClass takes an object as an argument, not an ES6 class. Objects need commas separating items. Also, plain objects don't have a constructor like class does. In React's createClass object spec you probably want getInitialState instead
React.createClass({
getInitialState() {
return { notebooks: {} }
}, // <--- comma
render() {...}
})
you could however rewrite the entire thing using an ES6 class (which does not have commas separating methods.
class App extends React.Component {
constructor() {
super();
this.state = {
notebooks: {}
};
} // <--- no comma
render() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
}
Yes, this is because you are not separating the function with a comma. You should write it like this
var App = React.createClass({
getInitialState: function() {
return {
notebooks: {}
};
},
render: function() {
return (
<div style={{"height": "100%"}}>
<NotebookNav notebooks={this.props.notebooks}></NotebookNav>
<Technique></Technique>
</div>
);
}
});
also should be this.state.notebooks not this.props.notebook
I was following this tutorial and reached the below code with searches wikipedia for a given term. The below code works fine and fetches the search result from wikipedia.
export class WikiAppComponent {
items: Array<string>;
term = new Control();
constructor(public wikiService: WikiService) { }
ngOnInit() {
this.term.valueChanges.debounceTime(400).subscribe(term => {
this.wikiService.search(term).then(res => {
this.items = res;
})
});
}
}
But when I refactored the and moved the code for search to a separate function it is not working. this.wikiService inside the search function is going undefined. Can you throw some light on why it is going undefined?
export class WikiAppComponent {
items: Array<string>;
term = new Control();
constructor(public wikiService: WikiService) { }
search(term) {
this.wikiService.search(term).then(res => {
this.items = res;
});
}
ngOnInit() {
this.term.valueChanges.debounceTime(400).subscribe(this.search);
}
}
You are having a scope issue, "this" inside your callback is not refering to your page. Change your function callback like this:
this.term.valueChanges.debounceTime(400).subscribe(
(term) => {
this.search(term);
});