Struts 2 won't transfer params from ExtJS form to ActionSupport class - forms

I am implementing a simple ExtJS form that submits to a Struts 2 ActionSupport class. The code for the various components is as follows:
MyAction.java:
//packaging and imports
public class MyAction extends ActionSupport {
private String aField;
private String anotherField;
public String execute() throws Exception {
System.out.println(afield + " " + anotherField); //just checking values, atm
return ActionSupport.SUCCESS;
}
public String getAField() {
return this.aField;
}
public void setAField(String aField) {
this.aField = aField;
}
public String getAnotherField() {
return this.anotherField;
}
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
}
myForm.js:
Ext.onReady(function() {
Ext.QuickTips.init();
// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';
var myForm = new Ext.form.FormPanel({
id: 'myFormId',
url: 'submitMyForm.action',
defaults: {
xtype: 'textfield'
},
items: [
{
fieldLabel: 'A Field',
id: 'aField',
name: 'aField',
allowBlank: false
},
{
fieldLabel: 'Another Field',
id: 'anotherField',
name: 'anotherField',
allowBlank: false
}
],
renderTo: 'contentMain'
});
var submitButton = new Ext.Button({
text: 'SUBMIT',
handler: function(button, event) {
myForm.getForm().submit({
url: 'submitMyForm.action',
failure: function() {
Ext.Msg.alert('Error', 'Can not save data.');
}
});
}
});
});
struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="myPackage" namespace="/" extends="json-default">
<action name="submitMyForm" class="mycodepackage.MyAction">
<result name="*" type="json">
<param name="includeProperties">aField</param>
</result>
</action>
</package>
</struts>
When the submit button is pressed, my action executes properly, and in addition to standard debugging data prints out:
null null
The JSON result is sent back correctly, but of course is also null:
14:22:17,046DEBUG JSONResult:68 - Adding include property expression: aField
14:22:17,052DEBUG JSONWriter:68 - Ignoring property because of include rule: anotherField
14:22:17,053DEBUG JSONUtil:68 - [JSON]{"aField":null}
Now, it's my understanding that the values entered in the form should be inserted into the instance variables for my action class. Am I wrong in this? If not, what could be going wrong? If so, what can I do to ensure that the form data is sent to my action handler?
Thanks.

Any parameters sent will be placed into the similarly named setters. Why don't you first check that the form parameters are getting sent correctly with LiveHttpHeaders Firefox plugin.

Once we realized the form data was not being correctly passed into the http request, my coworker developed a form data interceptor, which we use to load the data in manually. For more information, look into the <interceptor>, <interceptor-stack>, and <interceptor-ref> tags.

Related

Drupal 9 : Ajax process to display an entity form in a popup

