Related
When componentDidUpdate occurs I want to change attribute for example label of an element. I'm able to change the value of an element but not the label of it.
this is the element I'm trying to change which is rendered already. Should I re-render it?
var _phone = {
type: 'text',
name: 'phone',
label: 'Phone#:',
value: values.phone || '',
onChange: this.onChange
};
var _address = {
type: 'text',
name: 'address',
label: 'Address:',
value: values.address || '',
onChange: this.onChange
};
some part is here:
componentDidUpdate = function(prevProps, prevState) {
const { values } = this.state;
if (JSON.stringify(prevState.values) !== JSON.stringify(values)) {
if (
values.lkp_language != '' &&
values.lkp_language !== prevState.values.lkp_language &&
values.lkp_language * 1 == 1
) {
_lkp_participant_type.label = 'test'
var comps = {
_lkp_participant_type: _lkp_participant_type
}
this.setState((prevState) => {
return { ...prevState,
values: { ...prevState.values,
...comps
}
}
});
}
}
}
I have built a fairly large react-redux application. In one component I have added an undo feature. Tracing the state all the way through it is definitely being updated and not mutated. It even re-renders the component and all child components. However on the page the component position is not modified until I click the component to either move it again or highlight it.
I have definitely verified that this is not a case of mutated state and have stepped through all the redux code to ensure that the shallow equality fails and I have added breakpoints on the main component and the child component which should be moved.
I will add code if you want to see it but my question is why a re-rendered component in React would not re-render in the updated position on the screen, even though the top and left coordinates have definitely changed?
Edit adding code
//layout.js
const mapLayoutDispatchToProps = (dispatch) => {
//add action creators here - by reference?
return {
Layout_Set_Current_Site: (siteId) => { dispatch(Layout_Set_Current_Site(siteId)) },
Layout_Get_Sites: () => { dispatch(Layout_Get_Sites()) },
Layout_Get_Map_Background: (siteId, callback) => { dispatch(Layout_Get_Map_Background(siteId, callback)) },
Layout_Get_UserImages: (deskId) => { dispatch(Layout_Get_UserImages(deskId)) },
Layout_Create_Desk: (type, siteId, height, width) => { dispatch(Layout_Create_Desk(type, siteId, height, width)) },
Layout_Restore_All: () => { dispatch(Layout_Restore_All()) },
Layout_Undo_Change: (callback) => { dispatch(Layout_Undo_Change(callback)) },
Layout_Redo_Change: () => { dispatch(Layout_Redo_Change()) },
Layout_Fetch_Desks: (siteId) => { dispatch(Layout_Fetch_Desks(siteId)) },
Layout_Get_Desk_Types: () => { dispatch(Layout_Get_Desk_Types()) },
Layout_Set_Current_Desk: (desk) => { dispatch(Layout_Set_Current_Desk(desk)) }
};
}
getDesks = () => {
console.log("Layout.getDesks");
// const d = this.props.layout_moveData.desks.present;
const desks = clone(this.props.layout_moveData.present);
return desks;
}
handleKeyPress = (e) => {
console.log("Layout.handleKeyPress");
if (this.state.edit) {
switch (e.code) {
case 'KeyZ':
if (e.ctrlKey) {
this.props.Layout_Undo_Change();
e.cancelBubble = true;
// this.forceUpdate();
}
break;
case 'KeyY':
if (e.ctrlKey) {
//this.props.Layout_Redo_Change();
UndoMove.redo();
e.cancelBubble = true;
}
break;
default:
break;
}
}
}
buildDesks = () => {
const desks = this.getDesks();
let ret = desks.map((desk, index) =>
<Desk
key={index}
desks={desks}
index={index}
deskTypes={this.props.layout.deskTypes}
scale={this.getScale()}
editable={this.state.edit}
/>
);
return ret;
}
render=()=>{
return (
<div>
<Row>
<Col sm={1}>
{this.showAdmin()}
</Col>
<Col sm={10}>
{this.state.details}
</Col>
</Row>
<Row>
<Col sm={1}>
<select onChange={(e) => this.changeMap(e.target)}>
{this.buildMapOptions()}
</select>
</Col>
<Col sm={10} id="Layout_Map_Container">
{this.buildMap()}
{this.buildDesks()}
</Col>
</Row >
{this.showStatus()}
</div>
);
}
}
//desks.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Draggable from '../../Elements/Draggable';
import {
Layout_Clear_Desk,
Layout_Delete_Desk,
Layout_Update_Desk_Data,
Layout_Set_Current_Desk
} from '../../../redux/Creators/Layout_Creator';
import '../../../shared/styles/layout.css';
const clone = require('rfdc')();
const mapDesksStateToProps = (state) => {
return {
layout: state.layout,
layout_moveData: state.layout_moveData
}
}
const mapDesksDispatchToProps = (dispatch) => {
return {
Layout_Clear_Desk: (deskId) => { dispatch(Layout_Clear_Desk(deskId)) },
Layout_Delete_Desk: (deskId) => { dispatch(Layout_Delete_Desk(deskId)) },
Layout_Update_Desk_Data: (desk, deskId) => { dispatch(Layout_Update_Desk_Data(desk, deskId)) },
Layout_Set_Current_Desk: (deskId) => { dispatch(Layout_Set_Current_Desk(deskId)) }
}
}
class Desk extends Component {
constructor(props) {
super(props);
this.state = {
desk: clone(props.desks[props.index]),
desks: clone(props.desks)
}
}
rightClick = (e, deskId) => {
if (this.props.editable) {
const desk = this.state.desk;
let rotation = parseInt(desk.rotation);
rotation += 90;
if (rotation >= 360) rotation -= 360;
desk.rotation = rotation;
this.props.Layout_Set_Current_Desk(desk);
this.props.Layout_Update_Desk_Data(desk);
}
}
updateProperties = (data) => {
let string = `Top: ${data.top}, Left:${data.left}`;
// data = this.state.details + ', ' + data
this.setState({ details: string });
}
mouseUp = (e, deskId, data) => {
console.log("Layout.mouseUp");
const desks = clone(this.state.desks);
// let desk = ;
if (data.dragged && this.props.editable) {
// this.clickDesk(e, deskId);
const scale = this.props.scale;
const newX = parseInt(data.left / scale);
const newY = parseInt(data.top / scale);
desks[deskId].x = newX + ""; //convert to strings
desks[deskId].y = newY + "";
console.log(this.state.desks);
console.log(desks);
this.props.Layout_Update_Desk_Data(desks, deskId);
}
else {
this.clickDesk(e, deskId);
}
}
clickDesk = (e, deskId) => {
if (deskId !== null && deskId !== undefined && deskId !== false) {
this.props.Layout_Set_Current_Desk(this.state.desk);
}
else {
this.props.Layout_Set_Current_Desk(null);
}
}
render() {
let deskImg = null;
// const desk = this.props.desks[this.props.index];
let desk = clone(this.state.desk);
try {
let dImg = this.props.deskTypes.find(
d => parseInt(d.deskType) === parseInt(desk.deskType)
);
deskImg = dImg.deskImage;
}
catch (ex) {
console.log(ex);
}
const userName = desk.UserLogon !== (null || '') ? desk.UserLogon : "Unassigned";
const top = Math.trunc(parseInt(parseInt(desk.y) * this.props.scale));
const left = Math.trunc(parseInt(parseInt(desk.x) * this.props.scale));
let imgStyle = {
width: `${parseInt(parseInt(desk.width) * this.props.scale)}px`,
height: `${parseInt((parseInt(desk.height) * this.props.scale))}px`,
position: 'absolute'
}
if (this.props.layout.currentDesk && desk.id === this.props.layout.currentDesk.id) {
imgStyle.border = '2px solid cyan';
}
const url = `data:image/jpeg;base64,${deskImg}`;
try {
//
return (
<Draggable key={desk.id}
index={this.props.index}
enabled={this.props.editable}
left={left}
top={top}
onMove={this.updateProperties}
onStop={this.mouseUp}
onRightClick={this.rightClick}
>
<div style={{
position: 'relative',
transform: `rotate(${parseInt(desk.rotation)}deg)`
}}
className='deskImg'>
<img style={imgStyle} alt={userName} src={url} />
</div>
</Draggable>
);
}
catch (ex) {
console.log(ex);
return null;
}
}//buildDesks
}
export default connect(mapDesksStateToProps, mapDesksDispatchToProps)(Desk);
//layout_creators.js
export const Layout_Undo_Change = () => (dispatch, getState) => {
const state = getState();
const desks = clone(state.layout_moveData);
console.log("1", state.layout_moveData.present)
//if no past to undo to
if (desks.past.length === 0) return;
const previous = clone(desks.past[desks.past.length - 1]);
desks.past.shift();
const undoPast = clone(desks.past);
// const undoPast = clone(desks.past).slice(0, desks.past.length - 1);
const undoFuture = [clone(desks.present), ...clone(desks.future)]
const undoDesks = { past: undoPast, present: previous, future: undoFuture };
dispatch({ type: ActionTypes.LAYOUT_UNDO_MOVES, payload: clone(undoDesks) });
console.log("2", state.layout_moveData.present)
let init = fetchInit();
init.method = "POST";
const deskData = { mode: 'UPDATEMANY', data: previous };
init.body = JSON.stringify(deskData);
let myReq = new Request(`/dataAPI/Layout/`, init);
fetch(myReq)
.then((response) => {
if (response.ok) {
return response;
}
else {
var error = new Error("Error " + response.statusText);
error.response = response;
throw error;
}
}, (error) => {
var err = new Error(error.message);
throw err;
})
.catch(err => {
return dispatch({
type: ActionTypes.LAYOUT_FAILED,
payload: err.message
});
});
}
//layout_reducer.js
import * as ActionTypes from '../ActionTypes';
export const layout = (state = {
isLoading: true,
isLoadingMap: false,
isLoadingDesks: false,
desksLoaded: false,
mapLoaded: false,
currentMap: null,
currentDesk: null,
maps: [],
deskTypes: [],
editMode: false,
errMess: null
}, action) => {
switch (action.type) {
case ActionTypes.LAYOUT_SITES_LOADING:
return { ...state, isLoading: true };
case ActionTypes.LAYOUT_DESKS_LOADING:
return { ...state, isLoadingDesks: true, desksLoaded: false };
case ActionTypes.LAYOUT_MAP_LOADING:
return {
...state, isLoadingMap: true, desks: [],
currentDesk: null, editMode: false, desksLoaded: false
};
case ActionTypes.LAYOUT_MAP_LOADED:
return { ...state, isLoadingMap: false, mapLoaded: true, maps: action.payload };
case ActionTypes.LAYOUT_MAPS_LOADED:
return { ...state, maps: action.payload, isLoading: false };
case ActionTypes.LAYOUT_DESKTYPES_LOADED:
return { ...state, deskTypes: action.payload };
case ActionTypes.LAYOUT_SET_CURRENT_DESK:
return { ...state, currentDesk: action.payload };
case ActionTypes.LAYOUT_SET_EDITMODE:
return { ...state, editMode: action.payload };
case ActionTypes.LAYOUT_DESK_DELETED:
return { ...state, currentDesk: null }
case ActionTypes.LAYOUT_DESKS_LOADED:
return { ...state, currentDesk: null, isLoadingDesks: false, desksLoaded: true }
case ActionTypes.LAYOUT_SET_ACTIVE_MAP:
return { ...state, currentMap: action.payload, desksLoaded: false };
case ActionTypes.LAYOUT_FAILED:
return {
...state, isLoadingMap: false, isLoadingDesks: false, desksLoaded: false,
errMess: action.payload, pageUsageData: []
};
case ActionTypes.LAYOUT_RESTORE_ALL:
return {
...state,
isLoading: true, isLoadingMap: false, mapLoaded: false, currentMap: null,
maps: [], desks: [], deskTypes: [], editMode: false,
errMess: null, desksLoaded: false
}
default:
return state;
}
}
export const layout_moveData = (state = {
past: [],
present: null,
future: []
}, action) => {
switch (action.type) {
case ActionTypes.LAYOUT_DESKS_LOADING:
return { ...state, present: [], past: [], future: [] };
case ActionTypes.LAYOUT_DESKS_LOADED:
return { ...state, present: action.payload, past: [], future: [] };
case ActionTypes.LAYOUT_DESK_DELETED:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_RESTORE_ALL:
return { ...state, present: [], past: [], future: [] };
case ActionTypes.LAYOUT_SET_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_UNDO_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
case ActionTypes.LAYOUT_REDO_MOVES:
return { ...state, present: action.payload.present, past: action.payload.past, future: action.payload.future };
default:
return state
}
}
I have extrapolated all items in the page to separate components as in React redux state change does not cause update even after deep copy of all state data. This allowed for better stack handling.
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
Vue 2 - disable input - multiple components
Hi all,
I struggle to solve a problem, where I want to disable other input fields, once the first char has been entered in another input field.
I've been trying to solve this with $emit, #focus, and other solutions and I'm still stuck. I was also not able to utilize the answers to be found here.
Snippet:
const Autocomplete = {
name: "autocomplete",
props: {
items: {
type: Array,
required: false,
default: () => ['test']
},
isAsync: {
type: Boolean,
required: false,
default: false
},
formLock: {
type: Boolean,
},
formId: {
type: String,
}
},
data() {
return {
isOpen: false,
results: [],
search: "",
isLoading: false,
arrowCounter: 0,
};
},
methods: {
onChange() {
// Let's warn the parent that a change was made
this.$emit("input", this.search);
// Is the data given by an outside ajax request?
if (this.isAsync) {
this.isLoading = true;
} else {
// Let's search our flat array
this.filterResults();
this.isOpen = true;
}
if (this.search.length === 0) {
this.isOpen = false;
}
console.log(this.search.length);
},
disableOther() {
var searchForms = document.getElementsByClassName('searchForm');
for (i = 0; i < searchForms.length; i++) {
}
console.log(searchForms.length);
},
filterResults() {
// first uncapitalize all the things
this.results = this.items.filter(item => {
return item.toLowerCase().indexOf(this.search.toLowerCase()) > -1;
});
},
setResult(result) {
this.search = result;
this.isOpen = false;
},
onArrowDown(evt) {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter() {
this.search = this.results[this.arrowCounter];
this.isOpen = false;
this.arrowCounter = -1;
},
handleClickOutside(evt) {
if (!this.$el.contains(evt.target)) {
this.isOpen = false;
this.arrowCounter = -1;
}
}
},
mounted() {
document.addEventListener("click", this.handleClickOutside);
},
destroyed() {
document.removeEventListener("click", this.handleClickOutside);
},
template: `
<div>
<input type="text" #input="onChange" class="searchForm" v-model="search" #keyup.down="onArrowDown" #keyup.up="onArrowUp" #keyup.enter="onEnter" v-bind:disabled="formLock" #focus="disableOther" />
<ul id="autocomplete-results" v-show="isOpen" class="autocomplete-results">
<li class="loading" v-if="isLoading">
Loading results...
</li>
<li v-else v-for="(result, i) in results" :key="i" #click="setResult(result)" class="autocomplete-result" :class="{ 'is-active': i === arrowCounter }">
{{ result }}
</li>
</ul>
</div>
`,
};
new Vue({
el: "#productSearchApp",
name: "productSearchApp",
data() {
return {
productName: [],
productCatalog: [],
lock: false,
searchName: "searchForm",
searchCatalog: "searchCatalog"
}
},
mounted() {
fetch("http://cormay.314-work.pl/wp-json/wp/v2/product")
.then(response => response.json())
.then((data) => {
for (i = 0; i < data.length; i++) {
this.productName.push(data[i].title.rendered);
};
for (i = 0; i < data.length; i++) {
this.productCatalog.push(data[i].product_catalog);
};
})
},
components: {
autocomplete: Autocomplete,
},
methods: {
updateLock(updateLock) {
this.lock = updateLock;
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="productSearchApp">
<autocomplete :items="productName" :form-id="searchName"></autocomplete>
<autocomplete :items="productCatalog" :form-id="searchCatalog"></autocomplete>
</div>
Thanks!
You could try something like this.
You'll notice that I'm passing the name of model back and forth a bit, which may seem like a nuisance to manage, but if you were to configure it as part of a v-for loop, it would make it easy to manage.
Vue.component('custom-input', {
props: ['value', 'disabled'],
template: `
<input
:disabled="disabled"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
new Vue({
el:'#app',
data:{
first: null,
second: null,
active: null
},
methods: {
onChange(e, model){
this.active = null
if (e.length > 0) {
this.active = model
}
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<custom-input v-model="first" :disabled="active !== null && active !== 'first'" #input="onChange($event, 'first')">Foo</custom-input>
<custom-input v-model="second" :disabled="active !== null && active !== 'second'" #input="onChange($event, 'second')">Bar</custom-input>
</div>
Maybe you could use a state manager like VueX or Instance Properties
... like:
// global state
Vue.prototype.$state = new Vue({
data: {
active: null
}
})
// input component
Vue.component('vue-input', {
props: ['value'],
template: `
<label>
<slot></slot>
<input v-model="model" :disabled="disabled" />
</label>
`,
data() {
return {
// avoid mutation
model: this.value
}
},
beforeDestroy() {
// reset state in case we remove the active component
if (this.$state.active === this) this.$state.active = null;
},
watch: {
model(value) {
// set active to 'this' if value length > 0 or null active value
this.$state.active = value.length ? this : null;
}
},
computed: {
disabled() {
// disable if active is not null and not equal to 'this'
return this.$state.active && this.$state.active !== this;
}
}
})
// app instance
new Vue({
el: '#app',
data: {
foo: null,
bar: null
}
})
label {
font: caption;
display: flex;
justify-content: space-between;
width: 160px;
margin-bottom: 10px;
}
input:disabled {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<vue-input v-model="foo">Foo</vue-input>
<vue-input v-model="bar">Bar</vue-input>
</div>