Polling not actually happening, React JS (ES6) - javascript

Consider for a moment the following code:
import React from 'react';
import ReactDOM from 'react-dom/dist/react-dom';
import chunk from 'lodash/array/chunk';
class Tweets extends React.Component {
constructor(props) {
super(props);
this.state = {
tweets: null,
error: null,
info: null,
source: null
}
}
componentDidMount() {
this._isMounted = true;
this.startPolling();
}
componentDidUpdate() {
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
}
componentWillUnmount() {
this._isMounted = false;
if (this._timer) {
clearInterval(this._timer);
this._timer = null;
}
}
shouldComponentUpdate(nextProps, nextState) {
for (var key in this.state) {
for (var nextKey in nextState) {
if (this.state[key] !== nextState[nextKey]) {
return true
}
}
}
return false;
}
startPolling() {
var self = this;
setTimeout(function() {
self.poll();
if (!self._isMounted) {
return;
}
self._timer = setInterval(self.poll, 10000);
}, 1000);
}
poll() {
console.log('I should be called ....');
var self = this;
var source = null;
if (this.props !== undefined) {
source = this.props.source
}
$.get(source, function(result) {
if (self._isMounted) {
self.setState({
error: null,
tweets: result.data,
source: source
});
}
}).fail(function(response) {
self.setState({
error: 'Could not fetch tweets. Looks like something went wrong.',
});
});
}
checkIfLoading() {
if (this.state.tweets === null &&
this.state.error === null &&
this.state.info === null) {
return true;
}
return false;
}
buildTweets () {
let rows = [];
let currRow;
this.state.tweets.forEach((tweet, i) => {
if(i % 3 === 0) {
currRow = i;
rows[currRow] = [];
}
rows[currRow].push(
<div className='col-lg-4 col-md-6 col-sm-6 col-xs-12' key={tweet.id}>
<div className="white-panel tweet-panel">
<span><strong>{tweet.attributes.user_name}</strong><em>Posted: {tweet.attributes.time_ago}</em></span>
<p><span dangerouslySetInnerHTML={{__html: tweet.attributes.tweet}} /></p>
</div>
</div>
);
});
return rows;
}
render() {
if (this.checkIfLoading()) {
return (<div><i className="fa-li fa fa-spinner fa-spin"></i> One second please, while I fetch some tweets ....</div>);
}
var key = 0;
var tweets = this.buildTweets().map(function(tweetRows){
key += 1;
return (
<div key={key} className="row">
{tweetRows}
</div>
)
});
return (
<div id='tweetRowContainer'>
{tweets}
</div>
);
}
}
var tweetsElement = document.getElementById("tweets");
if (tweetsElement !== null) {
ReactDOM.render(
<Tweets source={"//" + location.hostname + "/api/v1/fetch-tweets"} />,
tweetsElement
);
}
module.exports = Tweets;
What we care about the most is the:
poll() {
console.log('I should be called ....');
...
This gets console logged the first time when the data gets fetched and then is displayed to the page, but as we can see just before this method:
startPolling() {
var self = this;
setTimeout(function() {
self.poll();
if (!self._isMounted) {
return;
}
self._timer = setInterval(self.poll, 10000);
}, 1000);
}
In theory this should be polling .... no?

Related

How to trigger event when timer count downs to 0

I have a parent component which has timer component inside it. Timer starts at 15 minutes and count downs till 0. When my timer shows time as 0 I want to trigger a submit button event, submit button is inside Quiz Component (Quiz Component is also a child component of Parent Component). I found probably I can use MutationObserver when p tag changes. I am not sure whether it's the correct and only approach or there is better way to achieve this.
Parent Component:
import React, { Component } from 'react';
import '../css/App.css'
import Quiz from './Quiz';
import Timer from './Timer';
import { connect } from 'react-redux';
import { ActionTypes } from '../redux/constants/actionTypes';
import { saveQuizAll, getQuizIndex } from '../commonjs/common.js';
const mapStateToProps = state => { return { ...state.quiz, ...state.quizAll } };
const mapDispatchToProps = dispatch => ({
onQuizLoad: payload => dispatch({ type: ActionTypes.QuizLoad, payload }),
onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});
class QuizContainer extends Component {
state = {
quizes: [
{ id: 'data/class1.json', name: 'Class 1' },
{ id: 'data/class2.json', name: 'Class 2' },
{ id: 'data/class3.json', name: 'Class 3' },
{ id: 'data/class4.json', name: 'Class 4' },
],
quizId: 'data/class1.json'
};
pager = {
index: 0,
size: 1,
count: 1
}
componentDidMount() {
console.log('componentDidMount');
this.load(this.state.quizId);
}
load(quizId, isValReload) {
console.log('In load');
let url = quizId || this.props.quizId;
if (isValReload) {
let quiz = this.props.quizAll.find(a => url.indexOf(`${a.id}.`) !== -1);
console.log('In load quiz : ', quiz);
this.pager.count = quiz.questions.length / this.pager.size;
this.props.onQuizLoad(quiz);
this.props.onPagerUpdate(this.pager);
}
else {
fetch(`../${url}`).then(res => res.json()).then(res => {
let quiz = res;
quiz.questions.forEach(q => {
q.options.forEach(o => o.selected = false);
});
quiz.config = Object.assign(this.props.quiz.config || {}, quiz.config);
this.pager.count = quiz.questions.length / this.pager.size;
this.props.onQuizLoad(quiz);
this.props.onPagerUpdate(this.pager);
});
}
}
//This event implements restriction to change class without finishing curretnly selectd class
onClassClick = (e) => {
let qus = this.props.quiz.questions;
// console.log(qus);
let isNotAllAns = qus.some((q, i) => {
var isNot = false;
if (q.answerType.id !== 3 && q.answerType.id !== 4) {
isNot = (q.options.find((o) => o.selected === true)) === undefined;
}
else {
// console.log('q', q);
isNot = ((q.answers === "" || q.answers.length === 0));
}
return isNot;
});
if (isNotAllAns) {
alert('Please complete the quiz.');
e.stopPropagation();
}
}
/*
saveQuizAll(_quizAll, _quiz) {
let allQuiz = [];
// , _quizAll, _quiz;
// if (true) {
// _quiz = this.quiz;
// _quizAll = this.quizAll;
// }
console.log(this, _quiz, _quizAll);
if (_quiz.questions.length !== 0) {
if (_quizAll.length !== undefined) {
console.log('Not Initial Setup Splice', _quiz.id);
allQuiz = _quizAll;
const qIndex = this.getQuizIndex(_quiz.id.toString());
if (qIndex > -1) {
allQuiz.splice(qIndex, 1, _quiz);
}
else {
allQuiz.splice(_quizAll.length, 0, _quiz);
// allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
}
}
else {
allQuiz[0] = _quiz;
}
return allQuiz;
// if (true) {
// this.onQuizChange(allQuiz);
// }
}
}
*/
onChange = (e) => {
// console.log(this.props.quizAll, this.props.quizAll.length);
let allQuiz = [];
allQuiz = saveQuizAll(this.props.quizAll, this.props.quiz);
//below code converted into saveQuizAll funstion
/*
if (this.props.quizAll.length !== undefined) {
console.log('Not Initial Setup Splice', this.props.quiz.id);
allQuiz = this.props.quizAll;
const qIndex = this.getQuizIndex(this.props.quiz.id.toString());
if (qIndex > -1) {
allQuiz.splice(qIndex, 1, this.props.quiz);
}
else {
allQuiz.splice(this.props.quizAll.length, 0, this.props.quiz);
// allQuiz.splice(this.props.quizAll.length-1, 0, this.props.quizAll, this.props.quiz);
}
}
else {
allQuiz[0] = this.props.quiz;
}
*/
// console.log('allQuiz Out - ', allQuiz);
this.props.onQuizChange(allQuiz);
console.log('Check QuizAll - ', this.props.quizAll);
const aQuiz = JSON.parse(JSON.stringify(this.props.quizAll));
this.setState({ quizId: e.target.value });
if (aQuiz.length !== undefined && getQuizIndex(this.props.quizAll, e.target.value) > -1) {
// console.log(aQuiz.findIndex(a => e.target.value.indexOf(`${a.id}.`) !== -1));
this.load(e.target.value, true);
}
else {
this.setState({ quizId: e.target.value });
this.load(e.target.value, false);
}
}
// getQuizIndex(qID) {
// return this.props.quizAll.findIndex(a => (qID.indexOf(`${a.id}.`) !== -1 || qID.indexOf(`${a.id}`) !== -1));
// }
render() {
return (
<div className="container">
<header className="p-2">
<div className="row">
<div className="col-6">
<h3>DADt Application</h3>
</div>
<div className="col-6 text-right">
<label className="mr-1">Select Quiz:</label>
<select onChange={this.onChange} onClick={this.onClassClick}>
{this.state.quizes.map(q => <option key={q.id} value={q.id}>{q.name}</option>)}
</select>
</div>
</div>
</header>
<Timer duration={900}/>
<Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} />
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(QuizContainer);
Here is my Timer Component
import React, { Component } from 'react'
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
seconds: 0
};
}
tick() {
this.setState((prevState) => ({
seconds: prevState.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const { duration } = this.props;
let timeLeft = duration - this.state.seconds;
timeLeft = Number(timeLeft);
let minutes = Math.floor(timeLeft % 3600 / 60);
let seconds = Math.floor(timeLeft % 3600 % 60);
let minutesDisplay = minutes > 0 ? minutes + (minutes === 1 ? " : " : " : ") : "";
let secondsDisplay = seconds > 0 ? seconds + (seconds === 1 ? "" : "") : "";
return <p className="badge badge-success">Time Left: {minutesDisplay}{secondsDisplay}</p>;
}
}
export default Timer;
Quiz Component:
import React, { Component } from 'react';
import { ActionTypes } from '../redux/constants/actionTypes';
import Review from './Review';
import Questions from './Questions';
import Result from './Result';
import { connect } from 'react-redux';
// import { saveQuizAll } from '../commonjs/common.js';
const mapStateToProps = state => { return { ...state.quiz, ...state.mode, ...state.pager, ...state.quizAll } };
const mapDispatchToProps = dispatch => ({
onSubmit: payload => dispatch({ type: ActionTypes.QuizSubmit, payload }),
onQuizChange: payload => dispatch({ type: ActionTypes.QuizAnswerAll, payload }),
onPagerUpdate: payload => dispatch({ type: ActionTypes.PagerUpdate, payload })
});
class Quiz extends Component {
move = (e) => {
let id = e.target.id;
let index = 0;
if (id === 'first')
index = 0;
else if (id === 'prev')
index = this.props.pager.index - 1;
else if (id === 'next') {
index = this.props.pager.index + 1;
}
else if (id === 'last')
index = this.props.pager.count - 1;
else
index = parseInt(e.target.id, 10);
if (index >= 0 && index < this.props.pager.count) {
let pager = {
index: index,
size: 1,
count: this.props.pager.count
};
this.props.onPagerUpdate(pager);
}
}
saveStore(e) {
let allQuiz = [];
console.log(this, e);
allQuiz = this.props.saveAll(e.props.quizAll, e.props.quiz);
console.log(allQuiz);
this.props.onQuizChange(allQuiz);
}
setMode = (e) => this.props.onSubmit(e.target.id);
// setMode(e) {
// console.log('in mode',e);this.props.onSubmit(e.target.id);
// }
renderMode() {
console.log('Inside here', this.props.mode);
if (this.props.mode === 'quiz') {
return (<Questions move={this.move} />)
} else if (this.props.mode === 'review') {
return (<Review quiz={this.props.quiz} move={this.move} />)
} else {
console.log('Before Results');
const divSel = document.querySelector('div.col-6.text-right');
// console.log('divSel', divSel);
if (divSel) {
divSel.style.display = "none";
}
return (<Result questions={this.props.quizAll || []} />)
}
}
render() {
return (
<div>
{this.renderMode()}
{(this.props.mode !== 'submit') &&
<div>
<hr />
<button id="quiz" className="btn btn-primary" onClick={this.setMode}>Quiz</button>
<button id="review" className="btn btn-primary" onClick={this.setMode}>Review</button>
<button id="submit" className="btn btn-primary" onClick={(e) => {this.setMode(e); this.saveStore(this)}}>Submit Quiz</button >
</div >}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Quiz);
I think you can have two approaches.
1. The "react" way
In the Parent component:
// ...
constructor(props) {
// ...
this.state = {
timeExpired: false
};
}
const onTimeExpired = () => {
this.setState({timeExpired: true});
}
// ...
render() {
return (
<div className="container">
{ // ... }
<Timer duration={900} onTimeExpired={onTimeExpired}/>
<Quiz quiz={this.state.quiz} quizId={this.state.quizId} saveAll={saveQuizAll} mode={this.state.mode} triggerSubmit={this.state.timeExpired} />
</div>
);
}
In the Timer component:
// ...
componentDidUpdate() {
if (this.state.seconds === this.props.duration) {
this.props.onTimeExpired();
}
}
// ...
In the Quiz component:
// ...
componentDidUpdate() {
if (this.props.triggerSubmit) {
// Do whatever you do on submit
}
}
// ...
2. The "quick and dirty" way:
In the Timer component
// ...
componentDidUpdate() {
if (this.state.seconds === this.props.duration) {
const quizForm = document.getElementById('quizFormId');
quizForm && quizForm.submit();
}
}
// ...
Provide a prop method onTimeFinished in your Timer component. Then in your render function you can add
{ !(this.props.duration-this.state.seconds) && this.props.onTimeFinished() }
Reference: React Conditional Rendering
try this:
Parent Component:
// state
state = {
triggerSubmit: false
}
// functions
doSubmit = () => {
this.setState({ triggerSubmit: true });
}
resetSubmit = () => {
this.setState({ triggerSubmit: false });
}
// jsx
<Timer duration={900} doSubmit={this.doSubmit} />
<Quiz
quiz={this.state.quiz}
quizId={this.state.quizId}
saveAll={saveQuizAll}
mode={this.state.mode}
resetSubmit={this.resetSubmit}
triggerSubmit={this.state.triggerSubmit} />
Timer Component:
// function
doSubmit = (timeLeft) => {
if (timeLeft === 0) {
this.props.doSubmit();
}
}
// jsx
<p className="badge badge-success"
onChange={() => {this.doSubmit(timeLeft)}>
Time Left: {minutesDisplay}{secondsDisplay}
</p>
Quiz Component:
// state
state = {
triggerSubmit: this.props.triggerSubmit
}
// function
triggerSubmit = () => {
if (this.state.triggerSubmit) {
your trigger submit code here...
this.props.resetSubmit();
}
}

React component does not update on set state

I am trying to conditionally render a component based on toggling of flag inside state. It looks like the state is getting updated but the component is not getting rendered. Can some one tell what is wring here. renderTree function updates the state, but render is not called then.
import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: build(),
checked: [],
expanded: [],
isDropdownExpanded: false,
keyword: ""
};
}
onCheck = checked => {
this.setState({ checked }, () => {
console.log(this.state.checked);
});
};
onExpand = expanded => {
this.setState({ expanded }, () => {
console.log(this.state.expanded);
});
};
renderTree = () => {
this.setState(
prevState => {
return {
...prevState,
isDropdownExpanded: !prevState.isDropdownExpanded
};
},
() => {
console.log(this.state);
}
);
};
onSearchInputChange = (event, data, searchedNodes) => {
this.setState(prevState => {
if (prevState.keyword.trim() && !data.value.trim()) {
return {
expanded: [],
keyword: data.value
};
}
return {
expanded: this.getAllValuesFromNodes(searchedNodes, true),
keyword: data.value
};
});
};
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
getAllValuesFromNodes = (nodes, firstLevel) => {
if (firstLevel) {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
} else {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
}
};
keywordFilter = (nodes, keyword) => {
let newNodes = [];
for (let n of nodes) {
if (n.children) {
const nextNodes = this.keywordFilter(n.children, keyword);
if (nextNodes.length > 0) {
n.children = nextNodes;
} else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.children = nextNodes.length > 0 ? nextNodes : [];
}
if (
nextNodes.length > 0 ||
n.label.toLowerCase().includes(keyword.toLowerCase())
) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
} else {
if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
}
}
return newNodes;
};
getHighlightText = (text, keyword) => {
const startIndex = text.indexOf(keyword);
return startIndex !== -1 ? (
<span>
{text.substring(0, startIndex)}
<span style={{ color: "red" }}>
{text.substring(startIndex, startIndex + keyword.length)}
</span>
{text.substring(startIndex + keyword.length)}
</span>
) : (
<span>{text}</span>
);
};
render() {
const { checked, expanded, nodes, isDropdownExpanded } = this.state;
let searchedNodes = this.state.keyword.trim()
? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
: nodes;
return (
<div>
<Dropdown fluid selection options={[]} onClick={this.renderTree} />
{isDropdownExpanded && (
<div>
<Input
style={{ marginBottom: "20px" }}
fluid
icon="search"
placeholder="Search"
iconPosition="left"
onChange={(event, data) => {
this.onSearchInputChange(event, data, searchedNodes);
}}
/>
<CheckboxTree
nodes={searchedNodes}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
showNodeIcon={true}
/>
</div>
)}
</div>
);
}
}
export default Widget;
Problem is in your shouldComponentUpdate method:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
Since renderTree only changes isDropdownExpanded value, shouldComponentUpdate always returns false
If shouldComponenetUpdate returns true then your component re-renders, otherwise it dosen't.
In your code sandbox, it can be seen that every time you click on the dropdown, the shouldComponenetUpdate returns false for this condition
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
Either you need to change the state of this variable in your renderTree function or you need to re-write this condition as
if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
return false;
}
Ciao, to force a re-render in React you have to use shouldComponentUpdate(nextProps, nextState) function. Something like:
shouldComponentUpdate(nextProps, nextState) {
return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}
When you change isDropdownExpanded value, shouldComponentUpdate will be triggered and in case return is equal to true, component will be re-rendered. Here working example (based on your codesandbox).

rendering formio custom component is not working

I have one formio js file like checkbox.js,and i register that component into my custom_formio component as follows...
checkbox.js
import _ from 'lodash';
import BaseComponent from 'formiojs/components/base/Base';
export default class CheckBoxComponent extends BaseComponent {
static schema(...extend) {
return BaseComponent.schema({
type: 'checkbox',
inputType: 'checkbox',
label: 'Checkbox',
key: 'checkbox',
dataGridLabel: true,
labelPosition: 'right',
value: '',
name: ''
}, ...extend);
}
static get builderInfo() {
return {
title: 'Checkbox',
group: 'basic',
icon: 'fa fa-check-square',
documentation: 'http://help.form.io/userguide/#checkbox',
weight: 50,
schema: CheckBoxComponent.schema()
};
}
get defaultSchema() {
return CheckBoxComponent.schema();
}
get defaultValue() {
return this.component.name ? '' : (this.component.defaultValue || false).toString() === 'true';
}
get hasSetValue() {
return this.hasValue();
}
elementInfo() {
const info = super.elementInfo();
info.type = 'input';
info.changeEvent = 'click';
info.attr.type = this.component.inputType || 'checkbox';
info.attr.class = 'form-check-input';
if (this.component.name) {
info.attr.name = `data[${this.component.name}]`;
}
info.attr.value = this.component.value ? this.component.value : 0;
return info;
}
build() {
if (this.viewOnly) {
return this.viewOnlyBuild();
}
if (!this.component.input) {
return;
}
this.createElement();
this.input = this.createInput(this.element);
this.createLabel(this.element, this.input);
if (!this.labelElement) {
this.addInput(this.input, this.element);
}
this.createDescription(this.element);
this.restoreValue();
if (this.shouldDisable) {
this.disabled = true;
}
this.autofocus();
this.attachLogic();
}
get emptyValue() {
return false;
}
isEmpty(value) {
return super.isEmpty(value) || value === false;
}
createElement() {
let className = `form-check ${this.className}`;
if (!this.labelIsHidden()) {
className += ` ${this.component.inputType || 'checkbox'}`;
}
this.element = this.ce('div', {
id: this.id,
class: className
});
this.element.component = this;
}
labelOnTheTopOrLeft() {
return ['top', 'left'].includes(this.component.labelPosition);
}
labelOnTheTopOrBottom() {
return ['top', 'bottom'].includes(this.component.labelPosition);
}
setInputLabelStyle(label) {
if (this.component.labelPosition === 'left') {
_.assign(label.style, {
textAlign: 'center',
paddingLeft: 0,
});
}
if (this.labelOnTheTopOrBottom()) {
_.assign(label.style, {
display: 'block',
textAlign: 'center',
paddingLeft: 0,
});
}
}
setInputStyle(input) {
if (!input) {
return;
}
if (this.component.labelPosition === 'left') {
_.assign(input.style, {
position: 'initial',
marginLeft: '7px'
});
}
if (this.labelOnTheTopOrBottom()) {
_.assign(input.style, {
width: '100%',
position: 'initial',
marginLeft: 0
});
}
}
createLabel(container, input) {
const isLabelHidden = this.labelIsHidden();
let className = 'control-label form-check-label';
if (this.component.input
&& !this.options.inputsOnly
&& this.component.validate
&& this.component.validate.required) {
className += ' field-required';
}
this.labelElement = this.ce('label', {
class: className
});
this.addShortcut();
const labelOnTheTopOrOnTheLeft = this.labelOnTheTopOrLeft();
if (!isLabelHidden) {
// Create the SPAN around the textNode for better style hooks
this.labelSpan = this.ce('span');
if (this.info.attr.id) {
this.labelElement.setAttribute('for', this.info.attr.id);
}
}
if (!isLabelHidden && labelOnTheTopOrOnTheLeft) {
this.setInputLabelStyle(this.labelElement);
this.setInputStyle(input);
this.labelSpan.appendChild(this.text(this.component.label));
this.labelElement.appendChild(this.labelSpan);
}
this.addInput(input, this.labelElement);
if (!isLabelHidden && !labelOnTheTopOrOnTheLeft) {
this.setInputLabelStyle(this.labelElement);
this.setInputStyle(input);
this.labelSpan.appendChild(this.text(this.addShortcutToLabel()));
this.labelElement.appendChild(this.labelSpan);
}
this.createTooltip(this.labelElement);
container.appendChild(this.labelElement);
}
createInput(container) {
if (!this.component.input) {
return;
}
const input = this.ce(this.info.type, this.info.attr);
this.errorContainer = container;
return input;
}
set dataValue(value) {
const setValue = (super.dataValue = value);
if (this.component.name) {
_.set(this.data, this.component.key, setValue === this.component.value);
}
return setValue;
}
get dataValue() {
const getValue = super.dataValue;
if (this.component.name) {
_.set(this.data, this.component.key, getValue === this.component.value);
}
return getValue;
}
get key() {
return this.component.name ? this.component.name : super.key;
}
getValueAt(index) {
if (this.component.name) {
return this.inputs[index].checked ? this.component.value : '';
}
return !!this.inputs[index].checked;
}
getValue() {
const value = super.getValue();
if (this.component.name) {
return value ? this.setCheckedState(value) : this.setCheckedState(this.dataValue);
}
else {
return value;
}
}
setCheckedState(value) {
if (!this.input) {
return;
}
if (this.component.name) {
this.input.value = (value === this.component.value) ? this.component.value : 0;
this.input.checked = (value === this.component.value) ? 1 : 0;
}
else if (value === 'on') {
this.input.value = 1;
this.input.checked = 1;
}
else if (value === 'off') {
this.input.value = 0;
this.input.checked = 0;
}
else if (value) {
this.input.value = 1;
this.input.checked = 1;
}
else {
this.input.value = 0;
this.input.checked = 0;
}
if (this.input.checked) {
this.input.setAttribute('checked', true);
}
else {
this.input.removeAttribute('checked');
}
return value;
}
setValue(value, flags) {
flags = this.getFlags.apply(this, arguments);
if (this.setCheckedState(value) !== undefined) {
return this.updateValue(flags);
}
}
getView(value) {
return value ? 'Yes' : 'No';
}
destroy() {
super.destroy();
this.removeShortcut();
}
}
custom_formio.component.ts
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { FormioAuthService } from 'angular-formio/auth';
import { Formio } from 'formiojs';
import { CheckBoxComponent }from './Checkbox'
#Component({
selector: 'app-custom_formio',
templateUrl: './custom_formio.component.html',
styleUrls: ['./custom_formio.component.less']
})
export class CustomFormioComponent {
title = 'app';
offlineCount = 0;
offlineMode: any = null;
offlineError = '';
constructor(private auth: FormioAuthService, private router: Router) {
this.auth.onLogin.subscribe(() => {
this.router.navigate(['/home']);
});
this.auth.onLogout.subscribe(() => {
this.router.navigate(['/auth/login']);
});
this.auth.onRegister.subscribe(() => {
this.router.navigate(['/home']);
});
Formio.registerComponent('custom_formio', CheckBoxComponent);
}
}
but i don't know what code i have to write in custom_formio.component.html to render the custom checkbox component into my application.
custom_formio.component.html
<div class="m-content">
<div class="m-portlet m-portlet--mobile">
<div class="m-portlet__body">
<div id="custom_formio"></div>
</div>
</div>
</div>
but it is not working,can any one help...
It's the ES5 class CheckBoxComponent that should contain the custom HTML you want to rendrer providing it in Build() using super class BaseComponent method renderTemplate().
Custom formio components should be created with native html so you can not use angular components.
But, there is a solution using new angular feature (Angular elements aka web compoenents)
Check-out avan2s's angular project
https://github.com/avan2s/angular-app-starterkit
useful: https://github.com/formio/angular-formio/issues/201

