I have weird issue when trying to create a custom autocomplete editor.
Basicly what I've done is I've pulled the built-in AutocompleteEditor class and refactored it to plain ES6, and renamed the class to ProductSelectEditor. No modifications to the code logic.
When I try to use it, I'm getting error "Cannot read property 'onCommit' of undefined" when handleChange() is called:
handleChange() {
this.props.onCommit(); // props undefined
}
Now if i replace the editor with the real built-in AutocompleteEditor, it works just fine. I can't see any straight reason, why my custom version does not work, when only alterations I'm doing are refactoring the code away from TypeScript, renaming the class, and eventually exporting the class out as default?
Any clues on what I'm not understanding here?
Below is the whole refactored code
import React from 'react'
import ReactDOM from 'react-dom'
import ReactAutocomplete from 'ron-react-autocomplete';
import PropTypes from 'prop-types';
import '../css/ron-react-autocomplete.css'
const { shapes: { ExcelColumn } } = require('react-data-grid')
let optionPropType = PropTypes.shape({
id: PropTypes.required,
title: PropTypes.string
});
export default class ProductSelectEditor extends React.Component {
static propTypes = {
onCommit: PropTypes.func,
options: PropTypes.arrayOf(optionPropType),
label: PropTypes.any,
value: PropTypes.any,
height: PropTypes.number,
valueParams: PropTypes.arrayOf(PropTypes.string),
column: PropTypes.shape(ExcelColumn),
resultIdentifier: PropTypes.string,
search: PropTypes.string,
onKeyDown: PropTypes.func,
onFocus: PropTypes.func,
editorDisplayValue: PropTypes.func
};
static defaultProps = {
resultIdentifier: 'id'
};
handleChange() {
this.props.onCommit();
}
getValue() {
let value;
let updated = {};
if (this.hasResults() && this.isFocusedOnSuggestion()) {
value = this.getLabel(this.autoComplete.state.focusedValue);
if (this.props.valueParams) {
value = this.constuctValueFromParams(this.autoComplete.state.focusedValue, this.props.valueParams);
}
} else {
value = this.autoComplete.state.searchTerm;
}
updated[this.props.column.key] = value;
return updated;
}
getEditorDisplayValue() {
let displayValue = {title: ''};
let { column, value, editorDisplayValue } = this.props;
if (editorDisplayValue && typeof editorDisplayValue === 'function') {
displayValue.title = editorDisplayValue(column, value);
} else {
displayValue.title = value;
}
return displayValue;
}
getInputNode() {
return ReactDOM.findDOMNode(this).getElementsByTagName('input')[0];
}
getLabel(item) {
let label = this.props.label != null ? this.props.label : 'title';
if (typeof label === 'function') {
return label(item);
} else if (typeof label === 'string') {
return item[label];
}
}
hasResults() {
return this.autoComplete.state.results.length > 0;
}
isFocusedOnSuggestion() {
let autoComplete = this.autoComplete;
return autoComplete.state.focusedValue != null;
}
constuctValueFromParams(obj, props) {
if (!props) {
return '';
}
let ret = [];
for (let i = 0, ii = props.length; i < ii; i++) {
ret.push(obj[props[i]]);
}
return ret.join('|');
}
render() {
let label = this.props.label != null ? this.props.label : 'title';
return (<div height={this.props.height} onKeyDown={this.props.onKeyDown}>
<ReactAutocomplete search={this.props.search} ref={(node) => this.autoComplete = node} label={label} onChange={this.handleChange} onFocus={this.props.onFocus} resultIdentifier={this.props.resultIdentifier} options={this.props.options} value={this.getEditorDisplayValue()} />
</div>);
}
}
Alright, after few hours of poking and mangling found the reason for the props to be undefined. Apparently after stripping out the Typescripts, I needed to re-bind 'this' in order to get the correct context:
<ReactAutocomplete ... onChange={this.handleChange.bind(this)} ... />
Related
I have the following code
#Injectable()
export class ReceptionService {
private generalInfoDataSrc$ = new BehaviorSubject<any>(null);
public generalInfoData = this.generalInfoDataSrc$.asObservable();
setGeneralInfo(dataSrc: GeneralInfoMModal) {
this.generalInfoDataSrc$.next(dataSrc);
}
}
From my component1 I will set the above as
OnSelect(patient: any) {
let generalInfo = new GeneralInfoMModal();
generalInfo.id = patient.id;
// some other code here
// this.recepService.setGeneralInfo(generalInfo);
}
// from component2
//
ngOnInit() { getPatientDetails() }
getPatientDetails() {
this.receptionService.generalInfoData.pipe(mergeMap(response => {
if (response && response.id) {
this.loading = true;
return this.receptionService.get('User/Get' + response.id, this.sourceobj);
} else {
return of(null);
}
}), takeUntil(this.unsubscribe$)).subscribe(response => {
this.patient = response;
this.loading = false;
}, error => {
this.loading = false;
// this.utility.showMsg('An error occurred while getting user.')
}, () => {
})
}
Every things works well. I keep on selecting a user thereby calling the User/Get api. But if in case if the api returns an error then error part is executed after which when there is a change in behaviorsubject(user is selected) it doesn't call the User/Get. Is there other way of handling errors with behaviorsubject or any other approach to handle the idea. How a behaviorsubject should be used in such a case.
If you are using the same behavior subject over and over again, and if there is an error, you need to set the behavior subject back to null, so that when the next user is set, it will get the latest value.
Try something like this:
getPatientDetails() {
this.receptionService.generalInfoData.pipe(mergeMap(response => {
if (response && response.id) {
this.loading = true;
return this.receptionService.get('User/Get' + response.id, this.sourceobj);
} else {
return of(null);
}
}), takeUntil(this.unsubscribe$)).subscribe(response => {
this.patient = response;
this.loading = false;
}, error => {
this.loading = false;
///////////////////////////////// ADD THIS LINE ///////////////////////
this.recepService.setGeneralInfo(null);
// this.utility.showMsg('An error occurred while getting user.')
}, () => {
})
It's my understanding that when filterChangedCallback is called that the grid filters the current data and calls doesFilterPass and isFilterActive as it does so.
Here I have column that uses a custom filter, and a floating filter. The floating filter (a checkbox) gets displayed ok, and when I click on it onFloatingFilterChanged in the custom filter is called, the green filter active icon appears, and the grid refreshes, but doesFilterPass does not get called at all and I can't figure out why.
There's something fundamental I'm not getting with custom filters so if anyone can shed any light on this I'd be grateful.
(Edit: it may be the interplay between the custom/floating filters that's at issue. There was a similar ag-grid-angular issue posted to github last year but nothing came of that. So perhaps it's a bug?)
Custom filter
export default class BooleanFilter {
init(params) {
this.params = params;
this.valueGetter = params.valueGetter;
this.filterChangedCallback = params.filterChangedCallback;
this.status = false;
}
onFloatingFilterChanged(status) {
this.status = status;
this.filterChangedCallback();
}
getGui() {
return '<div />';
}
getModel() {
return this.isFilterActive() ? { filterType: 'boolean', filter: this.status } : undefined;
}
setModel(model) {
this.state.status = model ? model.value : '';
}
isFilterActive() {
return this.status === true;
}
doesFilterPass(params) {
console.log(this.status, params);
}
}
Floating filter
export default class FloatingCheckboxFilter extends Component {
state = { status: false };
handleToggle = (e) => {
const { target: { checked } } = e;
const { parentFilterInstance } = this.props;
this.setState({ status: checked });
parentFilterInstance((instance) => {
instance.onFloatingFilterChanged(checked);
});
}
onParentModelChanged = (parentModel) => {
this.setState({
status: !parentModel ? false : parentModel.filter
});
}
render() {
const { status } = this.state;
return (
<input
style={{ marginTop: '0.75em' }}
type="checkbox"
checked={status}
onChange={this.handleToggle}
/>
);
}
}
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 have a custom form component that implements ControlValueAccessor. This component has an internal property touched.
export class BmInputComponent implements ControlValueAccessor, Validator {
private onTouchedCallback: () => {};
private touched: boolean = false;
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
onBlur() {
this.touched = true;
this.onTouchedCallback();
}
}
I need to implement a method like
markTouched() {
this.touched = true;
}
That could be called by the user of the component when markAsTouched is executed in the formControl: customInputControl.markAsTouched()
I cannot find an angular-way to do this.
#Edit:
Tried to inject the NgControl:
#Component({
selector: 'bm-input',
templateUrl: './bm-input.component.html',
encapsulation: ViewEncapsulation.None,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => BmInputComponent),
multi: true
}
]
})
export class BmInputComponent implements ControlValueAccessor, Validator {
private onTouchedCallback: () => {};
private touched: boolean = false;
constructor(#Self() #Optional() public _formControl: NgControl) {
this._viewDate = new Date();
if (this._formControl) {
this._formControl.valueAccessor = this;
this._formControl.statusChanges.subscribe(this.markTouched);
}
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
onBlur() {
this.touched = true;
this.onTouchedCallback();
}
markTouched() {
if(this._formControl.touched)
this.touched = true;
}
}
But I am getting Cannot instantiate cyclic dependency! NgControl when the component is invoked with a formControl.
Have you tried #SkipSelf() instead of #Self()?
You could try this:
constructor(private injector: Injector) {
}
ngDoCheck() {
let ngControl = this.injector.get(NgControl);
if (! ngControl.control) {
return;
}
this.touched = ngControl.control.touched;
}
The circular dependency is caused by having both the NG_VALUE_ACCESSOR in your #Component(...) providers, and injecting NgControl in the constructor. These are mutually exclusive.
See the example in the NG material documentation here: https://material.angular.io/guide/creating-a-custom-form-field-control#ngcontrol
Suppose I have defined a library app with some classes:
library app;
class User {
// some members
}
class Question {}
class Answer {}
Is it possible to get the mirrors of class specified by name?
var className = specifyClassName(); // may be "User", "Question", etc
ClassMirror cm = getClassMirror(className);
Here is possible example of your requirements.
library app;
import 'dart:mirrors';
import 'dart:async';
void main() {
var names = [
'dart.async.Future',
'dart.mirrors.ClassMirror',
'app.User',
'app.Question',
'Answer',
'app.Stackoverflow'
];
for(var name in names) {
var clazz = getClassMirrorByName(name);
print('$name: $clazz');
}
}
ClassMirror getClassMirrorByName(String className) {
if(className == null) {
return null;
}
var index = className.lastIndexOf('.');
var libname = '';
var name = className;
if(index > 0) {
libname = className.substring(0, index);
name = className.substring(index + 1);
}
LibraryMirror lib;
if(libname.isEmpty) {
lib = currentMirrorSystem().isolate.rootLibrary;
} else {
var libs = currentMirrorSystem().findLibrary(new Symbol(libname)).toList();
if(libs.length == 1) {
lib = libs.first;
}
}
if(lib == null) {
return null;
}
return lib.classes[new Symbol(name)];
}
class User {
// some members
}
class Question {}
class Answer {}
Output:
dart.async.Future: ClassMirror on 'Future'
dart.mirrors.ClassMirror: ClassMirror on 'ClassMirror'
app.User: ClassMirror on 'User'
app.Question: ClassMirror on 'Question'
Answer: ClassMirror on 'Answer'
app.Stackoverflow: null