I have this controller which performs me a multiple domain databinding class, and it is working as i want. But i would like some help about the error messages. Is it possible to display error messages from multiple domain classes? If so, how should be the code both is the view and in the controller?
class CarroMovel {
String move
String rodas
String espelhos
Carro carro
static hasMany = [carros: Carro]
static belongsTo = Carro
static constraints = {
move(nullable:false, blank:false)
}
static mapping = {
version false
}
}
class Carro {
String name
String marca
String matricula
static constraints = {
name(nullable:false, blank:false)
}
static mapping = {
version false
}
}
def save3 = {
def carroInstance = new Carro( )
def carroMovelInstance = new CarroMovel( )
carroInstance.name = params.carro.name
carroInstance.marca = params.carro.marca
carroInstance.matricula = params.carro.matricula
carroMovelInstance.move = params.carroMovel.move
carroMovelInstance.rodas = params.carroMovel.rodas
carroMovelInstance.espelhos = params.carroMovel.espelhos
carroInstance.save(failOnError: true)
carroMovelInstance.carro = carroInstance
carroMovelInstance.save(failOnError: true)
}
<g:form controller="carro" action="save3">
<h1>Add New Carro Record</h1>
<p>Basic Information</p>
<label>Name
<span class="small">Add your name</span>
</label>
<input type="text" name="carro.name" value="${carroInstance?.name}" /><br>
<label>Marca
<span class="small">Add your name</span>
</label>
<input type="text" name="carro.marca" value="${carroInstance?.marca}" /><br
<label>Matricula
<span class="small">Add your name</span>
</label>
<input type="text" name="carro.matricula" value="${carroInstance?.matricula}" /><br>
<label>Move
<span class="small">Add your name</span>
</label>
<input type="text" name="carroMovel.move" value="${carroMovelInstance?.move}" /><br>
<label>Rodas
<span class="small">Add your name</span>
</label>
<input type="text" name="carroMovel.rodas" value="${carroMovelInstance?.rodas}" /><br>
<label>Espelho
<span class="small">Add your name</span>
</label>
<input type="text" name="carroMovel.espelhos" value="${carroMovelInstance?.espelho}" /><br>
<g:submitButton name="save" value="Save" id="addConference"/>
<div class="spacer"></div>
</g:form>
<g:hasErrors bean="${carroInstance}">
<div class="errors">
<g:renderErrors bean="${carroInstance}" as="list" />
</div>
</g:hasErrors>
Grails Validation Errors Primer
The errors for a domain object are stored in an errors property that is added to the object after it is validated. This property is an implementation of the Spring Errors interface.
You can get the errors either by calling the methods of this interface directly, e.g. to display the errors for the move field of a Carro instance:
List<FieldError> moveErrors = carroMovelInstance.errors?.getFieldErrors('move')
To get the error messages for each error, you'll need a referrence to the messageSource bean created by Grails. You could get the message for each of the above errors with:
List<String> errorMessages = moveErrors.collect {error ->
messageSource.getMessage(error, Locale.default)
}
Alternatively, Grails provides the eachError and renderErrors tags that simplify displaying errors and their corresponding messages in a GSP.
Specific Problems With Your Code
In your controller code, an exception will be thrown whenever save fails due to validation errors, so the view has no opportunity to display the errors. To fix this, change the controller so that it returns the domain objects (along with their errors) when saving fails
def save3 = {
def carroInstance = new Carro( )
def carroMovelInstance = new CarroMovel( )
carroInstance.name = params.carro.name
carroInstance.marca = params.carro.marca
carroInstance.matricula = params.carro.matricula
carroMovelInstance.move = params.carroMovel.move
carroMovelInstance.rodas = params.carroMovel.rodas
carroMovelInstance.espelhos = params.carroMovel.espelhos
// I'm assuming in the code below that the view that displays the form is 'create.gsp'
if (!carroInstance.save()) {
render view: 'create', model : [carro: carroInstance, carroMovel: carroMovelInstance]
return
}
carroMovelInstance.carro = carroInstance
if (!carroMovelInstance.save()) {
render view: 'create', model : [carro: carroInstance, carroMovel: carroMovelInstance]
}
}
The GSP also needs to be changed to display these errors using either the Errors API directly or one of the Grails tags (see above)
Related
I am coding a solution where the user will submit a form, posting the values back to my ASP.NET MVC controller. My model is complex and the form fields are contained in a nested object (I'm using CQRS via MediatR). When I submit the form, the values come across as null. How can I get the complex model to recognize the form fields?
Here is my code:
Controller:
[HttpPost]
[Route("edit")]
public async Task<IActionResult> Edit(UpdateApplicationCommand command)
{
await _mediator.Send(command)
.ConfigureAwait(false);
return RedirectToAction("Index");
}
Models:
public class UpdateApplicationCommand : IRequest<Unit>
{
public ApplicationEditGeneralViewModel ApplicationEditGeneralViewModel { get; set; } = null!;
}
public class ApplicationEditGeneralViewModel
{
[Required]
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
View:
#model ApplicationEditGeneralViewModel
<form method="post" asp-action="Edit" asp-controller="Applications">
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.Name, new { #class = "form-control", placeholder = "Application Name"})
<label for="Name">Application Name</label>
</div>
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.Description, new { #class = "form-control", placeholder = "Application Description"})
<label for="Description">Application Description</label>
</div>
<div class="d-flex flex-row-reverse bd-highlight">
<input type="submit" value="Submit" class="btn btn-primary mt-2" />
</div>
</form>
I've tried to reduce the complex model to its fields, by placing the contents of the ApplicationEditGeneralViewModel directly into the UpdateApplicationCommand class. This worked, but I'd really like to keep the nested structure so that I can reuse the ApplicationEditGeneralViewModel object.
I saw this solution here:
How to bind nested model in partial view
But I'd rather avoid adding the name as a route object (if possible) for every form field. Is there another, more simple way that I can do this?
The first way, you can custom model binding like below:
public class CustomModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var model = new UpdateApplicationCommand()
{
ApplicationEditGeneralViewModel = new ApplicationEditGeneralViewModel()
{
Description = bindingContext.ValueProvider.GetValue("Description").ToString(),
Name = bindingContext.ValueProvider.GetValue("Name").ToString()
}
};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Apply the custom model binding like below:
[HttpPost]
public async Task<IActionResult> Edit([ModelBinder(typeof(CustomModelBinder))]UpdateApplicationCommand model)
{
//.....
}
The second way, just change your razor view like below:
#model UpdateApplicationCommand
<form method="post">
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.ApplicationEditGeneralViewModel.Name, new { #class = "form-control", placeholder = "Application Name"})
<label for="Name">Application Name</label>
</div>
<div class="form-floating mb-3">
#Html.TextBoxFor(m => m.ApplicationEditGeneralViewModel.Description, new { #class = "form-control", placeholder = "Application Description"})
<label for="Description">Application Description</label>
</div>
<div class="d-flex flex-row-reverse bd-highlight">
<input type="submit" value="Submit" class="btn btn-primary mt-2" />
</div>
</form>
I've gone through dozens of articles, docs, and stack overflow questions (even the one with a similar intro)regarding the same issues but it still persists.
I've tried this with putting the functions in the .cshtml.cs page and on the .cshtml page, named and unnamed handler names, different framework for sending emails, and adding an empty action field in the form along with other fixes but the issue seems to be that the handler method itself is not firing while the form is submitting. Any and all help is appreciated and please let me know if more information is needed.
My HTML form:
<form method="POST" asp-page-handler="email">
<!-- Name input-->
<div class="form-floating mb-3">
<input class="form-control" name="clientName" type="text" placeholder="Enter your name..." required/>
<label for="name">Full name*</label>
</div>
<!-- Email address input-->
<div class="form-floating mb-3">
<input class="form-control" name="clientEmail" type="email" placeholder="name#example.com" required/>
<label for="email">Email address*</label>
</div>
<!-- Phone number input-->
<div class="form-floating mb-3">
<input class="form-control" name="clientPhone" type="tel" placeholder="(123) 456-7890"/>
<label for="phone">Phone number</label>
</div>
<!-- Message input-->
<div class="form-floating mb-3">
<textarea class="form-control" name="clientMessage" type="text" placeholder="Enter your message here..." style="height: 10rem" required></textarea>
<label for="message">Message*</label>
</div>
<!-- Submit Button-->
<div class="d-grid"><button class="btn btn-primary btn-xl" type="submit" value="submit">Submit</button></div>
</form>
My functions as they are currently:
public void OnPostEmail()
{
var clientEmail = Request.Form["clientEmail"];
var clientName = Request.Form["clientName"];
var clientPhone = Request.Form["clientPhone"];
var clientMessage = Request.Form["clientMessage"];
sendEmail(clientEmail, clientName, clientPhone, clientMessage);
}
public void sendEmail(string clientEmail, string clientName, string clientPhone, string clientMessage)
{
var errorMessage = "";
try
{
// Initialize WebMail helper
WebMail.SmtpServer = "smtp.google.com";
WebMail.SmtpPort = 587;
WebMail.UserName = "***#gmail.com";
WebMail.Password = "MYPASSWORD";
WebMail.From = "***#gmail.com";
WebMail.EnableSsl = true;
// Send email
WebMail.Send(to: clientEmail,
subject: $"Request from: + {clientName}",
body: $"{clientMessage}\nPhone: {clientPhone}\nEmail: {clientEmail}"
);
}
catch (Exception ex)
{
errorMessage = ex.Message;
}
}
I'm quite new to Angular, and I've already searched the web, without finding a correct solution for my situation.
I have a dynamic form created by a *ngFor. I need to disabled the submit button if the inputs are all empty and show the alert div; but I need to enable the submit if at least one of those forms contains something different from ''.
Here is my html code
<form class="form-inline" #form="ngForm">
<div class="form-group" *ngFor="let meta of state.metaById; let i = index" style="margin: 5px">
<label>{{meta.nome}}</label>
<input type="text" class="form-control" #nome (blur)="inputInArray(nome.value, i);">
</div>
<button type="button" class="btn btn-primary" (click)="getCustomUnitaDocumentaliRow(this.param)" [disabled]="fieldNotCompiled">invia</button>
</form>
<div class="alert-notification" [hidden]="!fieldNotCompiled">
<div class="alert alert-danger">
<strong>Va compilato almeno un campo.</strong>
</div>
</div>
and here is my Typescript code
inputInArray(nome: string, indice) {
if (this.state.controlloMetaId = true) {
this.state.metadatoForm[indice] = nome;
}
// this.fieldNotCompiled = false;
for (const i in this.state.metaById) {
console.log(this.state.metadatoForm);
if (isUndefined(this.state.metadatoForm[i]) || this.state.metadatoForm[i] === '') {
this.fieldNotCompiled = true && this.fieldNotCompiled;
} else {
this.fieldNotCompiled = false && this.fieldNotCompiled;
}
console.log(this.fieldNotCompiled);
}
With this code I can check the first time a user type something in one input, but it fails if it empty one of them (or all of them)
Thanks for your time
UPDATE
Check if any input got a change that is different from empty or space, just by doing:
<input ... #nome (input)="fieldNotCompiled = !nome.value.trim()" ....>
DEMO
You can set a listener to the form changes:
#ViewChild('form') myForm: NgForm;
....
ngOnInit() {
this.myForm.valueChanges.subscribe((value: any) => {
console.log("One of the inputs has changed");
});
}
I have following model:
class HomePageModel
{
public User user { get; set; }
public ResultHistory resultHistory { get; set; }
public Option option { get; set; }
public HomePageModel()
{
}
}
Code from controller when passing it into view:
[FacebookAuthorize]
public async Task<ActionResult> Index(Context context)
{
ViewBag.AppUrl = GlobalFacebookConfiguration.Configuration.AppUrl;
if (ModelState.IsValid)
{
var user = await context.Client.GetCurrentUserAsync<MyAppUser>();
var option = new Option();
//CODE for reading user from db, simpified
Users dbUser = db.Get(context);
var resultHistory = new ResultHistory(dbUser.NSPGW, dbUser.NSPGL,
dbUser.NMPGW,dbUser.NMPGL,dbUser.NDPGW, dbUser.NDPGL);
HomepageModel homepageModel = new HomepageModel();
homepageModel.option = option;
homepageModel.resultHistory = resultHistory;
homepageModel.user = user;
return View(homepageModel);
}
}
Code from controller
#using (Html.BeginForm("Start", "Home", FormMethod.Post, new { id="opcijeForm"}))
{
<div class="row">
<div class="col-md-4">
<div class="radio">
<label>
<input type="radio" name="radio" value="sp" />SP
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="radio" value="mp" />MP
</label>
</div>
<div>
<input type="submit" value="start" />
</div>
</div>
<div class="col-md-8">
<div id="spoption">
#Html.DropDownListFor(m => m.option.sl, Model.option.slSelectList)
<label style="padding-left:10px">
<input type="checkbox" id="bb" /> BB
</label>
#Html.DropDownListFor(m => m.option.swt, Model.option.swtSelectList, new {id="bbdd" })
<label style="padding-left:10px">
<input type="checkbox" id="ziv" /> ziv
</label>
#* #Html.DropDownListFor(m => m.gameOption.sln, Model.option.slnSelectList, new { id="zivdd"})*#
</div>
<div id="mpoption">
<label>Numer of oppointment</label>
#Html.DropDownListFor(m=>m.option.spn, Model.option.spnSelectList)
#Html.DropDownListFor(m=>m.option.smv, Model.option.smvSelectList)
</div>
</div>
</div>
}
and receiving model on post
[FacebookAuthorize]
[HttpPost]
public ActionResult Start(HomepageModel model)
{
//Some code
//here is for example model.option null
}
When I submit form with above model, in my controller action where I receive same model, all it's properties are null.Each class that is within HomePage has only string and int as properties. If I move something, for example from class Option and put it in HomePageModel direct, then properties I have moved are not null. Is there a way to post model as above, or should I just copy properties from these three class into HomePageModel class?
[EDIT]
It is because of meta-data FacebookAuthorize, when I remove it everything is working
MVC app facebook passing object from view to controller at submit
I have problem with the TYPO3 extension "Formhandler". I installed the extension, added captcha and everything is working.
This is the HTML template:
<!-- ###TEMPLATE_FORM1### begin -->
<form action="###REL_URL###" name="projektform" id="projektform" method="post" class="formhandler">
<br />
<div id="sender_name">
<label for="sender_name"><span style="color:red;">*</span>Name:</label>
<br />
<input type="text" name="formhandler[sender_name]" id="sender_name"
value="###value_sender_name###" />
###error_sender_name###
</div>
<br />
<div id="sender_email">
<label for="sender_email"><span style="color:red;">*</span>Email:</label>
<br />
<input type="text" name="formhandler[sender_email]" id="sender_email"
value="###value_sender_email###" />
###error_sender_email###
</div>
<br />
<div id="sender_message">
<label for="message"><span style="color:red;">*</span>Message:</label>
<br />
<textarea name="formhandler[message]" id="message">###value_message###</textarea>
###error_message###
</div>
<br />
<!--###CAPTCHA_INSERT### this subpart is removed if CAPTCHA is not enabled! -->
<div id="captcha">
<label for="freecapfield"><span style="color:red;">*</span>###SR_FREECAP_NOTICE###</label>
<br />
###SR_FREECAP_CANT_READ###
<br />
<div class="cap-img">
###SR_FREECAP_IMAGE###
</div>
<br />
<input type="text" id="freecapfield" name="formhandler[freecapfield]" title="###SR_FREECAP_NOTICE###" value="">
<br />
###error_freecapfield###
</div>
<!--###CAPTCHA_INSERT###-->
<br />
<input type="submit" value="Submit" ###submit_nextStep### />
</form>
<!-- ###TEMPLATE_FORM1### end -->
<!-- ###TEMPLATE_SUBMITTEDOK### begin -->
<p>The following message has been sent:</p>
<p>###value_message###</p>
<!-- ###TEMPLATE_SUBMITTEDOK### end -->
<!-- ###TEMPLATE_EMAIL_ADMIN_PLAIN### begin -->
The following contact form has been sent to you:
Sender: ###value_sender_name### ###value_sender_email###
Text:
###value_message###
<!-- ###TEMPLATE_EMAIL_ADMIN_PLAIN### end -->
This is the typo script:
<INCLUDE_TYPOSCRIPT: source="FILE:fileadmin/contactform/1-contactform.ts">
plugin.Tx_Formhandler.settings {
debug = 1
templateFile = fileadmin/contactform/1-contactform.html
formValuesPrefix = formhandler
finishers {
1 {
class = Tx_Formhandler_Finisher_Mail
}
2 {
class = Tx_Formhandler_Finisher_SubmittedOK
config.returns = 1
}
}
# Rules for the validation
validators.1.class = Validator_Default
validators.1.disabled = 0
validators.1.config.fieldConf {
message.errorCheck.1 = required
message.errorCheck.2 = minLength
message.errorCheck.2.value = 5
sender_name.errorCheck.1 = required
sender_email.errorCheck.1 = required
sender_email.errorCheck.2 = email
freecapfield.errorCheck.1 = srFreecap
}
# Layout if the error message
singleErrorTemplate {
totalWrap = |
singleWrap = <span style="color: red;">|</span>
}
}
So what I have is Name, Email, Message and a captcha fields, working perfectly.
But then I wanted to add a "subject" field in the form, so that when someone sends an email from the online contact form, he would be able to set a subject of that email.
I added an additional input field:
<div id="subject">
<label for="subject">Subject:</label>
<br />
<input type="text" name="formhandler[subject]" id="subject" value="###value_subject###"/>
</div>
After adding the input in the HTML template, I entered the value "SUBJECT". The result was that I was able to see the value in the formhandler debugger:
The current GET/POST params are:
sender_name NAME
sender_email EMAIL#MAIL.COM
subject SUBJECT
message MESSAGE
freecapfield kdlxp
step-2-next 1
submitted 1
randomID 5fab4cc19017c5c48dafb6a05ed7687b
removeFile
removeFileField
submitField
Then all I needed to do was to "assign" that value to the "admin subject" field. I did a lot of researching and I was able to find the following code:
plugin.Tx_Formhandler.settings.predef.myformname {
finishers {
1.class = Tx_Formhandler_Finisher_Mail
1.config {
limitMailsToUser = 5
admin {
subject = TEXT
subject.data = GPvar:formhandler|title
}
}
}
}
So I put the code in my typo script, substituting "myformname" with the name of my form "projektform" and title with the name of my input field "subject", but when I send an email, there is no subject.
I did a lot of searching, tried a lot of examples, but the result was the same. Could you please point me to the right direction?
The use of GPvar is deprecated, use GP instead:
subject.data = GP:formhandler|subject
More information can be found in this howto: How to access Formhandler values in TypoScript
The Link has slightly chanced to http://www.typo3-formhandler.com/en/blog/howtos/how-to-access-formhandler-values/
Or you could use the mechanism that is meant to do that for you:
In your template you specified the fieldname by name="formhandler[subject]".
This means your value will be stored under the key "subject".
Most, if not all, finishers are able to deal with this key/value pairs just like this:
plugin.Tx_Formhandler.settings {
finishers {
1 {
class = Tx_Formhandler_Finisher_Mail
config.admin.subject = subject
}
}
}
Also another reason why your code might not work is that you did not specify your form to use a predef and configured most of it outside a predef. But the config for the subject that you found uses predef. Just changing the predef name is not enough do associate the config with your form. It has to be on the same level as your other config. This might just work as well:
plugin.Tx_Formhandler.settings {
finishers {
1.class = Tx_Formhandler_Finisher_Mail
1.config {
limitMailsToUser = 5
admin {
subject = TEXT
subject.data = GP:formhandler|subject
}
}
}
}
Sidenote:
Even though it is okay not to use predef (because you have no need for multiple different forms) it is not recommended, you should consider changing your config to using predefs.
Assuming that your form really is based on a predef form with the key "projektform":
plugin.Tx_Formhandler.settings.predef.projektform {
finishers {
1.class = Tx_Formhandler_Finisher_Mail
1.config {
limitMailsToUser = 5
admin {
subject = TEXT
subject.data = GP:formhandler|subject
subject.sanitize = 1
}
}
}
}
If you access GET/POST parameters using a cObject like "TEXT", you should always add "sanitize=1". Formhandler hooks into stdWrap and adds the submitted form data to the GET/POST array.
The better way to do it is the way #denvercoder suggested using just the name of the input field:
plugin.Tx_Formhandler.settings.predef.projektform {
finishers {
1.class = Tx_Formhandler_Finisher_Mail
1.config {
limitMailsToUser = 5
admin {
subject = subject
}
}
}
}