I want to update my state when I get errors from my ajax call.
My code:
var EmailForm = React.createClass({
getInitialState: function(){
return {
password:'',
email: '',
errors: ''
};
},
componentDidMount: function() {
this.serverRequest = $.get('/accounts/email-form/', function (result) {
var userInfo = result;
this.setState({
email: userInfo.email
});
}.bind(this));
},
submit: function (e){
var self;
e.preventDefault()
self = this;
console.log(this.state);
var data = {
password: this.state.password,
email: this.state.email,
CSRF: csrftoken
};
// Submit form via jQuery/AJAX
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$.ajax({
type: 'POST',
url: '/accounts/email-form/',
data: data,
datatype: JSON
})
.done(function(data) {
toastr.success('Profile updated');
})
.error(function(jqXhr) {
var error = jqXhr.responseJSON; //How can I append this errors to my errors state ?
toastr.error('There is some errors in your request');
});
},
passwordChange: function(e){
this.setState({password: e.target.value});
},
emailChange: function(e){
this.setState({email: e.target.value});
},
render: function() {
return (
<form onSubmit={this.submit}>
<div className="form-half">
<label htmlFor="password" className="input-label">Current Password</label>
<BasicInputPassword valChange={this.passwordChange} val={this.state.password}/>
<span className="form-error is-visible">{this.state.errors.password}</span>
</div>
<div className="form-half">
<label htmlFor="email" className="input-label">New email</label>
<BasicInput valChange={this.emailChange} val={this.state.email}/>
<span className="form-error is-visible">{this.state.errors.email}</span>
</div>
<button type="submit" className="button secondary" >Submit</button>
</form>
);
}
});
I have response errors in error variable. How can I update state errors with this json and display for example state.errors.email easy ? Is this possible ?
use this.setState()
var that = this;
$.ajax({
type: 'POST',
url: '/accounts/email-form/',
data: data,
datatype: JSON
})
.done(function(data) {
toastr.success('Profile updated');
})
.fail(function(xhr, status, error) {
that.setState({
//assign error to whatever you want under `state`
});
});
*make sure this is pointing at the right scope. Or use arrow functions for lexical this.
$.get('some.php')
.done(function(msg){ })
.fail(function(xhr, status, error) {
this.setState({});///here <===
}).bind(this);
or
$.ajax({
type: "GET",
url: "test.com",
success: function(msg){
alert( "Data Saved: " + msg );
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("some error");
}
});
you must use .fail() and you can access to error in .fail() and store in state.
You can do this.
const self = this;
$.get('api-url')
.done(function(msg){
// some work
})
.fail(function(xhr, status, error) {
self.setState({error: xhr.responseJSON });
});
As the this context changes so you can assign it at a variable first to avoid the confusion with the context.
Related
I am trying to extend the React.JS CommentBox example to include the ability to delete comments. I have added a simple button to the Comment class and all of the necessary methods and listeners. Now I am trying to add functionality to a handleDelete method in my CommentBox class to do the necessary Ajax calls to delete the comment on server as well as locally
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
handleDelete: function(id) {
var comments = this.state.data;
var newComments = [];
var commentToDelete = null;
for (var i = 0, len = comments.length; i < len; i++) {
if (comments[i].id != id) {
newComments.push(comments[i]);
} else {
commentToDelete = comments[i];
}
}
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'DELETE',
data: commentToDelete,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
<h1>Comments</h1>
<CommentList data={this.state.data} onCommentDelete={this.handleDelete} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
var CommentList = React.createClass({
handleDelete: function(id) {
console.log('handleDelete2 ' + id);
this.props.onCommentDelete(id);
},
render: function() {
var commentNodes = this.props.data.map(function(comment) {
var handle_delete = this.handleDelete.bind(this, comment.id);
return (
<Comment author={comment.author} key={comment.id} onCommentDelete={handle_delete}>
{comment.text}
</Comment>
);
}, this);
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.author}
onChange={this.handleAuthorChange}
/>
<input
type="text"
placeholder="Say something..."
value={this.state.text}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var md = new Remarkable();
var rawMarkup = md.render(this.props.children.toString());
return { __html: rawMarkup };
},
handleDelete: function(e) {
e.preventDefault();
console.log("handleDelete");
this.props.onCommentDelete();
},
render: function() {
var md = new Remarkable();
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
<button onClick={this.handleDelete}>Delete</button>
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={2000} />,
document.getElementById('content')
);
However, the item won't delete permanently and there is an error in the console:
Failed to load resource: the server responded with a status of 405 (Method Not Allowed)
/api/comments error Method Not Allowed
I really am not an Ajax expert so I am sure I am making a very obvious mistake.
Does anyone have any suggestions? Thanks in advance.
I have used getInitialState on my BrowseWidgetBox component. Although when passing the data to my MainMenu component, the data remains empty, as if the AJAX call to the api was never run in the BrowseWidgetBox.
My question then, is why is this happening? Shouldn't componentDidMount call the ajax api and re-set the state to include the contents of the ajax call? I want the state of my groupsData and my itemData to be present when the page is initially loaded. I am a bit worried that getInitialState is hindering the calls to ajax at least 'initially' which is causing my error.
Here is the full code of the two components:
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData); // console.log here
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>obj.description</li>);
});
return (<div className="MainMenu">
<ul>{categories}</ul>
</div>);
}
});
var BrowseWidgetBox = React.createClass({
getInitialState: function () {
return {groupsData: {}, itemsData: {}};
},
getGroupsApi: function(){
$.ajax({
url: this.props.groupsApi,
dataType: 'json',
type: 'GET',
success: function(groupsData){
this.setState({groupsData: groupsData});
console.log(groupsData) // Console.log here
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
getItemsApi: function() {
$.ajax({
url: this.props.itemsApi,
dataType: 'json',
type: 'GET',
success: function(itemsData){
this.setState({itemsData: itemsData});
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.getGroupsApi();
this.getItemsApi();
},
render: function() {
return (<div className="BrowseWidgetBox">
<MainMenu groupsData={this.state.groupsData} itemsData={this.state.itemsData} />
<Display />
</div>);
}
});
React.render(
<BrowseWidgetBox groupsApi="http://this/is/a/good/url" itemsApi="http://this/is/a/good/api/call" />, document.getElementById('widget-container')
);
You're trying to use the map in object...
In
getInitialState: function () {
return {groupsData: {}, itemsData: { objects: [] }};
},
the first render are getting a object in groupsData
try change to
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData); // console.log here
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>obj.description</li>);
});
return (<div className="MainMenu">
<ul>{categories}</ul>
</div>);
}
});
var BrowseWidgetBox = React.createClass({
getInitialState: function () {
return {groupsData: { objects: [] }, itemsData: []};
},
getGroupsApi: function(){
$.ajax({
url: this.props.groupsApi,
dataType: 'json',
type: 'GET',
success: function(groupsData){
this.setState({groupsData: groupsData});
console.log(groupsData) // Console.log here
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
getItemsApi: function() {
$.ajax({
url: this.props.itemsApi,
dataType: 'json',
type: 'GET',
success: function(itemsData){
this.setState({itemsData: itemsData});
}.bind(this),
error: function(xhr, status, err){
console.error(this.props.groupsApi ,status, err.toString());
}.bind(this)
});
},
componentDidMount: function() {
this.getGroupsApi();
this.getItemsApi();
},
render: function() {
return (<div className="BrowseWidgetBox">
<MainMenu groupsData={this.state.groupsData} itemsData={this.state.itemsData} />
<Display />
</div>);
}
});
React.render(
<BrowseWidgetBox groupsApi="http://this/is/a/good/url" itemsApi="http://this/is/a/good/api/call" />, document.getElementById('widget-container')
);
So I have a template and I need to show/hide some text based on a return value from a method. I searched and noticed one should use handlebars helpers in order to achieve this. So I added a resetPassword helper inside the controller. The options.fn(this) part works. The options.inverse(this) doesn't. It throws the ubiquitous JS error Uncaught TypeError: undefined is not a function....
templates/reset-password.hbs:
<div class = "container">
{{#resetPassword}}
<h4>Password has been reset</h4>
<h5>Your new password is: <b>{{password}}</b></h5>
{{else}}
<h4>Something went wrong! </h4>
<h5>The password has not been reset! Please try again later.</h5>
{{/resetPassword}}
</div>
controllers/reset-password.js:
export default Ember.Controller.extend({
token: null,
init: function ()
{
this._super();
Ember.Handlebars.registerHelper('resetPassword', function (options)
{
var token = this.get('token');
var result = false;
/* Ember.$.ajax({
type: "POST",
url: "/reset_password",
contentType: "text/html",
dataType: "json",
async: false,
beforeSend: function (request)
{
request.setRequestHeader("Authorization", token);
},
success: function (data, textStatus)
{
this.set('password', data.password);
result = true;
},
error: function (data, textStatus)
{
result = false;
}
});*/
if (result)
{
return options.fn(this);
}
return options.inverse(this);
});
}
});
So because JS and Ember purely suck, here's a workaround:
{{#if resetPassword}}
<h4>Password has been reset</h4>
<h5>Your new password is: <b>{{password}}</b></h5>
{{else}}
<h4>Something went wrong! </h4>
<h5>The password has not been reset! Please try again later.</h5>
{{/if}}
And the controller action:
resetPassword: function ()
{
var self = this;
var token = this.get('token');
var result = false;
Ember.$.ajax({
type: "POST",
url: "/api/users/reset_password",
contentType: "text/html",
dataType: "json",
async: false,
beforeSend: function (request)
{
request.setRequestHeader("Authorization", token);
},
success: function (data, textStatus)
{
var responseUser = data["users"][0];
self.set('password', responseUser.password);
result = true;
},
error: function (data, textStatus)
{
result = false;
}
});
return result;
}.property()
Actually, I've been trying to add a 'Delete a comment' functionality to my Comment Box system.
Here is my code:-
var Comment = React.createClass({
handleClick: function(e){
e.preventDefault();
var commentId = this.props.comment.id;
return this.props.onDelete(commentId);
},
render: function () {
return (
<div className="comment">
<h4 className="commentAuthor">
{this.props.comment.email}
</h4>
{this.props.comment.comment}
<a onClick={this.handleClick}> × </a>
</div>
);
}
});
var CommentList = React.createClass({
handleDelete: function(commentId){
return this.props.del(commentId);
},
render: function () {
var commentNodes = this.props.comments.map(function (comment, index) {
return (
<Comment comment = {comment} onDelete = {this.handleDelete} key = {index} />
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentBox = React.createClass({
getInitialState: function () {
return {comments: []};
},
componentDidMount: function () {
this.loadCommentsFromServer();
},
loadCommentsFromServer: function () {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function (comments) {
this.setState({comments: comments});
}.bind(this),
error: function (xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.comments;
var newComments = comments.concat([comment]);
this.setState({comments: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: {"comment": comment},
success: function(data) {
this.loadCommentsFromServer();
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
delc: function(commentId){
$.ajax({
url: this.props.url,
data: {"id" : commentId},
type: 'DELETE',
dataType: 'json',
success: function (comments) {
this.setState({comments: comments});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList del={this.delc} comments={this.state.comments} />
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
</div>
);
}
});
var CommentForm = React.createClass({
handleSubmit: function() {
var email = this.refs.email.getDOMNode().value.trim();
var comment = this.refs.comment.getDOMNode().value.trim();
this.props.onCommentSubmit({email: email, comment: comment});
this.refs.email.getDOMNode().value = '';
this.refs.comment.getDOMNode().value = '';
return false;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="email" placeholder="Your email" ref="email" />
<input type="text" placeholder="Say something..." ref="comment" />
<input type="submit" value="Post" />
</form>
);
}
});
var ready = function () {
React.renderComponent(
<CommentBox url="18/comments.json" />,
document.getElementById('art')
);
};
$(document).ready(ready);
Now, when I try to delete a comment, it throws an error that says 'Uncaught TypeError: undefined is not a function'
Switching to 'source' tab on devTool, I found that the problem is in 'onDelete' function.
It says, 'onDelete' function is undefined.
I think the problem is due to 'this' keyword, but I'm not sure.
What should I do to resolve this issue? Let me know if I'm missing out on something.(I'm a newbie)
Thank you in advance.
The function passed to map will not automatically share a this pointer with your react class.
To use this inside the anonymous function, call .bind(this) at the end of the function definition to yield a function with the expected this inside.
var commentNodes = this.props.comments.map(function (comment, index) {
...
});
Becomes:
var commentNodes = this.props.comments.map(function (comment, index) {
...
}.bind(this));
I am a newbie in the Reactjs, I am trying to perform CRUD operation. But I am having issues when performing Delete event. This is How my screen looks:
![enter image description here][1]
And my code looks like this:
var DALHandler=React.createClass({
getInitialState:function(){
return{data: {objects:[]}} // variable should be created with array
},
DeleteOrganizationFromServer: function(id) { // this is Json Function
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'URL/'+id,
dataType: 'json',
type: 'Delete',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url='url', status, err.toString());
}.bind(this)
});
},
loadOrganizationFromServer: function() {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'URL/'+id,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url='url', status, err.toString());
}.bind(this)
});
},componentWillMount: function() {
this.loadOrganizationFromServer();
//setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},render: function() {
return (
<div >
<OrganizationAPP onOrganizationSubmit={this.handleOrganizationSubmit} />
<OrganizationList data= {this.state.data} />
<Organization DeleteOrganization= {this.DeleteOrganizationFromServer} />
</div>
);
}
});
var OrganizationList=React.createClass({
render:function(){
var results = this.props.data;
var parsed_results = results.objects;
var organizations = parsed_results.map(function(organization){
return <Organization id={organization.id} name={organization.name} description={organization.description}> </Organization>
});
return(<div>
{organizations}
</div>)
}
});
//var converter = new Showdown.converter();
var Organization = React.createClass({
handleDeleteClick: function (e) {
//alert(this.props.id);
var currentId=this.props.id;
this.props.DeleteOrganization(); // ERROR CAUSES HERE:
},
render: function() {
return (
<div className="row">
<div className="small-2 large-2 columns">{this.props.id} </div>
<div className="small-4 large-4 columns">{this.props.name} </div>
<div className="small-4 large-4 columns">{this.props.description}</div>
<div className="small-2 large-2 columns">
<input type="button" onClick={this.handleDeleteClick} data-order={this.props.id} value="Delete" />
</div>
</div>
);
}
});
I know I am doing some stupid mistake but I don't find help to solve this. Please help me to sort it out.
Thank you in advance.
You create your Organization elements like this:
<Organization id={organization.id}
name={organization.name}
description={organization.description}></Organization>
So inside Organization you will have three props: id, name, and description. You try to call this.props.DeleteOrganization() from inside Organization, which is undefined because it's not one of the three props you're passing to it.
To make it work with OrganizationList, you need to pass the delete function down to it:
<OrganizationList data={this.state.data} onDelete={this.DeleteOrganizationFromServer} />
And inside OrganizationList's render function, you can pass it down again.
<Organization id={organization.id}
name={organization.name}
description={organization.description}
onDelete={this.props.onDelete.bind(null, organization.id}></Organization>
And inside Organization:
handleDeleteClick: function(){
this.props.onDelete()
}
I have managed to the solve problem myslef but this fiddle helped me greatly: http://jsfiddle.net/ammit/wBYHY/5/
So mainly I added one more function in OrganizationList that calls DeleteOrganizationFromServer function, so now my code looks like this:
var OrganizationHandler=React.createClass({
getInitialState:function(){
return{data: {objects:[]}} // variable should be created with array
},
DeleteOrganizationFromServer: function(id) {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'url/'+id,
dataType: 'json',
type: 'Delete',
success: function(data) {
this.loadOrganizationFromServer();
}.bind(this),
error: function(xhr, status, err) {
console.error('url="url', status, err.toString());
}.bind(this)
});
},
loadOrganizationFromServer: function() {
$.ajax({
headers: { 'Accept': 'application/json',
'Content-Type': 'application/json'},
url: 'url',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('url="url', status, err.toString());
}.bind(this)
});
},
componentWillMount: function() {
this.loadOrganizationFromServer();
},render: function() {
return (
<div >
<OrganizationAPP onOrganizationSubmit={this.handleOrganizationSubmit} />
<OrganizationList external_DeleteOrganization={this.DeleteOrganizationFromServer} data= {this.state.data} />
</div>
);
}
});
var OrganizationList=React.createClass({
internal_DeleteOrganization: function(id) {
this.props.external_DeleteOrganization(id);
},
render:function(){
var results = this.props.data;
var parsed_results = results.objects;
var that = this; // Not equally that to this was also causing error,
var organizations = parsed_results.map(function(organization){
return <Organization onDeleteOrganization={that.internal_DeleteOrganization} id={organization.id} name={organization.name} description={organization.description} />
});
return(<div>
{organizations}
</div>)
}
});
var Organization = React.createClass({
handleDeleteClick: function () {
this.props.onDeleteOrganization(this.props.id);
},
});