I have a page with a form that is prepopulated with user information. It is a user profile page. I have validation for some of the fields in place but currently my validation method is just hard coded to execute addFieldError("exp", "Cats"); where exp is a variable that is being validated and Cats is a random message. The form has selects and a doubleselect which I am repopulating by executing actions in the jsp. (Seen Below)
This is the entire form:
<s:form action="%{formAction}" method="post" enctype="multipart/form-data">
<div id="buttons">
<s:submit value="Save" />
</div>
<div id="left_column" class="divStyle">
<div id="picture">
<div id="picture_border">
Picture should go here 150px x 150px
</div>
<s:file name="upload" label="File" />
</div>
<hr />
<div id="contact" class="divPad">
<h3>Contact Information</h3>
<s:textfield name="email" value="%{profile.email}" required="true" />
</div>
<hr />
<div id="availabilityDuration" class="divPad">
<h3>When Available</h3>
<s:textfield name="whenAvailable" value="%{profile.whenAvailable}" />
<h3>Availability Length</h3>
<s:textfield name="availabilityLength" value="%{profile.availabilityLength}" />
<h3>Desired Credit</h3>
<s:action name="CreditSelectAction" executeResult="true" />
</div>
</div>
<div id="right_column" class="divStyle">
<div id="basic_info" class="divPad">
<h4>College & Major</h4>
<s:action name="CollegeMajorsSelectAction" executeResult="true" />
<hr />
<h4>Years of Work Experience</h4>
<s:action name="ExpYearsSelectAction" executeResult="true" /> <hr />
<h4>Undergrad</h4>
<s:action name="UndergradSelectAction" executeResult="true" /> <hr />
<h4>Graduation Year</h4>
<s:action name="GradYearSelectAction" executeResult="true" />
</div>
<hr />
<div id="aboutDescription" class="divPad">
<h3>About Me</h3>
<s:textarea name="comments" value="%{profile.comments}" cols="40" rows="10" />
</div>
<hr />
<div id="skillsNeeds" class="divPad">
<h3>Skills</h3>
<div id="userSkillList">
<s:iterator value="profile.skills" status="status">
<div>
<h5 class="formLabels">Skill Description</h5>
<s:textfield name="userSkillDescription_%{#status.count}" value="%{description}" />
<h5 class="formLabels">Years of Experience</h5>
<s:textfield name="userSkillExperience_%{#status.count}" value="%{experience}"/>
<h5 class="removeSkillLink" onclick="removeUserSkill(this);">Remove Skill</h5>
</div>
</s:iterator>
<h5 class="addSkillLink" id="addSkill" onclick="addUserSkill();">Add New Skill</h5>
</div>
</div>
</div>
</s:form>
The dropdowns are populating alright. The problem is that the values that I thought would be saved in the value stack and retained upon reloading the jsp (%{formAction}, %{profile.email}, etc.) are not being retained when I reload the jsp. How do I capture these values and present them when the page is reloaded after the failed validation? I have tried adding them to the session, but that tends to get messy and I'm not sure how to get that to work with the formAction.
Code from struts.xml:
<action name="updateProfile" class="profiles.actions.UpdateProfileAction" method="execute">
<!-- <interceptor-ref name="basicStack"/>
<interceptor-ref name="fileUpload">
<param name="allowedTypes">image/jpeg,image/gif,image/png,image/jpg</param>
</interceptor-ref>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/> -->
<result name="success">/index.jsp</result>
<result name="input">/jsp/editProfile.jsp</result>
</action>
Code snippet from the Action that loads the form:
public String execute()
{
// get the user profile
String result = "success";
//If the profile is null, then the user is new and does not yet have a profile
//NOTE: If the user's profile doesn't exist, when trying to view someone else's
//profile, they will be redirected to edit their own.
if(user.intValue() == 0)
{
logger.info("New User Detected. Returning Failure.");
result = "failure";
}
else
{
//If the userid is null, we are loading the user's profile
//Otherwise, we are viewing someone else's profile
if(userid == null)
userid = user.toString();
profile = dao.selectCurUserById(Integer.parseInt(userid));
// get all of my projects
this.setMyProjects(projectDAO.selectMyProjects(Integer.parseInt(userid)));
// get all of the projects i've been invited to
this.setJoinedProjects(projectDAO.selectJoinedProjects(Integer.parseInt(userid)));
}
return result;
}
Code snippet from that Action that updates the user profile:
public String execute()
{
// request that sent from the client
HttpServletRequest request = ServletActionContext.getRequest();
Map<String, Object> session = ActionContext.getContext().getSession();
profile = new UserProfile();
id = ((authentication.beans.User) session.get("currentUser")).getId();
profile.setId(id);
profile.setEmail(email);
profile.setAvailabilityLength(availabilityLength);
profile.setComments(comments);
profile.setUndergrad(Integer.parseInt(undergrad));
profile.setWhenAvailable(whenAvailable);
profile.setYear(year);
profile.setCredit(credit);
profile.setMajor(Major.getMajor(major));
profile.setCollege(College.getCollege(college));
profile.setExp(exp);
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yy");
profile.setModify(sdf.format(new GregorianCalendar().getTime()));
//If file is not null, then the user is uploading an image for his/her
//profile
if(file != null)
{
byte[] b = new byte[(int) file.length()];
try
{
new FileInputStream(file).read(b);
b = Base64.encode(b);
}
catch (IOException e)
{
// TODO Auto-generated catch block
profile.setComments(profile.getComments() + "\n" + e.getMessage());
}
profile.setPic(b);
}
// get all of the params and then process the user skills
HashMap<String,String[]> params = (HashMap<String,String[]>)request.getParameterMap();
// process the user skills
profile.setSkills(processSkills(params));
// add the userid to the skills
for(int i = 0; i < profile.getSkills().size(); i++){
profile.getSkills().get(i).setUserID(profile.getId());
}
//TODO: Check the result and do error handling
boolean result = dao.updateProfile(profile);
return "success";
}
UPDATE
The problem was pretty much what coding_idiot said. In the action for loading the form, I needed to have getters for the information to initially populate the form (had this). In the action for updating the information, I needed to have setters for the information put into the form AND a getter for where to get the new information if the form should be repopulated after a failed validation. I fixed this by populating the profile object in the validate() function in the update action with the data I got from the form and then providing a setter for the profile object. I did not have to make any changes to my .jsp
I don't know what you've written in your action, but from above I can infer that this is a slight misunderstanding about functionality issue. You are having fields with names - email, whenAvailable etc. but their value is fetched from profile object.
How can you expect to set it from something that doesn't exist ?
To make it more clear :
Values that go from JSP to action is email, but what comes back is profile.email, instead it
should have been just email, or you can simply skip the value attribute or you can change your
field-name to profile.email
Let me know if that helps.
[EDIT]
Reading your question again, the getters and setters for profile won't help, because what
is going into the action is just primitive fields (email etc.) So you should have getters/setters for these fields instead and then to re-populate the fields into the JSP
you can simply use for e.g. :
<s:textfield name="whenAvailable" value="%{whenAvailable}" />
OR
<s:textfield name="whenAvailable"/>
Related
We have a Struts2 application where we are building survey feature using which application users will be able to create surveys by adding different questions.
These questions are comprised of question text and an html control for getting response.
The html controls so far supported are single and multi-select list, text field, textarea, checkbox and radiobox.
So the survey form renders displaying all questions added by the user where each question has its question text displayed followed by the html field control selected for that question.
Basically, it is a dynamic form where form field names are being dynamically generated as all surveys will be different and therefore there are no properties in the Action class backing survey form fields.
We are generating form field name using prefix question_ appended with database id of the question to represent each question response input field uniquely. Here is a snippet from our JSP page to make things clear.
<s:form id="surveyForm" action="survey/submitFeedback">
<s:iterator value="surveyQuestions">
<p class="form-group <s:if test="%{fieldErrors.get('question_' + surveyQuestionId).size() > 0}">has-error</s:if>" >
<label class="control-label" >
<s:property value="questionText"/>
</label>
<s:if test="required" ><span style='color:red'>*</span></s:if>
<br>
<s:if test="surveyQuestionType.type == #com.reach150.enumeration.SurveyQuestionTypeEnum#OPENENDED_TEXTFIELD" >
<s:textfield name="question_%{surveyQuestionId}" cssClass="form-control" maxlength="charactersLimit" />
</s:if>
<s:elseif test="surveyQuestionType.type == #com.reach150.enumeration.SurveyQuestionTypeEnum#OPENENDED_TEXTAREA" >
<s:textarea name="question_%{surveyQuestionId}" style="height: 150px; width: 400px;" cssClass="form-control" maxlength="charactersLimit" />
</s:elseif>
<s:elseif test="surveyQuestionType.type == #com.reach150.enumeration.SurveyQuestionTypeEnum#SINGLESELECTDROPDOWN || surveyQuestionType.type == #com.reach150.enumeration.SurveyQuestionTypeEnum#MULTISELECTDROPDOWN" >
<s:select name="question_%{surveyQuestionId}" list="orderedSelectOptions" listKey="optionValue" listValue="optionLabel" emptyOption="true" multiple="true" cssClass="form-control" />
</s:elseif>
<s:else>
<s:radio name="question_%{surveyQuestionId}" list="#{'true':'Yes','false':'No'}" cssClass="radioMarginRight" />
</s:else>
<span class="help-block" for="question_${surveyQuestionId}">
<s:fielderror cssClass="font-bold text-danger">
<s:param>question_<s:property value="surveyQuestionId" /></s:param>
</s:fielderror>
</span>
<br/>
</p>
</s:iterator>
<button type="submit" class="btn btn-primary btn-lg pull-right "><s:if test="survey.requestReferral == true">Next</s:if><s:else>Done</s:else></button>
</s:form>
On form submit, in the action class we are using HttpServletRequest to get submitted form field values. The way we identify which question the answer belongs to is through the request parameter name which as can be seen in the above JSP snippet starts with prefix 'question_' followed by question Id. So we split the parameter name to get the question id and associate value against that question.
The problem we are facing is with repopulating survey form with submitted values when the page is presented back to the user in case of validation error as the parameter names are dynamic and cannot be backed by properties defined in Action class.
I have tried to populate radio button and textarea fields using the below code and several other ways but to no avail
<s:textarea name="question_%{surveyQuestionId}" style="height: 150px; width: 400px;" cssClass="form-control" maxlength="charactersLimit" value="#parameters.%{'question_' + surveyQuestionId}" />
<s:radio name="question_%{surveyQuestionId}" value="#parameters.%{'question_' + surveyQuestionId}" list="#{'true':'Yes','false':'No'}" cssClass="radioMarginRight" />
Below is the action mapping for survey submit action
<action name="survey/submitFeedback" class="surveyAction" method="submitFeedback">
<result name="success" type="tiles">survey.submit</result>
<result name="error" type="tiles">survey.view</result>
<param name="public">true</param>
</action>
Here is the code in Action class handling the submit logic:
private Integer npsScore = 0;
private Map<String, String[]> surveyResponseQuestionAnswerMap = new HashMap<>();
public String submitFeedback() {
try {
if (requestId == null) {
addActionError("Request Id missing! Invalid Request!");
throw new Exception("Invalid Request!");
}
surveyRequest = surveyService.getSurveyRequestByUUID(requestId);
if (surveyRequest == null) {
addActionError("Request Id Invalid! Invalid Request!");
throw new Exception("Request Id Invalid! Invalid Request!");
}
loadQuestionAnswersMap();
validateSurveyFeedback();
if (hasErrors()) {
throw new Exception("Error submitting response!");
} else {
surveyService.parseAndSaveSurveyResponse(surveyRequest, surveyResponseQuestionAnswerMap);
setSurveyCustomMessages(surveyService.getSurveyCustomMessagesSettingBySurveyId(survey.getSurveyId()));
}
return SUCCESS;
} catch (Exception e) {
addActionError("Error submitting response!");
logger.error(e);
loadSurvey();
return ERROR;
}
}
private void loadQuestionAnswersMap() {
HttpServletRequest httpRequest = ActionUtil.getRequest();
Enumeration<String> parameterNames = httpRequest.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
if (parameterName.startsWith("question_")) {
String[] values = httpRequest.getParameterValues(parameterName);
if (values != null) {
surveyResponseQuestionAnswerMap.put(parameterName, values);
}
}
}
}
private void validateSurveyFeedback() throws Exception {
HttpServletRequest httpRequest = ActionUtil.getRequest();
Survey survey = surveyRequest.getSurvey();
if (survey.isUseNetPromotorScore()) {
String npsScoreStr = httpRequest.getParameter("npsScore");
if (StringUtils.isBlank(npsScoreStr)) {
this.addFieldError("npsScore", "Answer is required");
} else {
setNpsScore(Integer.valueOf(npsScoreStr));
}
}
List<SurveyQuestion> requiredQuestions = surveyQuestionService.getRequiredSurveyQuestionsForSurvey(surveyRequest.getSurvey());
for (SurveyQuestion requiredQuestion : requiredQuestions) {
Integer requiredQuestionId = requiredQuestion.getSurveyQuestionId();
String requiredQuestionFieldParameterName = "question_" + requiredQuestionId;
logger.info("Required Question Field Parameter Name: " + requiredQuestionFieldParameterName);
String[] answers = httpRequest.getParameterValues(requiredQuestionFieldParameterName);
if (answers == null) {
this.addFieldError(requiredQuestionFieldParameterName, "Answer is required");
} else {
boolean noValue = true;
for (String answer : answers) {
if (StringUtils.isNotBlank(answer)) {
noValue = false;
break;
}
}
if (noValue) {
this.addFieldError(requiredQuestionFieldParameterName, "Answer is required");
}
}
}
}
We have finally solved the problem. The initial implementation was heading in a completely wrong direction but thanks to the clue from #RomanC, we re-factored the code and removed the direct use of HttpServletRequest to finally have a working solution. Core idea was to use a Bean for capturing response and have a List of those beans in the Action class corresponding to each survey question. On form submit, response is captured into the bean objects behind the scene by framework itself and thus available for further logic processing in submit handler action method.
In a form I have a field called Description. Through CKEditor I need to pass that value and store it in my database. Can anyone help me out? Here is my code:
<div id="descriptionMore" style="margin-bottom:20px;margin-top: 38px;margin-left: 101px;">
<aui:layout>
<aui:column columnWidth="240">
<liferay-ui:input-editor width="880" cssClass="richText" />
</aui:column>
</aui:layout>
</div>
First of all, make sure you are using aui:form.
Link which will be useful for you.
Save Content of CKeditor via "aui:form"
Add HTML Editor to Portlet
CK editor in Liferay 6.0
Adding CK Editor in Liferay
This will defiantly be useful for you.
<%
Content content = (Content) request.getAttribute(ApplicationConstants.CONTENT);
%>
<portlet:actionURL var="saveOrUpdateContentUrl" name="saveOrUpdateContent">
<portlet:param name="redirect" value="<%=currentURL%>" />
</portlet:actionURL>
<liferay-ui:header title='<%= (content==null) ? "new-content" : "edit-content" %>' backURL="<%=redirect %>" />
<aui:form method="post" name="content_fm" onSubmit='<%= "event.preventDefault(); " + renderResponse.getNamespace() + "saveEntry();" %>'>
<aui:fieldset>
<aui:field-wrapper label="product-content">
<liferay-ui:input-editor />
<aui:input name="content" id="content" type="hidden" />
</aui:field-wrapper>
<aui:button-row>
<aui:button type="submit" />
</aui:button-row>
</aui:fieldset>
</aui:form>
<script type="text/javascript">
function <portlet:namespace />initEditor() {
return '<%= UnicodeFormatter.toString((content==null)? StringPool.BLANK : content.getContent()) %>';
}
function <portlet:namespace />getContent() {
return window.<portlet:namespace />editor.getHTML();
}
function <portlet:namespace />saveEntry() {
document.<portlet:namespace />content_fm.action = '<%=saveOrUpdateContentUrl%>';
document.<portlet:namespace />content_fm.<portlet:namespace />content.value = <portlet:namespace />getContent();
alert(<portlet:namespace />getContent());
submitForm(document.<portlet:namespace />content_fm);
}
</script>
This above code is properly working in Liferay 6.2. You should fetch CKEditor content using alloy script or javascript and set in hidden parameter for storing the value.
I have following situation in code:
Action class:
#NameSpace("/")
public class MyAction extends ActionSupport implements ModelDriven<Car> {
private Car car = new Cart();
#Override
public Car getModel() {
return car;
}
#Action(value = "pageAction", results = {name = SUCCESS, location = "myPage", type="tiles"})
public String showPage() {
return SUCCESS;
}
#Action(value = "formSubmitAction", results = {name = SUCCESS, location = "results.jsp"})
public String formSubmitAction() {
System.out.println(car);
// everything has default values (nulls)
return SUCCESS;
}
}
View for myPage location:
<s:form
namespace="/"
action="pageAction"
method="post" >
<s:push value="model">
<s:textfield name="color" />
<s:textfield name="manufacturer" />
<sj:submit
href="formSubmitAction"
targets="output" />
</s:push>
</s:form>
<div id="output"></div>
results.jsp:
renders empty content into div#output
<s:property value="%{model}" />
<s:property value="%{model.color}" />
<s:property value="%{model.manufacturer}" />
I wonder why is that happening? Model data is not updated after submit.
I'm using struts2-jquery submit tag.
When I'm using simple form submit without Ajax the model is being updated,
but I want to load data asynchronously with Ajax.
How can I achieve that?
The solution is to add ID to form and to sj:submit tag. But I don't know why submit tag inside form wasn't working properly. The correct code is below:
<s:form
id="formId"
namespace="/"
action="pageAction"
method="post" >
<s:push value="model">
<s:textfield name="color" />
<s:textfield name="manufacturer" />
<sj:submit
formIds="formId"
href="formSubmitAction"
targets="output" />
</s:push>
</s:form>
EDIT
As it turns out you only have to add ID to form, and everything works :)
look at link in the comment below
The modelDriven interceptor pushes a model on top of the valueStack. So you can access model properties directly.
<s:property value="%{color}" />
<s:property value="%{manufacturer}" />
Problem
I am using jsp to submit a form and struts 2 action class takes care of it. If there is some problem, then i am sending the result to same page with an error message.
Along with the error message, i want to display property values that he had provided while submitting the request.
Source code
Form contains few text fields and few file type inputs.
My CreateRequest.jsp file:
<input type="file" name="attachment" id="myFile1" />
<input type="file" name="attachment" id="myFile2" />
<input type="file" name="attachment" id="myFile3" />
<input type="text" name="operationName" id="operation1" />
<input type="text" name="operationName" id="operation2" />
<input type="text" name="operationName" id="operation3" />
My Action class :
public class CreateRequest extends ActionSupport {
private List<File> attachment;
private List<String> attachmentContentType;
private List<String> attachmentFileName;
private List<String> operationName
// contains getter and setter for each property
public string execute(){
// some logic
//returns error if it fails otherwise success
}
}
struts.xml (Action Servlet) file:
<action name="createRequest"
class="action.CreateRequest">
<result name="success">RequestStatus.jsp
</result>
<result name="input" >CreateRequest.jsp</result>
<result name="error" >CreateRequest.jsp</result>
</action>
HELP
How do i get all those values displayed in CreateRequest.jsp page, when the action class returns error.
use ognl value=" %{operationName[0]}" for text box
I am using onclick event in anchor tag. when i clcik the anchor, I am invoking a java script function where I change the value of one input element and submit the form. The value is changed but the form does not submit the value. Please help me on this. FYI that if i create an element and add in the form before submission, it works. Why is not working in the first scenario.
<form name="form" >
<input type="hidden" name="input" value="test"/>
<a onclick='testMethod("test")'>click </a>
</form>
script used is
function testMethod(value)
{
if(serviceName != null && jQuery('#form') != null)
{
document.forms['form'].getElementById('input').value = value;
document.forms['form'].action = jQuery('#newAction').val();
document.forms['form'].submit();
}
}
The main problem here is you're trying to access the input by id when it doesn't have one set:
(Added id to form and id to input so we can select them easily):
<form name="form" id="form">
<input type="hidden" name="input" id="input" value="test" />
<a onclick='testMethod("test")' >click</a>
</form>
And the javascript to match (updating to use jQuery's selectors since you indicated you have jQuery support available):
function testMethod(value)
{
var form = $('#form');
if(serviceName != null && form.length)
{
$('#input').val(value);
form.attr('action', $('#newAction').val());
form.submit();
}
}
You can also update your code such that you aren't including the binding to the onclick method in the DOM, but attaching to it directly in javascript:
Changing the a element to have an ID:
<form name="form" id="form">
<input type="hidden" name="input" id="input" value="test" />
<a id="submitLink">click</a>
</form>
The javascript could now look like this:
$(document).ready(function() {
$('#submitLink').on('click', function(event) {
event.preventDefault();
var form = $('#form');
if(serviceName != null && form.length)
{
$('#input').val(value);
form.attr('action', $('#newAction').val());
form.submit();
}
});
});