Multiple async XMLHttpRequests in React

I did an example of multi async XMLHttpReqest based on examples i found:
var URL=["https://api.github.com/users/github","https://api.github.com/users/github/repos"];
var xhr = [null, null];
for (var i = 0; i < 2; i++) {
(function(i) {
xhr[i] = new XMLHttpRequest();
xhr[i].open("GET", URL[i], true);
xhr[i].onload = function (e) {
if (xhr[i].readyState === 4) {
if (xhr[i].status === 200) {
console.log(xhr[i].responseText);
} else {
console.error(xhr[i].statusText);
}
}
};
xhr[i].onerror = function (e) {
console.error(xhr[i].statusText);
};
xhr[i].send(null);
})(i);
}
Thing is that i am having a problem with implementing it in react as i can not assign value with this.setState({json_objs[i]:JSON.parse(xhr[i].responseText}) to object array.
This is my react code which is not working due problems with assigning value:
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
class ImageViewer extends React.Component {
constructor() {
super();
this.state = {
json_objs : [null,null],
links:["https://api.github.com/users/github/repos","https://api.github.com/users/github"]
}
}
componentDidMount() {
var xhr = [null, null]
for (var i = 0; i < 2; i++) {
(function(i) {
xhr[i] = new XMLHttpRequest();
xhr[i].open("GET", this.state.link, true);
xhr[i].onload = function (e) {
if (xhr[i].readyState === 4) {
if (xhr[i].status === 200) {
this.setState({ json_objs[i]:JSON.parse(xhr[i].responseText)});
} else {
console.error(xhr[i].statusText);
}
}
}.bind(this);
xhr[i].onerror = function (e) {
console.error(xhr[i].statusText);
};
xhr[i].send(null);
})(i);
}
render() {
if (!this.state.json_objs[0] || !this.state.json_objs[1]) {
return <div>Loading...</div>;
}
return(
<div>
<div><h1>{this.state.json_objs[1].email}</h1></div>
{this.state.json_objs[0].sort(function(a, b){var keyA = new Date(a.updated_at),keyB = new Date(b.updated_at);if(keyA > keyB) return -1;if(keyA < keyB) return 1;return 0;}).map((obj, index) => {
return (
<div key={index}>
<div>{obj.name}</div>
<div>{(new Date(obj.updated_at)).toString()}</td>
</div>
)
})}
</div>
)
}}
ReactDOM.render(<ImageViewer />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I have been reading about modyfing arrays in react but i am having problems with implementing it.
I think Promise's would be helpful here. Wrap each XHR request in a Promise, use Promise.all() to wait for both of them complete, then set your results into state (it will resolve with an array of response objects):
Promise.all([0, 1].map((index) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("GET", this.state.links[index], true)
xhr.onload = function() {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else {
reject(xhr.statusText)
}
}
})
})).then((json_objs) => {
this.setState({ json_objs }, () => {
console.log('json_objs => ', this.state.json_objs)
})
}).catch(err => console.error(err))

