Long time listener, first time caller. I've been practicing coding for ~1 year now and I'm currently trying to build a small CRUD app in Grails. For the past 3-4 weeks I have been unable to update records in the h2 database that Grails comes packaged with. I'm read many articles online, sifted through Stack Overflow, and even purchased the book 'Grails in Action' as a reference. Each resource makes me believe this should be relatively easy. I'm not sure if my issue is in my view (maybe how I'm posting the data), in my controller, or possibly in my application.yml. Below is my HTML, javascript, controller, domain, and application.yml code. Any help is GREATLY appreciated.
A few items to note:
1) When an update is executed, Grails validates with zero errors.
2) The update action prints the object's new attributes to the console, but the database isn't updated.
HTML - note, javascript function is used to change readOnly attributes from true to false
<form method="POST" action="../update">
<!--hidden field for contractor id-->
<input name="id" value="${params.id}" hidden>
<div class="row">
<div class="col-15">
<label for="name">Name:</label>
</div>
<div class="col-40">
<input class="readOnly" type="text" id="name" name="name" value="${params.name}" readonly>
</div>
<div class="editIcon">
<i id="editIcon" class="far fa-edit" title="Edit Contractor"></i>
</div>
</div>
<div class="row">
<div class="col-15">
<label for="street">Street:</label>
</div>
<div class="col-40">
<input class="readOnly" type="text" id="street" name="street" value="${params.street}" readonly>
</div>
</div>
<div class="row">
<div class="col-15">
<label for="city">City:</label>
</div>
<div class="col-40">
<input class="readOnly" type="text" id="city" name="city" value="${params.city}" readonly>
</div>
</div>
<div class="row">
<div class="col-15">
<label for="state">State:</label>
</div>
<div class="col-15 removeLeftPadding">
<select id="state" name="state" disabled>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
</select>
</div>
<div class="col-05 zip">
<label for="zip">Zip:</label>
</div>
<div class="col-13">
<input class="readOnly" type="text" id="zip" name="zip" value="${params.zip}" readonly>
</div>
</div>
<div class="row">
<div class="col-15">
<label for="paymentType">Payment Type:</label>
</div>
<div>
<select id="paymentType" name="paymentType" disabled>
<option value="1">ACH</option>
<option value="2">Credit Card</option>
</select>
</div>
</div>
<div class="row">
<input type="submit" value="Update">
</div>
</form>
JavaScript - I doubt the issue is here as the parameters are passed when viewing the network tab in Google dev tools (see screenshot below).
/*When icon edit icon is clicked, set readOnly/disabled values to false and input/icon colors to appropriate colors*/
$( "#editIcon" ).click(function() {
console.log("Edit called.")
if(!clicked){
$( "#name" ).prop('readonly', false)
$( "#street" ).prop('readonly', false)
$( "#city" ).prop('readonly', false)
$( "#state" ).prop('disabled', false)
$( "#zip" ).prop('readonly', false)
$( "#paymentType" ).prop('disabled', false)
//Set input color to black
$( "#name" ).css("color", "black")
$( "#street" ).css("color", "black")
$( "#city" ).css("color", "black")
$( "#state" ).css("color", "black")
$( "#zip" ).css("color", "black")
$( "#paymentType" ).css("color", "black")
//Set icon to red
$("#editIcon").css("color", "red")
clicked = true;
} else{
$( "#name" ).prop('readonly', true)
$( "#street" ).prop('readonly', true)
$( "#city" ).prop('readonly', true)
$( "#state" ).prop('disabled', true)
$( "#zip" ).prop('readonly', true)
$( "#paymentType" ).prop('disabled', true)
//Set input color to black
$( "#name" ).css("color", "gray")
$( "#street" ).css("color", "gray")
$( "#city" ).css("color", "gray")
$( "#state" ).css("color", "gray")
$( "#zip" ).css("color", "gray")
$( "#paymentType" ).css("color", "gray")
//Set icon to red
$("#editIcon").css("color", "#7543b7")
clicked = false;
}
});
Update Action in Contractor Controller
def update(Long id){
println("I made it to update for Contractors")
def contractorInstance = Contractor.get(id)
contractorInstance.properties = params
println contractorInstance.validate()
println contractorInstance.errors
if(!contractorInstance.save()){
println 'Contractor did not update.'
} else{
println 'Contractor updated.'
}
println contractorInstance.id //prints id to console
println contractorInstance.name //prints updated name to console
redirect(action: "index")
}
Development Data Source in Application.yml
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
logsql: true #added by PCB on 5/14
Dev Tools Screenshot - Form Data Post
Contractor Domain
class Contractor {
String name
String street
String city
String state
String zip
PaymentType paymentType
static hasMany = [masteragreements: MasterAgreement, contracts: Contract, termsAndConditions: TermsAndConditions]
static constraints = {
}
}
EDIT: I'm using Grails Version 4.0.3
A Transaction is required to write to the database in Grails 4/Hibernate 5.2+. In your case you are writing to a session but never instructing that session to be flushed(written) to the database. That happens at the end of a transaction or when flush is explicitly called.
The most common solution would be to move the logic to a Service and annotate the method with #Transactional or using GORM Data Services as they manage transactions for you.
One of the ways you can force the flush (and view your errors) is to do it something like this:
if(!instance.save(flush: true)){
instance.errors.allErrors.each { println it }
}
Related
I want to add a project name and task name while clicking on a particular date in fullcalendar but I don't know how to use bootbox.prompt or bootbox.dialog with more than one fields so can you help me out?
select: function (start, end, allDay) {
debugger;
bootbox.prompt("Add New Event", function (title) {
debugger;
if (title !== null) {
calendar.fullCalendar('renderEvent',
{
title: title,
start: start,
end: end,
allDay: allDay,
className: 'label-info'
},
true // make the event "stick"
);
}
});
It's quite simple, we can use bootbox dialog for that
bootbox.dialog({
title: 'Add New Event',
message: $('#form'),
show: false,
}).on("shown.bs.modal", function (e) {
$('#form').show()
}).on('hide.bs.modal', function (e) {
/**
* Bootbox will remove the modal (including the body which contains the login form)
* after hiding the modal
* Therefor, we need to backup the form
*/
$('#form').hide().appendTo('body');
})
.modal('show');
calendar.fullCalendar('unselect');
}
In html
<form id="form" method="post" class="form-horizontal" style="display: none;">
<div class="form-group">
<label class="col-xs-3 control-label">Username</label>
<div class="col-xs-5">
<input type="text" class="form-control" name="username" />
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Password</label>
<div class="col-xs-5">
<input type="password" class="form-control" name="password" />
</div>
</div>
<div class="form-group">
<div class="col-xs-5 col-xs-offset-3">
<button type="submit" class="btn btn-primary" style="float:right;">Login</button>
</div>
</div>
i have a search form whith jquery autocomplete on some fields.
<form id="prjs_form" class="prjcts" action="<wp:action path="/ExtStr2/do/enpim/projects/list.action" />" method="post">
<div class="row bord">
<div class="wid_ut">
<span class="label"><wp:i18n key="ACRONYM" /></span>
<span class="field"><input id="autAcronym" type="text" name="bean.acronym" value="${bean.acronym}" class="lrg autocomplete" /></span>
</div>
......................
<div class="row">
<div class="act_btns">
<input type="submit" name="search" value="<wp:i18n key='SEARCH'/>">
</div>
</div>
</form>
My need is to submit the form when I select one of the values suggested by autocomplete.
this is my jquery
jQuery('#autAcronym').autocomplete({
params: { type: 'project_acronym' },
paramName: 'text',
serviceUrl: '<wp:info key="systemParam" paramName="applicationBaseURL" />json/hall.ShowParameters',
minChars: 2,
select: function(event, ui) {
if(ui.item){
$(event.target).val(ui.item.value);
}
$('#prjs_form').submit();
return false;
}
});
when i start typing on field the autocomplete work fine, but when i select one of the suggested value the select event doesn't trigger....
what's wrong?
the other similar post on stackoverflow doesn't solved my problem
thanks in advance
example is very simple.
select the two search condition and return a table with pagination, the whole page will not refresh.
so i use the grails formRemote to submit the form, and the control return the gender with template and it work well. However, the pagination i want to use Jquery, but i cant pass the formRemote params to the remoteFunction using onSuccess method in formRemote.
Here it is the code:
<div class="formSep col-md-12">
<g:formRemote update="searchResult" class="form-inline" role="form" name="form"
url="[controller: 'autoRateRecord', action: 'search']" onSuccess="initPagination(data)">
<div class="form-group col-lg-2">
<g:select class="form-control" name="notified" from="${['done', 'undone']}"
noSelection="${['null': 'Oops']}">
</g:select>
</div>
<div class="form-group col-lg-2 pull-right">
<button type="submit" class="btn btn-primary btn-lg">
<span class="glyphicon glyphicon-search"></span> search
</button>
</div>
</g:formRemote>
</div>
<div id="searchResult">
<g:render template="searchList"/>
</div>
<script type='text/javascript'>
function initPagination(data) {
console.log("------> " + data)
$("#Pagination").pagination(10, {
callback: getRecordList(1),
prev_text: "prev",
next_text: "next",
items_per_page: 15,
num_edge_entries: 1
});
}
**!!!!!!! need formRemote data !!!!!!!**
function getRecordList(page_index) {
<g:remoteFunction controller="autoRateRecord" action="search" update="searchResult" params="'page='+page_index"/>
}
// On load, style typical form elements
$(function () {
});
</script>
the controller code is:
def search = {
log.info(ToStringBuilder.reflectionToString(params))
// logic .....
render(template: "searchList", model: [
autoRateRecords: result,
total : result.totalCount
])
}
I would change the pagination script to something like
$("#pageHiddenFieldId").val(pageNo);
$("#myForm").submit();
I'm using the bootstrap datetimepicker library like so:
#using (Html.BeginForm("Search", "Home", FormMethod.Post))
{
<div id="searchContainer">
<div class="well">
<div id="datetimepicker1" class="input-append date">
<input data-format="dd/MM/yyyy hh:mm:ss" type="text" id="StartDate" />
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
</div>
<div class="well">
<div id="datetimepicker2" class="input-append date">
<input data-format="dd/MM/yyyy hh:mm:ss" type="text" id="EndDate" />
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
</div>
#Html.Bootstrap().SubmitButton().HtmlAttributes(new { #class = "btn" })
</div>
}
This is another try:
#using (Html.BeginForm("Search", "Home", FormMethod.Post))
{
<div id="searchContainer">
#Html.Bootstrap().TextBoxFor(model => model.StartDate).HtmlAttributes(new { id = "datetimepicker1", #class = "input-append date" }).AppendIcon("icon-calendar")
#Html.Bootstrap().TextBoxFor(model => model.EndDate).HtmlAttributes(new { id = "datetimepicker2", #class = "input-append date" }).AppendIcon("icon-calendar")
#Html.Bootstrap().SubmitButton().HtmlAttributes(new { #class = "btn" })
</div>
}
JQuery:
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
language: 'en'
});
});
$(function () {
$('#datetimepicker2').datetimepicker({
language: 'en'
});
});
</script>
The first chunk of code where the divs are used, work fine when selecting a date and time. But the second one with the razor syntax doesn't.
When using the first chunk for selecting a date and time, it's not posted to the server when button is pressed.
The model only contains the 2 dates:
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
Does anyone see what I might be doing wrong here?
EDIT
With "it doesn't work" when using the Razor syntax, the datetimepicker just didn't show up, nothing happened. Now I have been testing some further, i get a runtime error pointing to this line in the datetimepicker.min.js file:
offset.top=offset.top+this.height;
And this is the error message it pops up in a dialog:
Unhandled exception at line 26, column 5290 in
http://tarruda.github.io/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.min.js
0x800a138f - JavaScript runtime error: Unable to get property 'top' of
undefined or null reference
This is the rendered html of the form:
<form action="/Home/Search" method="post"> <div id="searchContainer">
<select class="chzn-select" data-placeholder="Select items" id="Ids" multiple="multiple" name="Ids"><option value="1">test0</option>
<option value="2">test1</option>
<option value="3">test2</option>
<option value="4">test3</option>
<option value="5">test4</option>
<option value="6">test5</option>
</select>
<div class="input-append"><input class="input-append date" data-val="true" data-val-date="The field Start Date must be a date." data-val-required="The Start Date field is required." id="datetimepicker1" name="StartDate" type="text" value="1-1-0001 0:00:00" /><span class="add-on"><i class="icon-calendar"></i></span></div>
<div class="input-append"><input class="input-append date" data-val="true" data-val-date="The field End Date must be a date." data-val-required="The End Date field is required." id="datetimepicker2" name="EndDate" type="text" value="1-1-0001 0:00:00" /><span class="add-on"><i class="icon-calendar"></i></span></div>
<button class="btn btn" type="submit">Submit</button>
</div>
</form>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
language: 'en'
});
});
$(function () {
$('#datetimepicker2').datetimepicker({
language: 'en'
});
});
</script>
<style type="text/css">
#searchContainer {
margin-top:10%;
}
</style>
</div>
<div id="push"></div>
</div>
<div id="footer">
<div class="container">
<p>© 2013 - My ASP.NET MVC Application</p>
</div>
</div>
<script type="text/javascript" src="http://tarruda.github.com/bootstrap-datetimepicker/assets/js/bootstrap-datetimepicker.min.js"></script>
Your first approach has the problem, that you omitted the name attributes for the input fields. The name attribute defines what variable is posted by the form.
Just write :
<input ... type="text" id="StartDate" name="StartDate />
For your second try:
But the second one with the razor syntax doesn't.
What do you mean with doesn't work? Can you show the rendered HTML?
For the second try:
If you look at the documentation for bootstrap datetime-picker, it shows that the id attribute #datetimepicker1 should be applied to the container of the input. Instead you are applying it to the input itself.
Also it seems that attributes data-time-icon="icon-time" data-date-icon="icon-calendar" need to be applied to the <i> tag, which TwitterBootstrapMVC does not do out of the box.
Either submit an issue to the TwitterBootstrapMVC to add an overload to .IconAppend method to also take Icon class that you'd be able to customize however you want... or better yet, fork it make the necessary change and submit a pull request.
I changed my model to accept a string instead of a DateTime object, and that fixed it.
I have a form that I have 2 different sets of formfields that are utilized depending on a select box value. The problem I am having is when I try to disable the irrelevant input fields, I the disabled attribute comes up as: disabled="" instead of disabled="disabled" here is the code I am using. It is a fairly complicated form so I will use the relevant fields so I can try to keep it as simple as possible for you all. If you think something is missing... please let me know if you need to see more.
<cfform id="entry-form" ACTION="index-10.cfm?Company" name="send" class="uniForm">
<div class="ctrlHolder"><label for="" style="display:none"><em>*</em>Builder or Individual</label>
<cfselect name="select1" id="select1">
<option value="" <cfif Individual is "">selected="selected"</cfif>>Who is this Case for? (choose one)</option>
<option value="0"<cfif Individual is 1>selected="selected"</cfif>>An Individual Home Owner</option>
<option value="1"<cfif Individual is not 1 and Individual is not "">selected="selected"</cfif>>A Builder</option>
</cfselect>
<p class="formHint">A selection is required</p>
</div>
<!--- this is for individual home owner. --->
<div class="hide" id="hide1">
<div class="ctrlHolder"><label for="" style="display:none"><em>*</em>First name</label>
<cfinput type="text"
name="FirstName"
id="FirstName"
data-default-value="Enter your first name"
size="35"
class="textInput required validateAlpha"
maxlength="50"
value="#FirstName#">
<p class="formHint">First Name is required</p>
</div>
</div>
<div class="hide" id="hide2">
<div class="ctrlHolder"><label for="" style="display:none"><em>*</em>Builder Name</label>
<cfinput type="text" id="builder"
name="BuilderName"
data-default-value="Type a builder's name"
size="35"
class="textInput required"
value="" />
<p class="formHint">Builder's name is required</p>
<cfinput id="builder_hidden" name="BuilderID" type="hidden" value="" />
<cfinput id="builder_hidden_plan" name="PlanID" type="hidden" value="" />
</div>
</div>
</cfform>
<script>
$(document).ready(function(){
$("#select1").change(function(){
if ($(this).val() == "1" ) {
$("#hide2").slideDown("fast"); //Slide Down Effect
$("#hide1").slideUp("fast");
$("#FirstName").prop("disabled", true);
$("#builder").prop("disabled", false);
} else if ($(this).val() == "0" ){
$("#hide1").slideDown("fast"); //Slide Down Effect
$("#hide2").slideUp("fast");
$("#FirstName").prop("disabled", false);
$("#builder").prop("disabled", true);
}
});
</script>
I am using:
jquery-1.9.1.js
jquery-ui-1.10.1.custom.js
uni-form-validation.jquery.js
I found the issue. The disabled property was being added. It was the required class that was keeping this from working. I added removeClass and addClass methods in order to correct this.
Please change the jQuery 'prop' to 'attr' & check the below script once it works fine.....
<script type="text/javascript">
$(document).ready(function(){
$("#select1").change(function(){
if ($(this).val() == "1" ){
$("#hide2").slideDown("fast"); //Slide Down Effect
$("#hide1").slideUp("fast");
$("#firstname").attr("disabled", "disabled");
$("#builder").attr("disabled", false);
}
else if ($(this).val() == "0" ){
$("#hide1").slideDown("fast"); //Slide Down Effect
$("#hide2").slideUp("fast");
$("#firstname").attr("disabled", false);
$("#builder").attr("disabled", "disabled");
}
});
});