Uncaught TypeError: Cannot read property 'responseText' of undefined - extjs

buttons: [
{
text: "Add User",
id: "new-record-add-button",
handler: function() {
var form = this.up('form').getForm();
form.submit({
url: BasePath+'/main/admin/adduser',
method: 'POST',
waitTitle: 'Authenticating',
waitMsg: 'Please Wait',
success: function(form, action) {
win.close()
Ext.Msg.show({
title:'Success'
,msg:'User added successfully'
,modal:true
,icon:Ext.Msg.INFO
,buttons:Ext.Msg.OK
});
},
failure: function(form, action) {
console.log(action.response.responseText);
obj = Ext.JSON.decode(action.response.responseText);
console.log(obj);
Ext.Msg.alert('Error',obj.errors)
form.reset();
}
})
//this.up("window").close();
}
},
{
text: "Cancel",
handler: function() {
this.up("window").close();
}
}
]
I am getting the following error when I reach the failure function in my form:
Uncaught TypeError: Cannot read property 'responseText' of undefined
This is my php code:
public function adduserAction()
{
$response = new JsonModel();
//$error = array();
$errors="";
if(!ctype_alpha($_POST["first_name"])) {
$errors.="First Name cannot contain characters and numbers";
}
if(!ctype_alpha($_POST["last_name"])) {
$errors.="Last Name cannot contain characters and numbers";
}
if(!filter_var($_POST['email_address'], FILTER_VALIDATE_EMAIL)) {
$errors.="Email should be of the format john.doe#example.com";
}
if(empty($_POST["role"])) {
$errors.="Role cannot be empty";
}
if($errors!="") {
$response->setVariables(array("success"=>false, "errors"=>$errors));
}
else {
$response->setVariables(array("success"=>true, "errors"=>$errors));
}
return $response;
}

responseText is an ExtJs thing - it represents the actual text returned from the server (eg, using echo) before being decoded.
You should get it in asynchronous callbacks within the operation or request objects, unless there was a server exception of a sort or if you set success to false, that's why you don't get it in the failure handler.
To really understand what's going on with it I recommend you have a look at Connection.js.

if you do a form submit through ExtJs, then on success of the form submission it needs response.responseText to be set as {"sucess":"true"}. if you are submitting the form to some page you have to make sure that you will be returning this object from backend. Or else you have to override the existing onSuccess method.
The second way is something like this,
Ext.override(Ext.form.action.Submit,{
onSuccess: function(response) {
var form = this.form,
success = true,
result = response;
response.responseText = '{"success": true}';
form.afterAction(this, success);
}
});
Place this snippet in your application and see the magic. Cheers. :)

Related

CSRF token from either the request body or request headers did not match or is missing - cake php 4 [duplicate]

I have a project in Cakephp 3.6 in which 3 actions in MessageController are called by Ajax. I have a problem, however, when I send a request to one of the action, XHR returns to me this:
{
"message": "CSRF token mismatch.",
"url": "\/messages\/changepriority\/8",
"code": 403,
"file": "D:\\xampp\\htdocs\\myapp\\vendor\\cakephp\\cakephp\\src\\Http\\Middleware\\CsrfProtectionMiddleware.php",
"line": 195
}
This is one of the action what I try to call from Ajax:
public function changepriority($id=null)
{
$this->autoRender = false;
$message = $this->Messages->get($id);
$message->priority = ($message->priority === false) ? true : false;
if ($this->Messages->save($message)) {
echo json_encode($message);
}
}
And this is my ajax:
$(".email-star").click(function(){
var idmessage = this.id;
$.ajax({
headers : {
'X-CSRF-Token': $('[name="_csrfToken"]').val()
},
dataType: "json",
type: "POST",
evalScripts: true,
async:true,
url: '<?php echo Router::url(array('controller'=>'Messages','action'=>'changepriority'));?>' +'/'+idmessage,
success: function(data){
if(data['priority'] === false) {
$("#imp_" + idmessage).removeClass("fas").removeClass('full-star').addClass( "far" );
}
else {
$("#imp_" + idmessage).removeClass("far").addClass( "fas" ).addClass("full-star");
}
}
});
});
I have read the documentation about Cross Site Request Forgery, and I tried to turn off the Csrf for these action first with:
public function beforeFilter(Event $event)
{
$this->getEventManager()->off($this->Csrf);
}
and then with:
public function beforeFilter(Event $event)
{
$this->Security->setConfig('unlockedActions', ['index', 'changepriority']);
}
But nothing. The Xhr return always the CSRF token mismatch.
What can I do ?
Edit:
I change the action in this way:
public function changepriority($id=null)
{
$this->autoRender = false;
$message = $this->Messages->get($id);
$message->priority = ($message->priority === false) ? true : false;
if ($this->Messages->save($message)) {
$content = json_encode($message);
$this->response->getBody()->write($content);
$this->response = $this->response->withType('json');
return $this->response;
}
}
In that way the action works. Can it be like that?
First check your $('[name="_csrfToken"]').val() output.
If you didn't get any output, need to check csrfToken hidden field is exist or not. Just right click in your page and click View Page Source
If not exist, you don't follow proper way when you create Form. Basically, when forms are created with the Cake\View\Helper\FormHelper, a hidden field is added containing the CSRF token.
If everything is correct, add the following line inside your ajax call after header
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},
Ps. Disabling the CSRF is not recommended by cakePHP and most of the developer aware of this. Hope this help.
beforeSend: function (xhr) {
xhr.setRequestHeader('X-CSRF-Token', <?= json_encode($this->request->getAttribute('csrfToken')) ?>);
},