React child components won't unmount (reappear)

I'm running stuck on a problem in React (files provided below). I'm trying to generate a set of child components and would like to be able to remove these. I have a state parameter this.state.lipids which contains an array of objects, using these I generate several <DataListCoupledInput /> components inside <RatioCoupledDataLists />. The components are generated and do what their supposed to do with the exception of refusing to be removed.
When I alter my this.state.lipids array by removing an element via removeLipid(index) it triggers a re-render that for some reason magically generates the same <DataListCoupledInput /> component that I just removed.
I tried moving the responsibility up to the parent level of <RatioCoupledDataLists /> but the problem remains.
Please help me.
RatioCoupledDataLists.jsx
class RatioCoupledDataLists extends React.Component {
constructor(props) {
super(props)
this.state = {
lipids: [{value: null, upperLeaf: 1, lowerLeaf: 1}]
}
this.registerLipid = this.registerLipid.bind(this);
this.addLipid = this.addLipid.bind(this);
this.removeLipid = this.removeLipid.bind(this);
}
registerLipid(index, lipid) {
var lipids = [];
lipids = lipids.concat(this.state.lipids);
lipids[index] = lipid;
this.setState({lipids: lipids});
this.state.lipids[index] = lipid;
}
addLipid() {
var mapping = {value: null, upperLeaf: 1, lowerLeaf: 1};
this.setState({lipids: this.state.lipids.concat(mapping)});
}
removeLipid(index) {
var lipids = [];
lipids = lipids.concat(this.state.lipids);
lipids.splice(index, 1);
this.setState({lipids: lipids});
}
render() {
var itemIsLast = function(index, len) {
if (index + 1 === len) {
// item is last
return true;
}
return false;
}
var makeLipidSelects = function() {
var allLipids = [];
for (var i = 0; i < this.state.lipids.length; i++) {
// Add RatioCoupledDataList
allLipids.push(
<DataListCoupledInput
fr={this.registerLipid}
list="lipids"
description="Membrane lipids"
add={this.addLipid}
remove={this.removeLipid}
key={i}
id={i}
isFinal={itemIsLast(i, this.state.lipids.length)}
lipid={this.state.lipids[i]} />);
}
return allLipids;
}
makeLipidSelects = makeLipidSelects.bind(this);
return <div className="category">
<div className="title">Custom Membrane</div>
<DataList data={lipids} id="lipids" />
{makeLipidSelects()}
</div>
}
}
DataListCoupledInput.jsx
class DataListCoupledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.lipid.value,
active: this.props.isFinal,
upperLeaf: this.props.lipid.upperLeaf,
lowerLeaf: this.props.lipid.lowerLeaf
}
this.handleChange = this.handleChange.bind(this);
this.registerLeafletValue = this.registerLeafletValue.bind(this);
this.registerLipid = this.registerLipid.bind(this);
}
registerLipid(lipid) {
this.props.fr(this.props.id, lipid);
}
handleChange(event) {
this.registerLipid({value: event.target.value, upperLeaf: this.state.upperLeaf, lowerLeaf: this.state.lowerLeaf});
this.setState({value: event.target.value});
}
registerLeafletValue(leaflet) {
if (leaflet.name === "upperRatio") {
this.registerLipid({value: this.state.value, upperLeaf: leaflet.value, lowerLeaf: this.state.lowerLeaf});
this.setState({upperLeaf: leaflet.value});
}
if (leaflet.name === "lowerRatio") {
this.registerLipid({value: this.state.value, upperLeaf: this.state.upperLeaf, lowerLeaf: leaflet.value});
this.setState({lowerLeaf: leaflet.value});
}
}
render() {
var canEdit = function() {
if (this.props.isFinal === true) {
return <input onBlur={this.handleChange} list={this.props.list} />;
} else {
return <div className="description">{this.state.value}</div>
}
}
canEdit = canEdit.bind(this);
var addOrRemove = function() {
if (this.props.isFinal !== true) {
return <div className="remove" onClick={() => {this.props.remove(this.props.id)}}><div>-</div></div>;
} else {
return <div className="add" onClick={this.props.add}><div>+</div></div>;
}
}
addOrRemove = addOrRemove.bind(this);
return (
<div className="input-wrap">
<div className="input">
{canEdit()}
</div>
<div className="abundance">
<DynamicNumber
min={this.props.min}
max={this.props.max}
step={this.props.step}
name="lowerRatio"
value={this.state.upperLeaf}
fr={this.registerLeafletValue}
type="linked" />
<div className="to">:</div>
<DynamicNumber
min={this.props.min}
max={this.props.max}
step={this.props.step}
name="upperRatio"
value={this.state.lowerLeaf}
fr={this.registerLeafletValue}
type="linked" />
</div>
{addOrRemove()}
<div className="description">{this.props.description}</div>
</div>
)
}
}

Categories