Related
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();
}
}
I have a tsx file which creates a table of comments. When the page is rendered, an array of information representing comments is gathered. A Set containing the indexes of the comments in the array, which is part of the state, determines whether a link under the comment reads 'show more' or 'show less'. Once that link is clicked, the index of the comment is added to the Set, and the state is updated with the addition of that index to the Set, if the Set contains the index of a comment when the state is updated, it should read 'show less'. The problem is when I click that link, it changes the link of each list element in the table, not just one.
import * as React from "react";
import { LocalizationInfo } from "emt-localization";
import { DateHandler } from "../handlers/date-handler";
import { UserIcon } from "./user-icon";
import { OnDownloadDocument } from "../models/generic-types";
import { getUserString } from "../models/user";
import { ClaimAction, ActionDetails, ReasonActionDetails, CommentDetails, DocumentDetails } from "../models/action";
const stateChangeActions = new Set<string>([
'Dispute',
'Rejection',
'SetUnderReview',
'Approval',
'Recall',
'Submission',
'RequestPartnerAction'
]);
const fiveMinutes = 5 * 60 * 1000;
const underscoreRegex = /_[^_]*_/g;
const actionTypeClassMap: {[actionType: string]: string} = {
'Submission': 'success',
'RequestPartnerAction': 'warn',
'Rejection': 'warn',
'Approval': 'success',
'Recall': 'warn',
'Dispute': 'warn',
'SetUnderReview': 'info',
'CustomerConsentDeclined': 'rejected'
};
const maxShortLength = 100;
interface ActionsProps {
readonly localization: LocalizationInfo;
readonly actions: ClaimAction[];
readonly onDownloadDocument: OnDownloadDocument;
readonly isInternalFacing: boolean;
readonly isHistoryPaneDisplay: boolean;
}
class ActionsState {
readonly expandedComments = new Set<number>();
}
export class Actions extends React.Component<ActionsProps, ActionsState> {
constructor(props: ActionsProps) {
super(props);
this.state = new ActionsState();
}
render():JSX.Element {
const loc = this.props.localization;
const isInternalFacing = this.props.isInternalFacing;
const toTime = (action:ClaimAction) => new Date(action.timeStamp).getTime();
const sortBy = (a:ClaimAction, b:ClaimAction) => toTime(b) - toTime(a);
const actions = this.props.actions.filter(action => {
if (isDocumentDetails(action.details)) {
if (action.actionType == 'DocumentSubmission' && action.details.documentType == 'Invoice') {
return false;
}
}
return true;
});
actions.sort(sortBy);
const grouped = groupActions(actions);
return (
<ul className={`claim-actions ${this.props.isHistoryPaneDisplay ? '' : 'user-comment-box'}`}>
{grouped.map((actions, index) => {
let actionClass = '';
actions.forEach(action => {
actionClass = actionClass || actionTypeClassMap[action.actionType];
});
const first = actions[0];
const icon = actionClass == 'success' ?
sequenceIcon('complete') :
actionClass == 'warn' ?
sequenceIcon('action-required') :
actionClass == 'rejected' ?
sequenceIcon('rejected') :
actionClass == 'info' ?
sequenceIcon('editing') :
<UserIcon
user={first.user}
isInternalFacing={isInternalFacing}
/>;
const elements = actions.map((action, actionIndex) => this.renderAction(action, actionIndex));
return (
<li className={actionClass} key={index}>
{icon}
<div className="win-color-fg-secondary">
<span className="claim-action-name win-color-fg-primary">
{ getUserString(first.user, isInternalFacing) }
</span>
<span className="text-caption">
{loc.piece("HistoryItemTitle", 0)}
{DateHandler.friendlyDate(first.timeStamp, true)}
</span>
</div>
{elements}
</li>
)
})}
</ul>
)
}
private renderAction(action:ClaimAction, actionIndex:number):JSX.Element|null {
const strings = this.props.localization.strings;
if (action.actionType == 'AddComments' || action.actionType == 'AddInternalComments') {
return this.renderComment((action.details as CommentDetails).comments, actionIndex);
}
const document = isDocumentDetails(action.details) ? action.details : null;
const documentLink = document ?
(key:number) =>
<a
key={key}
onClick={() => this.props.onDownloadDocument(document.documentId, document.name)}>
{document.name}
</a>
: null;
const locKey = `History_${action.actionType}`;
const localizedFlat = strings[locKey] || "";
const localized = replaceUnderscores(localizedFlat, documentLink);
const reason = (action.actionType === 'RequestPartnerAction' || action.actionType === 'Rejection') && action.details ? (action.details as ReasonActionDetails).reasonCode : '';
const reasonString = reason.charAt(0).toUpperCase() + reason.slice(1)
if (localized) {
return (
<div key={actionIndex}>
<div className="claim-action">
<span className="text-caption">{localized}</span>
</div>
<div className="claim-action">
{ reasonString && <span className="text-caption"><strong>{strings['ReasonLabel']}</strong>{` ${strings[reasonString]}`}</span> }
</div>
</div>
);
}
console.error(`Unknown action type ${action.actionType}`);
return null;
}
private renderComment(comment: string, actionIndex: number): JSX.Element {
const strings = this.props.localization.strings;
const canShorten = comment.length > maxShortLength;
const shouldShorten = canShorten && !this.state.expandedComments.has(actionIndex);
const shortened = shouldShorten ?
comment.substring(0, maxShortLength) + "\u2026" :
comment;
const paragraphs = shortened
.split('\n')
.map(s => s.trim())
.filter(s => s);
const elements = paragraphs.map((comment, i) =>
<div className="claim-comment" key={i}>
{comment}
</div>
);
const toggle = () => {
const next = new Set<number>(this.state.expandedComments);
if (next.has(actionIndex)) {
next.delete(actionIndex)
}
else {
next.add(actionIndex);
}
this.setState({ expandedComments: next });
}
const makeLink = (locKey:string) =>
<a
onClick={toggle}>{strings[locKey]}</a>;
const afterLink = canShorten ?
shouldShorten ?
makeLink('ShowMore') :
makeLink('ShowLess') :
null;
return (
<React.Fragment key={actionIndex}>
{elements}
{afterLink}
</React.Fragment>
);
}
}
// Function groups actions together under some conditions
function groupActions(actions:ClaimAction[]):ClaimAction[][] {
const grouped:ClaimAction[][] = [];
actions.forEach(action => {
if (grouped.length) {
const lastGroup = grouped[grouped.length - 1];
const timeDifference = new Date(lastGroup[0].timeStamp).getTime() - new Date(action.timeStamp).getTime();
if (stateChangeActions.has(lastGroup[0].actionType) && action.actionType == 'AddComments' && timeDifference < fiveMinutes) {
lastGroup.push(action);
return;
}
}
grouped.push([action]);
});
return grouped;
}
function isDocumentDetails(details:ActionDetails|null): details is DocumentDetails {
return !!details && (details.$concreteClass == 'InvoiceActionDetails' || details.$concreteClass == 'DocumentActionDetails');
}
function sequenceIcon(className: string):JSX.Element {
return (
<div className="sequence sequence-status claims-icon">
<div className={`step ${className}`} />
</div>
);
}
function replaceUnderscores(str: string, documentLink: ((k:number)=>JSX.Element)|null, startKey: number=0):JSX.Element[] {
if (!str) {
return [];
}
const match = underscoreRegex.exec(str);
if (!match) {
return replaceDocumentLink(str, documentLink, startKey);
}
const firstText = str.substring(0, match.index);
const middleText = match[0].substring(1, match[0].length - 1);
const lastText = str.substring(match.index + match[0].length);
const first = replaceUnderscores(firstText, documentLink, startKey);
const middle = [<strong className={'claims-emphasis'} key={startKey + first.length}>{middleText}</strong>];
const last = replaceUnderscores(lastText, documentLink, startKey + first.length + 1);
return first.concat(middle, last);
}
function replaceDocumentLink(str: string, documentLink: ((k:number)=>JSX.Element)|null, startKey: number=0):JSX.Element[] {
const replaceIndex = str.indexOf('{0}');
if (replaceIndex >= 0 && documentLink) {
return [
<React.Fragment key={startKey}>{str.substring(0, replaceIndex)}</React.Fragment>,
documentLink(startKey+1),
<React.Fragment key={startKey + 2}>{str.substring(replaceIndex+3)}</React.Fragment>
];
}
return [<React.Fragment key={startKey}>{str}</React.Fragment>];
}
I am a beginner at ReactJs.
And I have RangeError to run this code.
I have tried to solve this problem myself, but I couldn't do.
After a lot of testing, I just knew we had errors in this file.
Here is my code in the file.
import React from 'react'
import cookie from 'react-cookie';
import { connect } from 'react-redux';
import { fetchPieData, resetPieData } from '../actions/panel.js';
import PieChart from '../components/PieChart';
import { getCookieDomain } from '../utils/domain';
import PendingOverlay from '../components/PendingOverlay';
import cookiefetch from '../utils/userutils';
import {
companyDropdown as companyDropdownAction,
getCompaniesDropdown,
getCompany
} from '../actions/companies.js';
import {
getFileDropdown,
fileDropdown as fileDropdownAction,
fileSearchDropdown as fileSearchDropdownAction
} from '../actions/files.js';
const cookieDomain = getCookieDomain();
import {
toggleExpiringModal
} from '../actions/ui.js';
import {
checkdatediff
} from '../utils/numbers.js';
import '../assets/library.less'
class Panel extends React.Component {
constructor(props) {
super(props);
this.state = {
fileDropdownTitle: 'FILE',
setFileByCookie: true
};
}
componentWillMount() {
const { dispatch } = this.props;
var roleCookie = cookie.load('role');
roleCookie = roleCookie.toLowerCase();
const accountId = cookiefetch('accountId','');
var accid = accountId.toLowerCase();
const overrideCompany = cookiefetch('overrideCompany', '');
if(overrideCompany != '') {
var title = "FILE ("+overrideCompany+")";
this.setState({fileDropdownTitle: title});
} else {
this.setState({fileDropdownTitle: 'FILE'});
}
var roleCookie = roleCookie.toLowerCase();
if (roleCookie == 'administrator' || roleCookie == "adminread") {
dispatch(getCompaniesDropdown());
} else {
dispatch(getCompany(accid));
dispatch(getFileDropdown(accid));
//dispatch(companyDropdownAction(false, accid, accid));
}
}
componentDidUpdate(nextProps, nextState) {
const { dispatch } = this.props;
const { selectedCompany } = this.props.companies;
const { selectedValue } = this.props.companies.companyDropdown;
const nextSelectedValue = nextProps.companies.companyDropdown.selectedValue;
const nextSelectedDisplay = nextProps.companies.companyDropdown.selectedDisplay;
const fileDropdown = this.props.files.fileDropdown;
const nextFileDropdown = nextProps.files.fileDropdown;
const cookieDomain = getCookieDomain();
if (selectedValue == "" && selectedValue != nextSelectedValue) {
cookie.save('selectedCompany', {selectedDisplay: nextSelectedDisplay, selectedValue: nextSelectedValue, Petstore: nextProps.companies.selectedCompany.activePetstore2}, {domain: cookieDomain});
cookie.save('selectedFile', {selectedDisplay: "Select a FILE", selectedValue: ""}, {domain: cookieDomain});
dispatch(getFileDropdown(nextSelectedValue));
dispatch(fileSearchDropdownAction(false, "Select a FILE", "", ""))
} else if(typeof nextSelectedValue == 'string' && selectedValue != nextSelectedValue) {
cookie.save('selectedCompany', {selectedDisplay: nextSelectedDisplay, selectedValue: nextSelectedValue, Petstore: nextProps.companies.selectedCompany.activePetstore2}, {domain: cookieDomain});
cookie.save('selectedFile', {selectedDisplay: "Select a FILE", selectedValue: ""}, {domain: cookieDomain});
//dispatch(resetPieData());
dispatch(getFileDropdown(nextSelectedValue));
dispatch(fileDropdownAction(false, "Select a FILE", "", ""))
}
else if(nextState.setFileByCookie && typeof nextProps.companies.selectedCompany != 'undefined') {
var nextCompany = nextProps.companies.selectedCompany;
var companyOptions = {isActivePetstore: nextCompany.activePetstore, activePetstore2: nextCompany.activePetstore2,
isActiveGeneral: true, isActivePoc: nextCompany.activePoc, isActiveFormat: nextCompany.activeFormat,
isActiveSiteId: nextCompany.activeSiteId,
subAssembly: nextCompany.subAssembly};
if (nextFileDropdown.selectedDisplay == "List") {
dispatch(fetchPieData(nextFileDropdown.selectedValue, true, companyOptions));
} else if (nextFileDropdown.selectedValue != ""){
dispatch(fetchPieData(nextFileDropdown.selectedValue, false, companyOptions));
}
}
else if (nextFileDropdown.selectedValue != "" && fileDropdown.selectedValue != nextFileDropdown.selectedValue ) {
cookie.save('selectedFile', {selectedDisplay: nextFileDropdown.selectedDisplay, selectedValue: nextFileDropdown.selectedValue}, {domain: cookieDomain});
var companyOptions = {isActiveGeneral: true,isActivePetstore: selectedCompany.activePetstore, activePetstore2: selectedCompany.activePetstore,
isActiveGeneral: selectedCompany.activeGeneral, isActivePoc: selectedCompany.activePoc, isActiveFormat: selectedCompany.activeFormat,
isActiveSiteId: selectedCompany.activeSiteId,
subAssembly: selectedCompany.subAssembly};
if (nextFileDropdown.selectedDisplay == "List") {
dispatch(fetchPieData(nextFileDropdown.selectedValue, true, companyOptions));
} else {
dispatch(fetchPieData(nextFileDropdown.selectedValue, false, companyOptions));
}
}
}
toggleExpiringModal() {
const { dispatch, ui } = this.props;
if (ui.toggleExpiringModal) {
dispatch(toggleExpiringModal(false));
cookie.remove('didLogin', {domain: getCookieDomain()});
}
}
render () {
var companyDropdownComponent = null;
var filesDropdownComponent = null;
var dataType;
var expirationModal = null;
var expirationDate, daysAway, didLogin;
const { ui, companies } = this.props;
const customStyles = {
content : {
top : '40%',
left : '50%',
right : 'auto',
bottom : 'auto',
transform : 'translate(-50%, -50%)',
overflowY : 'hidden',
padding : '20px 20px 30px'
}
};
const isActivePetstore = cookiefetch('isActivePetstore', false);
const roleCookie = cookiefetch('role');
var role = roleCookie.toLowerCase();
const accountId = cookiefetch('accountId', '');
var accid = accountId.toLowerCase();
const overrideCompany = cookiefetch('overrideCompany','');
const { companiesDropdownData, companyDropdown, selectedCompany } = this.props.companies;
const { fileDropdown, filesDropdownData } = this.props.files;
const { pieData } = this.props.panel;
const { table } = this.props.data;
var companyOptions = null;
if (fileDropdown.selectedDisplay == "List") {
dataType = "masterList";
} else {
dataType = "file";
}
if ((role == "administrator" || role == 'adminread') && overrideCompany == '') {
companyDropdownComponent = (
<Dropdown
errorMessage='Please select a company'
error={false}
title="Company"
open={companyDropdown.display}
selectedDisplay={companyDropdown.selectedDisplay}
selectedValue={companyDropdown.selectedValue}
options={companiesDropdownData}
disabled="edit"
onClickAction={companyDropdownAction}
/>
);
}
if (companyDropdown.selectedValue != "") {
filesDropdownComponent = (
<DropdownFiles
errorMessage='Please select a FILE'
error={false}
title={this.state.fileDropdownTitle}
open={fileDropdown.display}
selectedDisplay={fileDropdown.selectedDisplay}
selectedValue={fileDropdown.selectedValue}
options={filesDropdownData}
disabled="edit"
onClickAction={fileDropdownAction}
/>
);
}
if (typeof selectedCompany != 'undefined') {
companyOptions = {isActivePetstore: selectedCompany.activePetstore, activePetstore2: selectedCompany.activePetstore,
isActiveGeneral: true, isActivePoc: true, isActiveFormat: selectedCompany.activeFormat,
isActiveSiteId: selectedCompany.activeSiteId,
subAssembly: selectedCompany.subAssembly};
}
for(var i = 0; i < companies.companies.length; i++) {
if (accountId === companies.companies[i].accountId) {
var dateInMiliseconds = companies.companies[i].expirationDate;
expirationDate = new Date(dateInMiliseconds);
expirationDate = companies.companies[i].expirationDate;
}
}
{ expirationDate != undefined ? daysAway = checkdatediff(expirationDate) : null }
{expirationDate != undefined ? didLogin = cookie.load('didLogin') : null }
if (daysAway < 1 && ui.toggleExpiringModal == true && didLogin) {
expirationModal = (
<Modal
isOpen={ui.toggleExpiringModal}
shouldCloseOnOverlayClick={true}
style={customStyles}
contentLabel="Modal"
>
<div className="custom-modal">
<div className="header-center">Notice</div>
<div className="content">
<p>test</p>
<p>test in <span id="days">{daysAway}</span> days.</p>
<p>test.</p>
</div>
<div className="buttons">
<div className="close" onClick={() => this.toggleExpiringModal()}>Ok</div>
</div>
</div>
</Modal>
);
}
return (
<div id="panel">
{ !isActivePetstore ? <Notification>test</Notification> : null }
{companyDropdownComponent}
{filesDropdownComponent}
<IconLink to="/files" title="Link to Files" type="table"/>
{ pieData != "empty" && pieData != "pending" && fileDropdown.selectedValue != "" ? <PieChart data={pieData} dataType={dataType} table={table} companyOptions={companyOptions}/> : null }
{ pieData != "empty" && pieData == "pending" ? <PendingOverlay/> : null }
{expirationModal}
</div>
)
}
}
function select(state) {
return {
companies: state.companies,
files: state.files,
createFile: state.createFile,
masterList: state.masterList,
panel: state.panel,
data: state.data,
ui: state.ui
}
}
export default connect(select)(Panel);
After running we have above error.
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
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?