I need help I am new in Drupal 9 Ajax development. I have developed a piece of code but it doesn't work.
To sumarise, I want to display an entity form in a popup. The popup is built dynamically with jQuery, I use Ajax to fill the popup and the form is built in an ajax command (the code is : \Drupal::service('entity.form_builder')->getForm($livraison, 'edit');)
The form is well displayed but I think I miss something in ajax developpement because when I submit the form, the entity is not saved. When I look at the javascript console log, I see 3 others command responses plus the command I have created in server side (modifLivraison).
result: Array(4) [ {…}, {…}, {…}, {…} ]
​0: Object { command: "settings", settings: {…}, merge: true }
1: Object { command: "insert", method: "prepend", selector: "head", … }
2: Object { command: "insert", method: "append", selector: "body", … }
3: Object { command: "modifLivraison", modif: "...
I think that the Entity form needs some javascript and that's why these 3 commands have been added.
I don't know if my ajax process is correct.
Here my code :
In the command class:
class ModifLivraisonCommand implements CommandInterface
{ protected livraisonId; ...
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render()
{
...
$entitytype_manager = \Drupal::service('entity_type.manager');
$storage = $entitytype_manager->getStorage('livraison');
$livraison = $storage->load($this->livraisonId);
...
$livraison_form = \Drupal::service('entity.form_builder')->getForm($livraison, 'edit');
$livraison_form['#cache']['max-age'] =0;
$output = \Drupal::service('renderer')->render($livraison_form);
return [
'command' => 'modifLivraison',
'modif' => $output
];
...
The controller :
public function modifLivraison($livraison) {
$response = new AjaxResponse();
$response->addCommand(new ModifLivraisonCommand($livraison));
return $response;
}
and the javascript in the client side :
create_popup('0,0,0,0')
...
jQuery.ajax('/livraisons/modif-livraison/' + livraison, {
success: function(result,textStatus, jqXHR) {
jQuery(".spinner").remove();
console.log(result);
var ajax;
Drupal.AjaxCommands.prototype.settings(ajax,result[0],200)
jQuery(result[1].selector)[result[1].method](result[1].data);
jQuery(result[2].selector)[result[2].method](result[2].data);
...
jQuery("#modif_livraison").append(result[3].modif);
...
}
})
I think the error is in the way of ajax calling of the url (jQuery.ajax('/livraisons/modif-livraison/' + livraison ...)
My problem is the entity is not saved when the form is submitted. What's wrong ?

How to use dynamic page titles in sails.js >v1.0?

For the last few days I was looking for a viable solution in order to optimize html page titles <title>SOME_TITLE</title> within sails.js layout files, like layout.ejs, that by default use a static page title.
Obviously, it would be way better to have dynamic page titles, e.g. Dashboard, Shopping Cart, etc...
Other people were looking for this answer before and got answers for prior sails versions in Solution 1, Solution 2 and Solution 3.
Unfortunately, none of them seem to be appropriate for the latest version of sails.js (as of this post).
Solution 1 was leading in the right direction and suggested what I was looking for. But you had to define a title for every controller and pass it into the view. Otherwise you will get
title is not defined at eval
So how to define a local variable that is accessible in each controller/view by default?
So one working complete solution for the current sails.js version is the following:
In your layout.ejs file define a dynamic page title like this
<head>
<title>
<%= title %>
</title>
...
</head>
...
Create a new custom hook, e.g. api/hooks/dynamic-page-title/index.js
module.exports = function dynamicPageTitleHook(sails) {
return {
routes: {
/**
* Runs before every matching route.
*
* #param {Ref} req
* #param {Ref} res
* #param {Function} next
*/
before: {
'/*': {
skipAssets: true,
fn: async function(req, res, next){
// add page title variable to each response
if (req.method === 'GET') {
if (res.locals.title === undefined) {
res.locals.title = 'plusX';
}
}
return next();
}
}
}
}
};
};
Now overwrite the page title in every controller that should use a custom page title, e.g. view-login.ejs
module.exports = {
friendlyName: 'View login',
description: 'Display "Login" page.',
exits: {
success: {
viewTemplatePath: 'pages/entrance/login',
},
redirect: {
description: 'The requesting user is already logged in.',
responseType: 'redirect'
}
},
fn: async function (inputs, exits) {
if (this.req.me) {
throw {redirect: '/'};
}
return exits.success({title: 'Login'});
}
};
module.exports = {
friendlyName: 'View homepage or redirect',
description: 'Display or redirect to the appropriate homepage, depending on login status.',
exits: {
success: {
statusCode: 200,
description: 'Requesting user is a guest, so show the public landing page.',
viewTemplatePath: 'pages/homepage'
},
redirect: {
responseType: 'redirect',
description: 'Requesting user is logged in, so redirect to the internal welcome page.'
},
},
fn: async function () {
if (this.req.me) {
throw {redirect:'/welcome'};
}
return {title: 'Home page'};
}
};
e.g: return (title: 'Home page')
I use version 1.4 of sails
I have add to the file layout.ejs the following code
<% if (typeof pagetitle=="undefined"){%>
<title>write your default title</title>
<% }else{%>
<title><%= pagetitle %></title>
<%}%>
<% if (typeof pagemetadescription=="undefined"){%>
<mеtа nаmе="dеѕсrірtіоn" соntеnt="write your default metadescription"></mеtа>
<% }else{%>
<mеtа nаmе="dеѕсrірtіоn" соntеnt="<%= pagemetadescription %>"></mеtа>
<%}%>
If in controller, i return variable pagetitle or pagedescription, they will be add to layout. Otherwise, the default value will be print.

Route attributes overriding route

Here's my route:
routes.MapRoute("Login", "", new { action = "Login", controller = "Authentication"})
.DataTokens = new RouteValueDictionary(new { area = "Authentication" });
routes.MapMvcAttributeRoutes();
Here is the controller with action:
[RouteArea("Authentication", AreaPrefix = "auth")]
[Route("{action=Login}")]
public class AuthenticationController : BaseController
{
[HttpGet]
[AllowAnonymous]
public ActionResult Login()
{
...
If I comment out routes.MapMvcAttributeRoutes(); then I can request the Login action using '/'. If its left in then this route doesn't work and I get a 404.
How do I make the default route for the website be the login page form the area?
The area is being registered independently of the attributes.
I removed the convention route and added the following to <system.web> in web.config:
<urlMappings enabled="true">
<clear />
<add url="~/" mappedUrl="~/auth" />
</urlMappings>

Ext.Direct File Upload - Form submit of type application/json

I am trying to upload a file through a form submit using Ext.Direct, however Ext.direct is sending my request as type 'application/json' instead of 'multipart/form-data'
Here is my form.
{
xtype: 'form',
api: {
submit: 'App.api.RemoteModel.Site_Supplicant_readCSV'
},
items: [
{
xtype: 'filefield',
buttonOnly: false,
allowBlank: true,
buttonText: 'Import CSV'
}
],
buttons:
[
{
text: 'Upload',
handler: function(){
var form = this.up('form').getForm();
if(form.isValid()){
form.submit({
waitMsg: 'Uploading...',
success: function(form, action){
console.log(action.result);
}
});
}
}
}
]
},
On the HTTP request, it checks to see if the request options is a form upload.
if (me.isFormUpload(options)) {
which arrives here
isFormUpload: function(options) {
var form = this.getForm(options);
if (form) {
return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
}
return false;
},
getForm: function(options) {
var form = options.form || null;
if (form) {
form = Ext.getDom(form);
}
return form;
},
However, options looks like this
{
callback: function (options, success, response) {
jsonData: Object
action: "RemoteModel"
data: Array[1]
0: form
length: 1
__proto__: Array[0]
method: "Site_Supplicant_readCSV"
tid: 36
type: "rpc"
__proto__: Object
scope: constructor
timeout: undefined
transaction: constructor
}
And there is no direct form config, but it exists in jsonData.data[0]. So it doesn't set it as type multipart/form-data and it gets sent off as type application/json.
What am I doing wrong? Why isn't the form getting submitted properly?
Edit - I am seeing a lot of discussion about a 'formHandler' config for Ext.Direct? I am being led to assume this config could solve my issue. However I don't know where this should exist. I'll update my post if I can find the solution.
Solution - Simply adding /formHandler/ to the end of the params set the flag and solved my issue. Baffled.
Supplicant.prototype.readCSV = function(params,callback, request, response, sessionID/*formHandler*/)
{
var files = request.files;
console.log(files);
};
The method that handles file upload requests should be marked as formHandler in the
Ext.Direct API provided by the server side.
EDIT: You are using App.api.RemoteModel.Site_Supplicant_readCSV method to upload files; this method needs to be a formHandler.
I'm not very familiar with Node.js stack but looking at this example suggests that you may need to add /*formHandler*/ descriptor to the function's declaration on the server side.

JSON object parsing error using jQuery Form Plugin

Environment: JQuery Form Plugin, jQuery 1.7.1, Zend Framework 1.11.11.
Cannot figure out why jQuery won't parse my json object if I specify an url other than a php file.
The form is as follows:
<form id="imageform" enctype="multipart/form-data">
Upload your image <input type="file" name="photoimg" id="photoimg" />
<input type="submit" id ="button" value="Send" />
</form>
The javascript triggering the ajax request is:
<script type="text/javascript" >
$(document).ready(function() {
var options = {
type: "POST",
url: "<?php $this->baseURL();?>/contact/upload",
dataType: 'json',
success: function(result) {
console.log(result);
},
error: function(ob,errStr) {
console.log(ob);
alert('There was an error processing your request. Please try again. '+errStr);
}
};
$("#imageform").ajaxForm(options);
});
</script>
The code in my zend controller is:
class ContactController extends BaseController {
public function init() {
/* Initialize action controller here */
}
public function indexAction() {
}
public function uploadAction() {
if (isset($_POST) and $_SERVER['REQUEST_METHOD'] == "POST") {
$image = $_FILES['photoimg']['tmp_name'];
$im = new imagick($image);
$im->pingImage($image);
$im->readImage($image);
$im->thumbnailImage(75, null);
$im->writeImage('userImages/test/test_thumb.jpg');
$im->destroy();
echo json_encode(array("status" => "success", "message" => "posted successfully"));
}
else
echo json_encode(array("status" => "fail", "message" => "not posted successfully"));
}
}
When I create an upload.php file with the above code, and modify the url from the ajax request to
url: "upload.php",
i don't run into that parsing error, and the json object is properly returned. Any help to figure out what I'm doing wrong would be greatly appreciated! Thanks.
You need either to disable layouts, or using an action helper such as ContextSwitch or AjaxContext (even better).
First option:
$this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout->disableLayout();
And for the second option, using AjaxContext, you should add in your _init() method:
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('upload', 'json')
->initContext();
This will disable automatically disable layouts and send a json header response.
So, instead of your two json_encode lines, you should write:
$this->status = "success";
$this->message = "posted successfully";
and
$this->status = "fail";
$this->message = "not posted successfully";
In order to set what to send back to the client, you simply have to assign whatever content you want into view variables, and these variables will be automatically convert to json (through Zend_Json).
Also, in order to tell your controller which action should be triggered, you need to add /format/json at the end of your URL in your jQuery script as follow:
url: "<?php $this->baseURL();?>/contact/upload/format/json",
More information about AjaxContext in the manual.
Is the Content-type header being properly set as "application/json" when returning your JSON?