been having toruble with the button that has action. I have several btns which I want to know its paramaeter. In grails tutorial it says it should be like this:
<g:actionSubmit action="action" value="${message(code: 'default.button.edit.label', default: 'Edit')}" params="['actionTaken':editPhone]"/>
I tried using remotelink, submitButton, submitToRemote tags but none works. I always get null when I try parsing it in my controller:
def action=
{
def actionTaken = params.actionTaken
def employeeId= params.employeeId
MySession session = MySession.getMySession(request, params.employeeId)
profileInstance = session.profileInstance
switch(actionTaken)
{
case "editPhone" :
isEditPhone=true
break
case "editEmail" :
isEditEmail=true
break
}
render(view:"profile", model:[profileInstance:session.profileInstance, isEditPhone:isEditPhone, isEditEmail:isEditEmail])
}
What am I missing? is my params code wrong? Is my code in parsing params wrong? this just gets me in circles with no progress. help. thanks.
The Grails documentation doesn't list params as one of the attributes accepted by actionSubmit.
It is possible to inject the value you want in your params list in the controller by exploiting what that tag actually does:
def editPhone = { forward(action:'action', params:[actionTaken: 'editPhone'])}
def editEmail = { forward(action:'action', params:[actionTaken: 'editEmail'])}
You may also just want to just code completely separate logic into the editPhone and editEmail actions if that makes your code cleaner.
Updated View Code:
<g:actionSubmit action="editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" />
The params attribute is parsed as a map, where the keys are treated a strings but the values are treated as expressions. Therefore
params="['actionTaken':editPhone]"
is trying to define the key named actionTaken to have the value from the variable named editPhone in the GSP model. Since there is no such variable you're getting null. So the fix is to move the quotes:
params="[actionTaken:'editPhone']"
which will set the value to the string "editPhone".
You could also pass the parameter inside the POST-data using
<input type="hidden" name="actionTaken" value="editPhone" />
inside the form. Then it is also accessible through the params variable.
It works for me.
I just had a similar problem (I needed to submit a delete together with an id) and found a solution using HTML5's "formaction" attribute for submit-inputs.
They can be given a value that can include a controller, action, additional parameters, etc.
In General, to add a parameter to a submit button such as a edit of a specific sub-object on a form would looks like this:
<input type="submit" formaction="/<controller>/<action>/<id>?additionalParam1=...&additionalParam2=..." value="Action" >
and in your example:
<input type="submit" formaction="action?actionTaken=editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" >
In my situation, I had a single form element with multiple actions on each row of a table (i.e. data table edit buttons). It was important to send the entire form data so I couldn't just use links. The quickest way I found to inject a parameter was with javascript. Not ideal, but it works:
function injectHiddenField(name, value) {
var control = $("input[type=hidden][name='" + name + "']");
if (control.size() == 0) {
console.log(name + " not found; adding...");
control = $("<input type=\"hidden\" id=\"" + name + "\" name=\"" + name + "\">");
$("form").append(control);
}
control.val(value);
}
Your button can look like this:
<g:each in="${objects}" var="object">
...
<g:actionSubmit value="edit" action="anotherAction"
onclick="injectHiddenField('myfield', ${object.id})"/>
</g:each>
Related
In my controller:
def billingDetails() {
def traineeDetails = session.traineeDetais
println "session data::"+traineeDetails
[traineeNames:traineeDetails.name]
}
This prints: [numberOfTrainees:2, submit_trainee_details:Next: Billing Details �, phone:[999999, 99999], email:[tester1#test.com, tester2#test.com], name:[Jack, Rob], jobTitle:[SE, SE], action:processTraineeDetails, controller:trainingOrder]
now in my GSP i want to have a select tag which will have name (jack, Rob) as options
<g:select name="traineeName"
from="${traineeNames}"
value=""
/>
which is not working fine.. How to make this to work so that i will get the names as options in dropdown list
make use of the optionKey optionValue fields in your g:select
reference:
http://grails.org/doc/2.2.1/ref/Tags/select.html
and do something like
<g:select optionKey="value" optionValue="value"
name="traineeName" from="${traineeNames}" />
I don't know the exact problem but one possible reason is that your session.traineeDetais.name is not a list. Check weather session.traineeDetais.name is a list or not, like
println "Check: ${session.traineeDetais.name instanceof List}"
if it is list then your code should work and if it is string then your drop down contain string characters as values.
I am trying to do the following:
#foreach(var p in #Model.line_products){
<img class="small_img_#Model.line_products[i].short_name" src="/Content/images/#Model.line_products[i].image_name" />
}
Which isn't working, it renders the text just the way it is, not recognizing the '#'. I found this other post in Stackoverflow, which suggests adding parenthesis in the following way:
#foreach(var p in #Model.line_products){
<img class="small_img_(#Model.line_products[i].short_name)" src="/Content/images/#Model.line_products[i].image_name" />
}
Using this workaround, I get that my id is rendered as small_img_(MODEL ATTRIBUTE). Isn't there a workaround which doesn't require adding specific characters? (such as the parenthesis).
You have more errors than a simple undercore problem here. You cannot use #Model inside your if. You are already in a # block. Simply use #foreach(var p in Model.line_products).
Plus, the way you wrote the parenthesis, they will get rendered. What you want is
small_img_#(Model.line_products[i].short_name)
Put the parenthesis after the # instead of before:
class="small_img_#(Model.line_products[i].short_name)"
I sometimes put a couple of Guids in the id of an element and an underscore separator doesn't work.
There are two ways around this. First use the entity code _ instead and secondly just use a hyphen.
<input id="chk_#classLeader.ClassLeader_#ing.Ingredient.Guid" type="checkbox" class="chk_Done form-check">
<input id="chk-#classLeader.ClassLeader-#ing.Ingredient.Guid" type="checkbox" class="chk_Done form-check">
This is because I want to grab out the Guid's when the check box is clicked with some JQuery like this:
$(".chk_Done").click(function () {
var obj =[];
const itemId = ($(this).attr("id"));
const myArray = itemId.split("_");
var ClassLeaderGuid = myArray[1], IngredientGuid = myArray[2];
I'm making a test page for a project I'm working on and I've made desired progress so far but I'm trying to create TextBoxes from a model of List being passed to the view, however, the it seems to just ignore anything I have tried.
<form id="form1" runat="server">
<input id="btnsubmit" type="submit" name="Submit" onclick="Submit" />
<div id="divControls">
<% foreach (TextBox control in (this.Model as List<TextBox>))
{
Html.Label("lblLabel", control.Text);
Html.TextBox(control.ID, control.Text, new { id = control.ID, style = "width:50", name = "txt" + control.ID });
} %>
</div>
</form>
The List isn't null in the Controller on return. I don't have a clue at what the problem could be. If I throw a Something in the for loop it executes the appropriate number of times so why isn't it creating the labels or textboxes?
At first I thought it was that I'm adding them inside a form but I removed the form tags and it still didn't work so I really have no Idea, any help would be much appreciated. I'm relatively new to MVC.
[HttpPost]
public ActionResult Index(FormCollection form)
{
List<TextBox> controls = new List<TextBox>();
foreach (String Key in form.Keys)
{
if (Key.Contains("txt"))
{
TextBox textBox = new TextBox();
textBox.ID = Key;
textBox.Text = form.GetValues(Key)[0];
controls.Add(textBox);
}
}
return View("Index", controls);
}
Here's my Action encase it's helps.
Also encase I wasn't clear enough, I am adding controls to a form at runtime using JQuery and then that Action will be part of the submit so it must send the textboxes back to the view so they are not deleted.
Like I said I'm new to the whole MVC and Asynchronous thing so If there's a better way to do this, advice would be much appreciated.
Your not printing the html
<% foreach (TextBox control in (this.Model as List<TextBox>))
{%>
<%=Html.Label("lblLabel", control.Text)%>
<%=Html.TextBox(control.ID, control.Text, new { id = control.ID, style = "width:50", name = "txt" + control.ID })%>
<% } %>
Your code is looping through the controls and the Html.whaterever is returning a string but your not doing anything with it, just discarding it.
you also don't need to return a whole TextBox object. This is probably inefficient. Just return an struct or a class containing your data
Html.Label returns a string containing a <label> tag.
You're discarding that string.
You need to write it to the page by writing <%= Html.Whatever() %>.
I have two domain classes
class Contract {
String number
static hasMany = [statements:Statement]
}
class Statement {
String code
static hasMany = [contracts:Contract]
}
I would like to show all statements available in my gsp with a checkbox next to each, allowing the user to choose which statements are applicable to the contract. So something like:
[ ] Statement Code 1
[ ] Statement Code 2
[ ] Statement Code 3
I started off with this:
<g:each in="${Statement.list()}" var="statement" status="i">
<g:checkBox name="statements[${i}].id" value="${statement.id}" checked="${contractInstance.statements.contains(statement.id)}" />
<label for="statements[${i}]">${statement.code}</label>
</g:each>
But i just cannot get a list of checked statements to the controller (there are null elements in the list, there are repeated statements...).
Any idea how to achieve this?
This is possible, but it does require a bit of a hack. First off, every checkbox must have the same name, "statements":
<g:each in="${org.example.Statement.list(sort: 'id', order: 'asc')}" var="statement" status="i">
<g:checkBox name="statements" value="${statement.id}" checked="${contract.statements.contains(statement)}" />
<label for="statements">${statement.content}</label>
</g:each>
Second, in the controller you have to remove the "_statements" property before binding:
def contract = Contract.get(params.id)
params.remove "_statements"
bindData contract, params
contract.save(failOnError: true)
The check box support hasn't been designed for this use case, hence the need for a hack. The multi-select list box is the one typically used for this type of scenario.
I personally prefer to get the list of Id's in this case.
<g:each var="book" in="${books}">
<g:checkBox name="bookIds" value="${book.id}" ...
</g:each>
Command Object:
class BookCommand {
List<Serializable> bookIds
}
In controller action:
BookCommand bc ->
author.books = Book.getAll(bc.bookIds)
Change the checkbox to something like this.
<g:checkBox name="statements.${statement.id}" value="true" checked="${contractInstance.statements.contains(statement)?:''}" />
and then in the controller, in params.statements you will get a list with the IDs of the checked statements.
Also notice the ?:'' in the checked property, it's a good idea to add it because any value(even 'false') in the checked property is interpreted as checked.
Are you mapping request directly to Contract? It's much more secure to map incoming request into an Command object.
As about mapping a list - values are mapped only to existing elements. I mean it cannot create new list elements. You need to prepare it before mapping. If you know that there is always 3 elements, you can make:
class ContractCommand {
List statements = [
new Statement(),
new Statement(),
new Statement(),
]
}
and map request to this object
I have a model that contains a collection, such as this:
class MyModel
{
public List<MySubModel> SubModels { get; set; }
}
In the view, I want to dynamically add/remove from this list using Javascript before submitting. Right now I have this:
$("#new-submodel").click(function () {
var i = $("#submodels").children().size();
var html = '<div>\
<label for="SubModels[' + i + '].SomeProperty">SomeProperty</label>\
<input name="SubModels[' + i + '].SomeProperty" type="textbox" />\
</div>'
$("#submodels").append(html);
});
This works, but it's ugly. And, if I want to show those labels/textboxes for the existing items, there's no clean way to do that either (without duplicating).
I feel like I should be able to use Razor helpers or something to do this. Any ideas? Help me stay DRY.
You approach may lead to unexpected errors if you when you are removing or adding the divs. For example you have 4 items, you remove the first item, then $('#submodels').children().size() will return 3, but your last inserted div has the name attribute value set SubModels[3].SomeProperty which results in a conflict. And if your posted values contain SubModels[1] but not SubModels[0] the default model binder will fail to bind the list (it will bind it as null). I had to learn this the hard way...
To eliminate the aforementioned problem (and your's) I suggest you do something like this:
$("#addBtn").click(function() {
var html = '<div class="submodel">\
<label>SomeProperty</label>\
<input type="textbox" />\
</div>'; // you can convert this to a html helper!
$("#submodels").append(html);
refreshNames(); // trigger after html is inserted
});
$(refreshNames); // trigger on document ready, so the submodels generated by the server get inserted!
function refreshNames() {
$("#submodels").find(".submodel").each(function(i) {
$(this).find("label").attr('for', 'SubModels[' + i + '].SomeProperty');
$(this).find("label").attr('input', 'SubModels[' + i + '].SomeProperty');
});
}
Then your view (or even better an EditorTemplate for the SubModel type) can also generate code like:
<div class="submodel">
#Html.LabelFor(x => x.SomeProperty);
#Html.EditorFor(x => x.SomeProperty);
</div>
It would also be possible to convert the code generation to a html helper class, and use it in the EditorTemplate and in the JavaScript code
I would recommend you going through the following blog post.