Handling form with repeatable inputs - grails

Before i start with my question, i will try to explain on what i'm trying to do.
I'm creating a form using Grails, and in the form, there are 2 sections on it, the primary input and the repeatable input. See below for ideas on how the structure looks like.
<div class="primary">
<div class="${hasErrors(bean: dataInputInstance, field: 'mainInput', 'error')}">
<label class="control-label">
<g:message code="dataInput.mainInput.label" default="Main Input"/>
</label>
<div class="controls">
<g:textField name="mainInput" value="${dataInputInstance?.mainInput}" />
</div>
</div>
</div>
<div class="repeatable">
<div class="span6">
<div class="${hasErrors(bean: dataInputInstance, field: 'inputA', 'error')}">
<label class="control-label">
<g:message code="dataInput.InputA.label" default="Input A"/>
</label>
<div class="controls">
<g:textField id='inputA_1' name="inputA" value="${dataInputInstance?.inputA}" />
</div>
</div>
</div>
<div class="span6">
<div class="${hasErrors(bean: dataInputInstance, field: 'inputB', 'error')}">
<label class="control-label">
<g:message code="dataInput.InputB.label" default="Input B"/>
</label>
<div class="controls">
<g:textField id='inputB_1' name="inputB" value="${dataInputInstance?.inputB}" />
</div>
</div>
</div>
The repeatable part of the input can be repeated from 1-50 times. The number of instances generated and saved in the database depends on how many repeated form created. Each instance is a combination of primary input + repeated input.
If say, i created 5 repeated sections, then when saving the form, the params will look like below
params:[mainInput: valMain, inputA:[valA1, valA2, valA3, valA4, valA5], inputB :[valB1, valB2, valB3, valB4, valB5]]
In the controller, i used the following when i try to form the instances and save it to the database
def save() {
def length = inputA.size()
def i
if (i = 0; i < length; i++){
DataInput dataInputInstance = new DataInput()
dataInputInstance.mainInput = params.mainInput.trim()
dataInputInstance.inputA = params.inputA[i].trim()
dataInputInstance.inputB = params.inputB[i].trim()
dataInputInstance.save(flush:true)
}
}
So far i managed to get that part correct.
Now here comes the problem.
Inside the save controller, i plan to include some data checking before i save it to the database. If checking is success, then it will proceed to save the data. If its not, then it will return back to the form page, together with the data that user input in the form. The code for that is as follow
if (insert checking condition here){
render(view: "create", model: [dataInputInstance: dataInputInstance]) //If fail
}
If the amount of repeatable part is 1, then the form will render along with the data that the user input. However if the repeatable part is more than 1, then it will have trouble to pass the data back to the form.
I tried to pass back the params back to the form because i thought since the params contain arrays on it, so it would be logical to pass back the params to the form, like below
if (insert checking condition here){
render(view: "create", model: [dataInputInstance: params]) //If fail
}
But still not working.
So anyone have any idea on this?

I'm looking at this block where you are setting dataInputInstance.inputA to be a single value and not the entire list object. Is this what you intended?
dataInputInstance.inputA = params.inputA[i].trim()
dataInputInstance.inputB = params.inputB[i].trim()
Could you post your view GSP code if this is not the cause? Need to see how are you reading the values from your view on validation failure.

Related

Boostrap 5, is-valid and empty field howto?

From the documentation (it's what I've understood): when a form is posted to our server, we check it, if it's not valid, me return it. In such case, we must add class="was-validated" to the form, and we me must precise for all fields, whether it's good (add class is-valid) or not (add class is-invalid).
In our case, we do accept empty fields as valid. So we return some fields with class is-valid and because the fields are empty, bootstrap still shows the red outline, not the green, like this:
The form code is :
<form action="" method="post"
class="was-validated" novalidate>
<div class="row mb-3">
<label for="id_content2" class="col-sm-2 col-form-label">Content2</label>
<div class="col-sm-10">
<input type="text" name="content2" class="form-control is-valid"
required id="id_content2" aria-describedby="content2" />
<div class="valid-feedback"> Valid </div>
</div>
</div>
</form>
What am I doing wrong? What is the solution to consider some empty fields as valid?
It seems like that the "is-valid" is being added when the form is returned to the user but the field is empty. That would explain the behaviour at least...
One solution to consider an empty field as valid is to remove the "required" attribute from the input field and check if the field is empty on the server side before processing the form.

Textbox missing when no data in model

I have an input that will display data in model. There supposed to be 2 ways to access this form. Either by entering ID no at the form before, so
the name displayed here, or without entering ID no, which means it is only a blank textbox. I successully retrieved the value from model where the
value is displayed inside the textbox. The problem is when the form is access without model. Textbox are not shown. It only displayed the label.
I am not sure why but i think it is maybe because the input value are assign to model. All i need is a textbox so user can input the data.
<div Class="form-group">
<Label Class="control-label col-md-4">Appointment Start</Label>
<div Class="col-sm-8">
#For Each Item In Model
#<input type="text" name="name" Class="form-control" value="#Html.DisplayFor(Function(modelItem) Item.name)">
Next
</div>
</div>

Adding an extra filter to Spree Commerce Orders

