I have a dropdown from the semantic-ui package, which works fine when I don't include the confirmation modal code in my project. This is what I currently have:
<Dropdown
loading={settingConfig}
disabled={!configEditable}
options={configs && configs.length > 0 ? configs.map(formatConfig) : teltonikaConfigs.map(formatTeltonikaConfig)}
onChange={this.handleConfigChange} />
<EditButton editingEnabled={configEditable} onClick={this.toggleConfigEdit} />
The options are formatted in the following way:
const formatConfig = conf => ( { key: conf.id, text: conf.name + '-' + conf.scriptVersionId + '.' + conf.configVersionId, value: conf.id, image: getRisk(conf.risk)} );
const formatTeltonikaConfig = conf => ({key: conf.id, text: conf.name, value: conf.id});
When an option is selected, the following function is called:
handleConfigChange = ( e, data ) => {
const forceUpdate = true;
// Not an Atom B Device
if (this.props.device.type !== "HARDWIRED-BM") {
const configId = data.value;
this.setState( { settingConfig: true, configEditable: false } );
updateConfiguration(this.props.device.imei, configId, forceUpdate)
.then( this.props.handleDeviceUpdate )
.catch( error => toast.error( "There has been an error whilst updating the device.. This will need to be updated manually. " + error.message ) )
.then(() => this.setState( { settingConfig: false } ) )
} else {
const configId = data.value;
console.log(" Id " + configId);
this.setState( { settingConfig: true, configEditable: false } );
updateTeltonikaDeviceConfig(this.props.device.imei, configId, forceUpdate)
.then(this.props.handleDeviceUpdate)
.catch(error => toast.error("There has been an error whilst updating the device.. This will need to be updated manually. " + error.message))
.then(() => this.setState({settingConfig: false}))
}
};
This will then get the configId from data.value as per the formatted configs and works fine...
However, when I add a confirmation dialog/modal so when the user selects an option, they have to confirm yes or no, configId/data.value is undefined. The code for this is as follows:
The dropdown changes to this (onChange method changes):
<Dropdown
loading={settingConfig}
disabled={!configEditable}
options={configs && configs.length > 0 ? configs.map(formatConfig) : teltonikaConfigs.map(formatTeltonikaConfig)}
onChange={this.show} />
<EditButton editingEnabled={configEditable} onClick={this.toggleConfigEdit} />
I also have this code to show the dropdown:
show = () => this.setState({ open: true })
handleConfirm = (e, data) => {
this.handleConfigChange(e, data);
this.setState({ open: false })
}
handleCancel = () => this.setState({ open: false })
Then the confirmation dialog code is as follows:
<div>
<Confirm
open={this.state.open}
cancelButton='No'
confirmButton="Yes"
onCancel={this.handleCancel}
onConfirm={this.handleConfirm}
/>
</div>
So from this, when an option is selected, the dialog window opens and when the user clicks YES e,data is sent into the handleConfirmMethod.. Now my problem is that data contains the following values, and not the configId which is passed through without the confirmation dialog:
The data passed through is that of what is in the confirmation dialog code.. Can someone please help me with this, as I need to pass through the configId as data?
Hope this makes sense!
You cannot get configId(data.value) from handleConfirm because, handleConfirm is a callback function which is fired when you click the "yes" button on <Confirm/>, it has no idea of which option you just selected. The only place you can get that information is the onChange callback on that <Dropdown/>.
To solve your problem, we need to pass the configId(data.value) from Dropdown's onChange callback to Confirm's onConfirm callback. There are several solutions.
I personally would suggest to save configId(data.value) into a state like:
<Dropdown
...
onChange={this.show} /* better to rename it to something else cuz it's not just "show" any more */
/>
// save the configId(data.value) to state
show = (event, data) => this.setState({ open: true, selectedConfigId: data.value });
// get the value from state. You can also get "this.state.selectedConfigId" in this.handleConfigChange directly
handleConfirm = (e, data) => {
this.handleConfigChange(e, this.state.selectedConfigId);
this.setState({ open: false })
}
Related
So I'm using react-bootstrap-typeahead and it's working completely fine when I'm typing something in the search box. It gives me the relevant options when I type something in the search box like this:
However, when I select one of options and then try to re-change the text it throws an error. This is how it looks like when I select 1 option.
And this is the error it throws: TypeError: 'Cannot read property 'match' of undefined'
Here is the state of the Search component which has the Typeahead:
class Search extends Component {
state = {
hcpName: [],
hcps: [],
searchName: '',
isLoading: false,
hcp_id: 101,
searchSelectedOption: ''
}
And here is the Typeahead I'm using:
<div className='col-md-3'>
<div class="form-group">
<Typeahead
id="basic-example"
options={this.state.hcpName}
placeholder="Search by Name..."
emptyLabel={this.state.isLoading ?
<>
<span>Searching...
<Loader
style={{ paddingLeft: '5px', display: 'inline' }}
type="Circles"
color="#0caf8d"
height={15}
width={20}
radius={30}
/>
</span>
</>
: ''}
isLoading={this.state.isLoading}
onInputChange={(searchName) => this.setState({ searchName }, () => {
{
let nameValue = this.state.searchName;
this.setState({ isLoading: true })
axios.post('/get-hcp-data', {
nameValue: nameValue
})
.then((res) => {
const hcps = res.data;
this.setState({ hcps: hcps, hcpName: Object.values(hcps.hcp_details_concat) })
this.setState({ isLoading: false })
}, (error) => {
console.log(error);
});
}
})}
onChange={(selectedOption) => {
console.log('selected option: ', selectedOption[0]);
console.log('npi id selected', selectedOption[0].replace(/(^.*\[|\].*$)/g, ''));
console.log('parsed npi id selected', parseInt(selectedOption[0].replace(/(^.*\[|\].*$)/g, '')[0]));
this.setState({hcp_id: parseInt(selectedOption[0].match(/[^[\]]+(?=])/g)[0])});
}}
/>
</div>
</div>
Inside 'onInputChange' inside Typeahead, I'm basically making an api call after every keystroke that a user enters. So that's why you can see an axios request over there. And inside 'onChange', I extract the number inside the square brackets of the user selection.
As I mentioned, I face an error when I try to change the text of the already selected option. For example, suppose I clicked on [101]Anna, I see that text in the search bar. And when I try to modify it again, I immediately see an error. What's the possible reason for this?
Here is the console log for onInput change:
I solved the problem by identifying that the match/replace function can't be used inside the onChange of Typeahead so I instead directly used it while fetching the api data. For that, I first set the state according to what the user has selected like this:
onChange={(selectedOption) => this.setState({ searchName: selectedOption }, () => {
console.log('selected option: ', selectedOption);
})}
And then while fetching the data, I made use of the searchName state to run the replace function.
dataFetch = () => {
this.setState({ receivedData: [], loading: true });
let page_id = this.state.page_id;
let hcp_id = parseInt(this.state.searchName[0].replace(/(^.*\[|\].*$)/g, ''));
axios.post('/test-json', {
page_id: page_id,
hcp_id: hcp_id
})
.then((res) => {
this.setState({ receivedData: res.data, loading: false });
console.log('State after loading data: ', this.state);
}, (error) => {
this.setState({ error: true });
console.log(error);
});
}
When i click on nav button then table data loads but on second time click table data doesn't load but data in loads on every click in react js
I have put the code in componentdidMount() {} to load it every time when it loads. i made async function which loads table and also set header.
{this.state.numofbugs.map((data)=> {
return (
<li key={data}>
<Link to={'/applogs/' + data}
className="waves-effect" className={this.props.location.pathname === '/applogs/' + data ? 'active': ''}>
<span className="font-size waves-effect" >{data}</span>
</Link>
</li>
)
})}
in file where it renders
componentDidMount() {
if (this.props.location.pathname.includes('app_')) {
this.showTable(this.props.location.pathname.split('/')[2]);
}
}
async showTable(col_name) {
// empty data
this.state.logtable = []
let conf = { headers: { Authorization: 'Bearer ' + localStorage.getItem('session') }}
Axios.get(links.baseURL + 'sample?collection=' + col_name, conf).then((response) => {
// get headers
this.state.tableHeaders = [
Object.keys(response.data.result[0])[0],
Object.keys(response.data.result[0])[1],
Object.keys(response.data.result[0])[2],
Object.keys(response.data.result[0])[3],
]
Object.keys(response.data.result).map((key) => {
this.state.logtable[key] = response.data.result[key];
this.state.logtable[key]['key'] = key.toString()
});
this.setState({'logtable': this.state.logtable});
});
}
I expected to get data in my table on every button click in table but after one time click on tab of collapse data laods in table then on second time data loads in tag not table
First thing, you don't need to empty your state like,
this.state.logtable = []
If you want to replace your existing state with new one, you can simply set the state with new data.
Another issue is, you are directly mutating your state, like this
this.state.tableHeaders = [
Object.keys(response.data.result[0])[0],
Object.keys(response.data.result[0])[1],
Object.keys(response.data.result[0])[2],
Object.keys(response.data.result[0])[3],
]
And this
Object.keys(response.data.result).map((key) => {
this.state.logtable[key] = response.data.result[key];
this.state.logtable[key]['key'] = key.toString()
});
You should make use of local variables to create temporary array, and then need to set them in your state like,
async showTable(col_name) {
let conf = { headers: { Authorization: 'Bearer ' + localStorage.getItem('session') }}
Axios.get(links.baseURL + 'sample?collection=' + col_name, conf).then((response) => {
// get headers
let tableHeaders = Object.keys(response.data.result[0]).map((key,index) => index <=3 && key.toString())
let logtable = Object.keys(response.data.result).map((key) => ({
logtable[key] : response.data.result[key];
logtable[key]['key'] : key.toString()
}));
this.setState({logtable, tableHeaders});
});
}
I have this state defined:
constructor(props){
super(props);
this.state = {
posts:[],
post:{},
openNew:false,
openModify:false
};
}
With the following function which contains a fetch, I recieve an array of objects with responseData:
getPosts(){
fetch(
DOMAIN+'/api/posts/', {
method: 'get',
dataType: 'json',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization':'Bearer '+this.props.token
}
})
.then((response) =>
{
return response.json();
})
.then((responseData) => {
this.setState({posts:responseData});
console.log("Log del responseData de posts");
console.log(responseData);
})
.catch(function() {
console.log("error");
});
}
This function is called in componentDidMount:
componentDidMount(){
this.getPosts()
}
The JSON object obtained from the fetch and kept within this.state.products looks like this:
As shown previously in the fetch, with this line this.setState({posts:responseData}); I can pass posts to the table where I want title, date and hour to be displayed:
<DataTables
height={'auto'}
selectable={false}
showRowHover={true}
columns={CAMPAIGN_TABLE_COLUMNS}
data={this.state.posts}
showCheckboxes={false}
rowSizeLabel="Filas por página"
onCellClick={this.handleOpenModify.bind(this)}
/>
The table called is:
const CAMPAIGN_TABLE_COLUMNS = [
{
key: 'title',
label: 'Título',
style:{width: '40%'}
}, {
key: 'created',
label: 'Fecha',
style:{width: '30%'},
render: (DateToFormat) => {
return moment(DateToFormat).format("DD/MM/YYYY");
}
}, {
key: 'created',
label: 'Hora',
style:{width: '30%'},
render: (DateToFormat) => {
return moment(DateToFormat).format("hh:mm:ss");
}
}
];
With all of this I am able to print the data that I want on the table, looking like this:
What I am not able to do is: When I click on a row of the table to pass the values that were previously printed, such as the title.
This dialog is constructed using the following lines:
<Dialog
title="Modificar Post"
actions={actions}
modal={false}
open={this.state.openModify}
onRequestClose={this.handleClose}
titleClassName="dialog-title"
contentStyle={{width:660}}
autoScrollBodyContent={true}
>
<TextField
fullWidth={true}
floatingLabelText="Título"
errorText="¡Ups! No deberías ver este mensaje."
defaultValue={this.state.posts.title}
/>
</Dialog>
I thought that binding this to handleOpenModify (the function that is called when you click on a row of the table):
handleOpenModify = () => {
this.getPosts();
this.setState({openModify: true});
};
Would allow me to print the title within the TextField as simple as giving to the defaultValue this.state.posts.title, but is not working as you can see on the last picture that I added.
P.D.: I call getPosts() in handleOpenModify in case it had to be called again when a row is clicked, but it hasn't worked either.
Any suggestions?
DataTables provides you the rowNumber and columnIndex as arguments.
For more information, check their docs:
https://github.com/hyojin/material-ui-datatables/blob/master/src/DataTables/DataTablesRow.js#L142
<DataTables
...
onCellClick={(event, rowNumber) => console.log('selectedPost', this.state.posts[rowNumber]) }
/>
Thanks to #EdwardChopuryan and #Semi-Friends I've been able to retrieve the data that I wanted.
First of all I had to change the name of my function handleOpenModify to handleCellClick, since I could pass through the row parameter all I wanted and keep it within post {}, declared before in the sate.
handleCellClick = (y,x,row) => {
this.getPosts();
this.setState({
openModify: true,
newForm:false,
post:{...row, _id:row._id,title:row.title}})
};
Then, on DataTable, bind it on the onCellClick parameter:
<DataTables
height={'auto'}
selectable={false}
showRowHover={true}
columns={CAMPAIGN_TABLE_COLUMNS}
data={this.state.posts}
showCheckboxes={false}
rowSizeLabel="Filas por página"
onCellClick={this.handleCellClick.bind(this)}
/>
And call the value that I wanted on the TextField through the defaultValue:
<TextField
fullWidth={true}
floatingLabelText="Título"
errorText="¡Ups! No deberías ver este mensaje."
defaultValue={this.state.post.title}
/>
And this is the result!
this is a sample on how to bind and retrieve specific data on click of cell
list item creation
var list = CAMPAIGN_TABLE_COLUMNS.map((data, key) =>
<td onClick={this.handleClick.bind(this, key)}>{data.title}</td>
)
onClick handler
handleClick(id) {
let item = CAMPAIGN_TABLE_COLUMNS[id]; // item data
}
as for your current code, you need to modify this part
onCellClick={this.handleOpenModify.bind(this)} // key or the array index
handleOpenModify(e, row, key) { // receive the column number as 3rd param
let item = CAMPAIGN_TABLE_COLUMNS[key]; // now get the respective object
}
I am using formsy-react to handle validation of my input fields. Problem is that I can't update states. I followed example on formsy github page and validation is working but problem is that states are incorrect. They are always one step (or few) behind and I am not sure why...
I used callback on setState function to implement some custom logic on validation and that part doesn't work properly.
I have a situation where user enters email. After user enters email I check if email is already registrated. If user is already in system, I create new input component (password type) and if not I create new "input type email" component.
Since all forms elements are required I added one more validation check that checks if new password or email component is added and if there is any data.
To update states I used Forms form API call onChange() and this part is not working for unknown reason.
Does someone knows where is the problem?
This is code I am using:
Component Input (shorted version)
changeValue(event) {
this.setValue(event.currentTarget.value);
},
render() {
// Set a specific className based on the validation
// state of this component. showRequired() is true
// when the value is empty and the required prop is
// passed to the input. showError() is true when the
// value typed is invalid
const className = (this.props.className || "col-md-4" );
const classValidationName =this.isValid() ? 'valid' : this.showError() ? ' invalid' : null;
// An error message is returned ONLY if the component is invalid
// or the server has returned an error message
const errorMessage = this.getErrorMessage();
return (
<div className= {className}>
<div className="md-form">
<span className="prefix"><i className={this.props.icon}></i></span>
<input
className={classValidationName}
name={this.props.name}
id={this.props.id}
type={this.props.inputType}
value={this.getValue() || ""}
onChange={this.changeValue}
onBlur={this.props.controlFuncOnBlur}
placeholder={this.props.placeholder}
required={this.props.required}
pattern={this.props.pattern}
/>
<label id={this.props.name + 'Label'} htmlFor={this.props.name} data-error={errorMessage}
data-success={this.props.successMessage}>{this.props.title}
</label>
</div>
</div>
);
}
Container (shorted version)
handleEmailBlur(event) {
const self = this;
if (this.refs.email.isValid) {
axios.get('/api/checkIsUserRegistrated', {
params: {
email: this.state.email
}
})
.then(function (response) {
if (self.state.userExist !== response.data[0].userExist) {
self.setState({
userExist: response.data[0].userExist,
confirmEmail: "",
password: ""
});
self.forceUpdate();
}
})
.catch(function (error) {
console.log(error);
});
}
}
enableButton = () => {
this.setState({
formValid: true
});
}
disableButton = () => {
this.setState({
formValid: false
});
}
saveCurrentValuesToStates = (getCurrentValues, isChanged) => {
console.log(this);
this.setState(getCurrentValues, ()=> {
if (this.state.formValid && (this.state.password || this.state.confirmEmail)){
this.setState({
canSubmitForm: true
});
}
else{
this.setState({
canSubmitForm: false
});
}
});
}
<Formsy.Form className="booker-form" ref="form"
onChange={this.saveCurrentValuesToStates} onValid={this.enableButton} onInvalid={this.disableButton}>
<SingleInput
inputType={'email'}
icon={'icon-Email'}
id={'email'}
name={'email'}
title={'E-mail'}
ref="email"
controlFuncOnBlur={this.handleEmailBlur}
content={this.state.email}
errorMessage={'Incorect E-Mail address'}
required
validations="isEmail"
validationError="This is not a valid email"
/>
{(this.state.userExist === '0') ?
<SingleInput
inputType={'email'}
icon={'icon-Email'}
id={'confirmEmail'}
name={'confirmEmail'}
title={'Confirm your E-mail'}
content={this.state.confirmEmail}
required
validations="equalsField:email"
validationError="Emails don't match"
/>
: null}
{(this.state.userExist === '1') ?
<SingleInput
inputType={'password'}
icon={'icon-Padlock'}
id={'password'}
name={'password'}
title={'Enter your password'}
content={this.state.password}
required
/>
: null}
i m using Kendo UI apsnet and i have a Gird with autocomptele as template in 3rd column and i want send data using javascript function "onAutoCompleteX", in this function i want to get id of active autocomplete to send text as parametere to action "GetArticle" but my probleme is how get this id, tried many methods always i get "undefined" or error
#using KendoUIMvcApplication2.Areas.Gescom.Models.Achat
<style>
.k-widget .templateCell
{
overflow: visible;
}
</style>
<script>
function initMenus(e) {
$(".templateCell").each(function () {
eval($(this).children("script").last().html());
});
}
function onAutoCompleteSelectX(e) {
var dataItem = this.dataItem(e.item.index());
var url = '#Url.Action("GetArticle", "Fiche")';
$.ajax({
url: url,
data: { code: dataItem.Code }, //parameters go here in object literal form
type: 'GET',
datatype: 'json',
success: function (data) {
if (data == null)
document.getElementById('labelx').innerHTML = "null";
else
document.getElementById('labelx').innerHTML = data.Code;
},
error: function () {
document.getElementById('labelx').innerHTML = "error";
}
});
}
function onAutoCompleteX() {
var currentId = $(this).attr('id');
//var currentId = $(this).id;
//document.getElementById('labelx').innerHTML = $(this).className; //$obj.attr('id');
return {
text: document.getElementById(currentId).value
//text: $("#id_of_another_autocomplete").val() works fine when i set static id manually
};
}
</script>
<div class="lines-tab-doc">
#(Html.Kendo().Grid<LineAppelOffre>()
.Name("grid-lines-doc")
// Declare grid column
.Columns(columns =>
{
// Cretae all the columns base on Model
columns.Bound(l => l.Document);
columns.Bound(l => l.LigneOriginale);
columns.Template(l => { }).Title(#Resources.Resource.Article)
.HtmlAttributes(new { #class = "templateCell" })
.ClientTemplate(
Html.Kendo().AutoComplete()
.Name("Article")
.HtmlAttributes(new { id = "#=LigneOriginale#", style = "width:100%;" })
.DataTextField("Code")
.Filter(FilterType.Contains)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetArticles", "Fiche").Data("onAutoCompleteX");
})
.ServerFiltering(true);
})
.Events(e => { e.Select("onAutoCompleteSelectX"); }).ToClientTemplate().ToHtmlString()
);
columns.Bound(l => l.Fournisseur);
columns.Bound(l => l.RefArtFrs);
// Edit and Delete button column
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(200);
})
.Events(ev => ev.DataBound("initMenus"))
// Declare ajax datasource.
// CRUD operation are wired back to ASP MVC Controller/Action e.g. HomeController, GetAll
// Set the model Id
.DataSource(datasoure => datasoure.Ajax()
.Model(model =>
{
//model.Id(l => l.Document);
model.Id(l => l.LigneOriginale);
})
.Read(read => read.Action("LinesAppelOffre_Read", "Achat"))
.Create(create => create.Action("LinesAppelOffre_Add", "Achat"))
.Update(update => update.Action("LinesAppelOffre_Update", "Achat"))
.Destroy(delete => delete.Action("LinesAppelOffre_Delete", "Achat"))
.PageSize(10)
)
// Add tool bar with Create button
.ToolBar(toolbar => toolbar.Create())
// Set grid editable.
.Editable(editable => editable.Mode(GridEditMode.InCell))
.Scrollable(scr=>scr.Height(327))
.Sortable()
.Selectable()
.Navigatable()
.Pageable(pageable =>
{
pageable.Refresh(true);
pageable.PageSizes(true);
pageable.Messages(msg => msg.Empty(null));
})
)
</div>
You can get your AutoComplete id like that:
function onAutoCompleteX(e) {
var currentId = e.sender.element.attr('id');
...
}
But I'm not sure if you have explicity set name as "Article" .Name("Article") you will not always get "Artilcle" as id, even if you set it using .HtmlAttributes property.
If so, just try to use different attribute then id and get is same way.
i used document.activeElement
Browser compatibility
Chrome 2
Firefox (Gecko) 3.0
Internet Explorer 4
Opera 9.6
Safari 4.0