backbone-forms remove built-in validators in runtime

I have successfully implemented backbone-forms plug-in with it's validators, for example:
var SampleModel = Backbone.Model.extend({
schema: {
field1: {
title: $t.field1, validators: ['required', 'number']
},
field2: {
title: $t.field2, type: 'Select', options: $options.field2, validators: ['required']
},
notes: {
title: $t.notes
}
}
});
Now I am trying to find "right" (at this moment - any) way to disable built-in validators, on, for example, some check box click. After checkbox is clicked, allow form to be saved without validation.
I tried to rebuild this.model.schema for each field without validators and after did this.model.form.commit(), but it did nothing.
Can you, please, give some advice?
EDIT:
At now, I am using "dirty" method adding additional argument into commit method. See Backbone-forms commit method source:
commit: function(options, dontValidate) {
//Validate
options = options || {};
var validateOptions = {
skipModelValidate: !options.validate
};
// DIRTY
if(!dontValidate) {
var errors = this.validate(validateOptions);
if (errors) return errors;
}
//Commit
var modelError;
var setOptions = _.extend({
error: function(model, e) {
modelError = e;
}
}, options);
this.model.set(this.getValue(), setOptions);
if (modelError) return modelError;
},

how can I update a model with custom idAttribute

in my simple backbone application, I am trying to update a model and every time it send a put request instead of post.
Well, this is my model named categoryModel
define(['Backbone'], function (Backbone) {
var CategoryModel = Backbone.Model.extend({
defaults: {
ID: '',
Name: 'Empty',
TagID: '0',
GID: '0'
},
idAttribute: "ID",
initialize: function () {
if (!this.get('Name')) {
this.set({ 'Name': this.defaults.Name });
}
}
});
return CategoryModel;
});
this is the collection
define(['Backbone','../../models/categories/categoryModel'], function (Backbone, categoryModel) {
var CategoryCollection = Backbone.Collection.extend({
url: '/parentcategory/Actions',
model: categoryModel
});
return new CategoryCollection;
});
here are my methods in the view
on a keychange event
createNewItem: function (e) {
var $this = $(e.currentTarget);
$('#selectedCategoryName').html($this.val());
//it creates a new model
globals.NewCategory = new CategoryModel({ Name: $this.val() });
}
on handleDrop event
handleDropEvent: function (event, ui) {
var draggable = ui.draggable;
//check if name has set
if (!globals.NewCategory) {
alert("Please write a category name");
$('#createNewCategory').focus();
return;
}
//get itemID
var itemID = draggable.attr("id").split('_')[1];
var itemDesc = draggable.attr("id").split('_')[0];
//check items category
if (itemDesc == "Tag") {
//check if tagID already exists
if (globals.NewCategory.TagID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("TagID", itemID);
} else if (itemDesc == "gTag") {
if (globals.NewCategory.GID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("GID", itemID);
}
categoriesCollection.create(globals.NewCategory, {
silent: true,
wait: true,
success: function (model, response) {
model.set("ID", response);
alert(model.id);
}
});
}
The categoriesCollection.create is called twice. Firstly for setting the TagID (on a success request it gets an ID ) and secondly for setting the GID.
Since the ID has been set, shouldn't had sent a POST request instead of PUT on the second call?
What am I doing wrong?
Thanks
The standard behaviour is to send a POST if the model is new ( doesn't have an ID attributed ) and send a PUT if the model id is set.
In your case it's working as designed, if you want it to use POST to send UPDATES you have to override Backbone.sync to work as you need, but I think it's easier for you to make your backend RESTful and create a PUT listener controller method for updates.
Another thing, if I got it right you are using create() to update models in your collection, I would advise you not to do that and instead use the save() directly in the model you want to update, the code will be a lot more readable.
Cheers.

Ext.data.HttpProxy callback on failure

I've the following ExtJS. The listener "write" is called when the response is a success (the response is JSON like: {"success":true,"message":"......"}). But how do I attach a callback when the response is not a success? ({"success":false,"message":"......"})
tableStructure.proxy = new Ext.data.HttpProxy({
api: {
read: '/controller/tables/' + screenName + '/getstructure/' + table,
create: '/controller/tables/' + screenName + '/createcolumn/' + table,
update: '/controller/tables/' + screenName + '/updatecolumn/' + table,
destroy: '/controller/tables/' + screenName + '/destroycolumn/' + table
},
listeners: {
write: tableStructure.onWrite
}
});
You want to catch the HttpProxy's exception event.
listeners: {
write: tableStructure.onWrite
exception: function(proxy, type, action, options, response, arg) {
if(type === 'remote') { // success is false
// do your error handling here
console.log( response ); // the response object sent from the server
}
}
}
You can find the full documentation in the Ext docs for Ext.data.HttpProxy down in the events section.
You should be able to make use of the write event itself. The write event's signature is:
write(dataproxy,action,data,response,record,options).
You can access the success variable from the action object and check if the value is true or false. You should be able to access the success variable as:
action.result.success
You can do:
if(action.result.success != true ) {
// If success is not true
} else {
// If success is true
}
You can also set an exception handler on the Ext.data.Store wrapping the HttpProxy, provided that you send a response code other than 200.
var store = new CQ.Ext.data.Store({
proxy : new CQ.Ext.data.HttpProxy({
method : "GET",
url : '/some_url'
}),
reader : new CQ.Ext.data.JsonReader(),
baseParams : {
param : 'some value'
}
});
store.on("beforeload", function() {
CQ.Ext.getBody().mask("Please wait...", false);
});
store.on("exception", function() {
CQ.Ext.getBody().unmask();
CQ.Ext.Msg.show({
title: 'Error',
msg: '<span style="color:red">Bad request.</span><br/>',
icon: CQ.Ext.Msg.ERROR,
buttons: CQ.Ext.Msg.OK
});
});

persisting filters in grid panel

I would like to persist filters applied on gridpanel on page refresh. Can you please guide me in doing this.
Thanks.
Here is the code which send the filter data to webservice
Ext.extend(Ext.ux.AspWebServiceProxy, Ext.data.DataProxy,
{
load: function(params, reader, callback, scope, arg) {
var userContext = {
callback: callback,
reader: reader,
arg: arg,
scope: scope
};
var proxyWrapper = this;
//debugger;
//Handles the response we get back from the web service call
var webServiceCallback = function(response, context, methodName) {
proxyWrapper.loadResponse(response, userContext, methodName);
}
var serviceParams = [];
var filters = {};
//Convert the params into an array of values so that they can be used in the call (note assumes that the properties on the object are in the correct order)
for (var property in params) {
if (property.indexOf("filter[") == 0) {
filters[property] = params[property];
}
else {
serviceParams.push(params[property]);
}
//console.log("Property: ", property, "Value: ", params[property]);
}
serviceParams.push(filters);
//Add the webservice callback handlers
serviceParams.push(webServiceCallback);
serviceParams.push(this.handleErrorResponse);
//Make the actual ASP.Net web service call
this.webServiceProxyMethod.apply(this.webServiceProxy, serviceParams);
},
handleErrorResponse: function(response, userContext, methodName) {
window.location.reload();
// Ext.MessageBox.show({
// title: 'Error',
// msg: response.get_message(),
// buttons: Ext.MessageBox.OK,
// icon: Ext.MessageBox.ERROR
// });
//alert("Error while calling method: " + methodName + "n" + response.get_message());
},
loadResponse: function(response, userContext, methodName) {
var result = userContext.reader.readRecords(response);
userContext.callback.call(userContext.scope, result, userContext.arg, true);
}
});
Turn on the Ext JS state manager globally (where you set Ext.BLANK_IMAGE_URL).
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
User changes to some components will now be stored in a cookie, which will persist across requests. If you need to store additional custom data, you can do that using Ext.state.Manager.set and Ext.state.Manager.get. State is configurable on individual components.
Saki has a good example.
To persist filters on grid you can use cookies, here you can find some help:
proxy: new Ext.data.HttpProxy({
url: (local ? url.local : url.remote),
method: 'GET',
listeners:{
beforeload : function(dataproxy,param) {
if(param.searchConditions != undefined && param.searchConditions != '[]') {
Ext.util.Cookies.set('SearchConditions',param.searchConditions);
}
}
}
})
In above sample you can find that we are setting "searchConditions" JSONArray in cookies.Now let us see how to get back that "searchCondition" whenever you load you Grid.
store.load({
params:{
start:0,
limit: 50,
searchConditions:JSON.parse(Ext.util.Cookies.get('SearchConditions'));
}
});
Here simply you just need to pass your "searchCondition" parameter value as value stored in Cookie. Hope above example is useful.Please comment for any help.

Resources