I'm currently trying to add an extra filter to filter dropdown on the Orders page by passing params.
I want to be able to filter down by postcode (zipcode).
I'm using a deface with a partial to add the following inside the filter form:
<div class="col-md-4">
<div class="form-group">
<label>
Post Code
</label>
<input class="form-control" type="text" name="q[ship_address_zipcode_start]" id="q_ship_address_zipcode_start">
</div>
</div>
I've tried multiple combinations and the params are being passed when I hit the filter search button but it's not drilling down the results with what I'm specifying in the input-box.
I'm not having much success, any help would be appreciated.

i want to get the form data form one gsp and want to access that data in another gsp and want to insert that data in table, how can i do this?

This is one gsp where i created one form
<div class="form-group">
<label for="msg_to">To</label>
<input type="email" id="msg_to" name="msg_to" class="form-control" style="width:500px;" required="">
</div>
<div class="form-group">
<label for="msg_subject">Subject</label>
<input type="text" id="msg_subject" class="form-control" style="width:500px;"name="msg_subject" required="" maxlength="50">
</div>
<div class="form-group">
<label for="msg_body">Body</label>
<textarea class="form-control" id="msg_body" style="width: 500px; height: 250px" name="msg_body" required="" ></textarea>
</div>
%{--<button type="submit" class="btn btn-default">Send</button>--}%
<g:actionSubmit value="Send" action="g_Inbox" style="color: #000000"></g:actionSubmit>
</form>
The data of above form must be access to another controller after submit and data should be display in the table form on that another view.
I fetch above data in following action:
def g_Inbox(){
def msg_to = params.msg_to;
def msg_subject = params.msg_subject;
def msg_body = params.msg_body;
println msg_to
println msg_subject
println msg_body
render(view: 'g_Inbox', model: [m_to : msg_to , m_sub : msg_subject , m_body: msg_body] )
}
from here i want to send it to view and want to add in new row of table..
You're in luck. What you want to do is very easy with Grails. But... you have to do your homework.
Things to read and digest
(best served with pizza and iced tea)
Groovy Server Pages -
https://grails.github.io/grails-doc/latest/guide/theWebLayer.html#gsp
Controllers - https://grails.github.io/grails-doc/latest/guide/theWebLayer.html#controllers
Once you understand GSP and controllers, you'll know exactly what to do.
Simple if You know how it works
render(view: 'g_Inbox', model:[params:params])
Access the params in gsp by
${params.msg_to}
and for Another Controller
redirect controller:'Controller' action: 'create',params:params

Successive form submission without losing data in Rails?

I am confused between the controller and views interaction. I have this initial form which validates the csv file uploaded. (POST form).
After this form is validated successfully, I give the user the option to confirm the details which, and this confirm button acts as another form.
The thing is I want to keep the details from the previous form values saved in the params hash. So basically I want to perform a merge with the second form.
Is this possible? If so, can you help me with the code for the second form cause currently it overrides the previous form. Both forms point to the same function in the controller.
<% unless #contents.blank? || #errors.present? %>
<form name="confirm_bulk_order" method="post" enctype="multipart/form-data" class="search line" action="/orders/create_bulk_order" id="confirm_bulk_order">
<div class="search-btn-align" id="confirmButton">
<input type="submit" name="confirm_bulk_order" value="Confirm Order" class="lmargin10 uiButton">
</div>
</form>
<% else %>
<form name="upload_bulk_order_csv" method="post" enctype="multipart/form-data" class="search line" action="/orders/create_bulk_order" id="upload_bulk_order_csv">
<div class="fileformField">
<span class="formlabel"> Upload CSV File: </span>
<input class="required" required="true" type="file" name="datafile"/>
</div>
<div class="search-btn-align" id="uploadButton">
<%= submit_tag 'Validate Bulk Order', :class => 'lmargin10 uiButton' %>
</div>
</form>
<% end %>
In controller orders
def create_bulk_corder
if #errors.blank? and params[:confirm_bulk_order]=="Confirm Order"
#Send the final REST order call
else
#contents = read_csv_file(params[:datafile]) if params[:datafile].present?
validate_order(#contents)
#Populate #errors etc, etc
....
....
end
render
end
What all changes must I make for this to be possible?
You're losing your params when the confirm button is pressed because they're not part of the confirm form. You could avoid this by adding a hidden field:
<form name="confirm_bulk_order" method="post" enctype="multipart/form-data" class="search line" action="/orders/create_bulk_order" id="confirm_bulk_order">
<!-- This assumes that #contents is a simple value... it's probably not, so you might need several hidden_field_tags here, one for each part of #contents that you want in your params -->
<%= hidden_field_tag "contents", #contents %>
<!-- You probably also want to show the user some info about their submission here -->
<div class="search-btn-align" id="confirmButton">
<input type="submit" name="confirm_bulk_order" value="Confirm Order" class="lmargin10 uiButton">
</div>
</form>
If you go with this approach, be sure to re-validate params[:datafile] after submission, since a malicious user could change this param value to get around your validation logic.
Also, I suggest factoring your action into two separate actions, and likewise for the views. You've got if-else blocks where you really ought to have separate files.
I suggest maybe using a gem that deals with multi paged forms or wizards: https://www.ruby-toolbox.com/categories/rails_wizards

Resources