I have html code in my cshtml file using MVC
<table id="mylist">
<tr>
<td>
<input type="text" id="list_0" name="list[0]">
</td>
<td>
<input type="text" id="list_1" name="list[1]">
</td>
</tr>
</table>
I have a button that dynamically adds a new tr with tds. After clicking submit button, in the controller, how do I get all the values from textboxes within the table? Is there an easier way to retrieve the values, so that I can save them in the database?
You are on the right path. Your dynamic text boxes should have name attribute set as you are already doing it. The model binder uses that for binding. Your model/ViewModel can look like below:
public class YourModel
{
public string[] List { get; set; }
}
Then the controller will have action method like below:
[HttpPost]
public ActionResult YourAction(YourModel model)
{
//you can access the text values like below:
var listItems = model.List;
// your call to some services methods that saves these values
}
You can read more about model binding with arrays in Scott Hanselman's post and model binding in general at this MSDN magazine article
Related
I'm learning Spring boot. I have a list of products with unique ids, and I want to implement a "lookup by id" functionality, but I don't know how to do it, I searched but got totally different stuff.
I already have a #Getmapping method like this:
#Getmapping(/products/{id})
If I manually type in the id in the url I'll get what I what. But I want to have an input box in the HTML page like:
<form>
Look up by id: <input/>
</form>
and after I submit the form it'll redirect to that page. For example, if I enter input of 1, it'll go to localhost:8080/products/1
I've been searching but all I got was stuff about #Postmapping.
Add a #PostMapping to your controller:
#Controller
#RequestMapping("/products")
public class ProductController {
#GetMapping //Controller method for showing the empty form
public String index(Model model) {
model.addAttribute("formData", new SearchFormData()); // Create an empty form data object so Thymeleaf can bind to it
return "index";
}
#PostMapping
public String searchById(SearchFormData formData) {
return "redirect:/products/" + formData.getId(); //Use the value the user entered in the form to do the redirect
}
#GetMapping("/{id}")
public String showProduct(#PathVariable("id") long id) {
...
}
}
With SearchFormData representing the form fields (there is only 1 field in this case):
public class SearchFormData {
private long id;
// getters and setters
And update Thymeleaf template like this:
<form th:action="#{/products}" th:method="post" th:object="${formData}">
<input th:field="*{id}" type="number">
<button type="submit">Search</button>
</form>
Note that the value of th:object needs to match with the name used to add the SearchFormData instance to the model.
See Form handling with Thymeleaf for more info.
The following simple code will direct you to a URL that is generated from a concatenation of the base address of the <form>'s action attribute and the value of its first <input>:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
this.action="/product/"+this.querySelector("input").value;
console.log(this.action);
// in real code: uncomment next line!
// this.submit()
})
<form>
Look up by id: <input type="text" value="123" />
</form>
In the real code you will delete the console.log() und uncomment the following line: this.submit().
Alternatively you could also do:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
location = "/product/"+this.querySelector("input").value;
})
This will redirect you to the page without actually submitting the form.
so simply want to make a button that will call the controller action passing a parameter...
have everything I believe in place but unable to configure/reference the parameter in the actionlink helper...
Yes I will be refactoring my button onclick once I get through this html helper setup...
<h1 style="font-size:30px">Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
<form action="/action_page.php">
<label for="productKey">Product Key:</label>
<input type="text" id="productKey" name="productKey"><br><br>
</form>
<p>Click the "Get Key" button and a trial key will be generated custom to your IBMi hardware".</p>
<p>
#Html.ActionLink(
"Get Key",
"GetTrialKey", // controller action
"HomeController", // controller
new { productKey }, // IT DOES NOT LIKE PRODUCTKEY (REFERENCED ABOVE)
new { #class = "btn btn-info" }) // html attributes
</p>
<div class="display-2">
<a button class="text-center, btn btn-info form-control text-white" typeof="button" onclick="location.href='#Url.Action("GetTrialKey(productKey)")'">Get Key</button></a>
<p>
<br />
</p>
</div>
refactored to...
view...
<form action="HomeController/getTrialKey" method="POST">
<label for="productKey">Product Key:</label>
<input type="text" name="productKey" maxlength="22" value="xxx xxxxxxx xxxx xxxxx"><br><br>
<input type="submit" value="Get Trial Key" class="btn btn-primary" />
</form>
controller...
[HttpPost]
public async Task<IActionResult> getTrialKey(string productKey)
{
when I run it i get...
This localhost page can’t be foundNo webpage was found for the web address: https://localhost:44346/HomeController/getTrialKey
Referring back to one of the comments:
I didn't discourage you from using HTML Helpers. I just meant the way you constructed the form and you used ActionLink was wrong. And it would be easier to just have an input for the product key inside the form, if that's the only thing you want to post back to the server.
And I would highly recommend you to read through documentations from Microsoft, at least this one: https://dotnet.microsoft.com/apps/aspnet/mvc to understand what a MVC is. From your code sample, I didn't see you used M - Model at all.
Anyway, if you just only want to get the product key the user types in, I would do it like this:
Define a Controller
I dislike the idea of putting everything under /home (i.e., HomeController). Just think about the URL to the page that would make sense to the user.
Now I am guessing what you are trying to do. I saw terms like product keys and trial keys. What about a controller called ProductKeyController:
public class ProductKeyController : Controller
{
// This corresponds to /productkeys, and you can list all the product keys
// on the view it returns.
public ActionResult Index()
{
return View();
}
// This corresponds to /productkeys/create, and you can create a specific product
// key by asking the user to provide a trial key?
// The view this returns might be the page where you build the form
public ActionResult Create()
{
...
return View();
}
// This corresponds the form post.
[HttpPost]
public ActionResult Create(CreateProductKeyViewModel model)
{
...
return View(model);
}
}
The view model
Your MVC controller is responsible to fetch data, if needed, build a view model, and pass it to the view. When you create a product key, if you need to ask the user to enter anything, you can declare a model and properties within it:
public class CreateProductKeyViewModel
{
[Required]
[Display(Name = "Trial Key")]
[MaxLength(22)]
public string TrialKey { get; set; }
}
The View Create.cshtml
Since you know the controller will be passing the view model to the view, you can declare it on top of the view so that everything you do with the view model inside the view is strongly-typed.
#model CreateProductViewModel
#{
Layout = "xxx";
}
<h1>Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
#using(Html.BeginForm("create", "productKey", new { area = "" }, FormMethod.Post))
{
#Html.LabelFor(x => x.TrialKey)
#Html.TextBoxFor(x => x.TrialKey)
<button type="submit">Create</button>
}
See how everything within the view is strongly-typed? You don't have to manually create the form and the input for asking user for the trial key.
And when the user enters the trial key and presses submit, it will post back to the Post method of Create. Since the view is declared with the view model, and the view model is the parameter of the create method, MVC has already done the model binding for you hence you will get what user entered on the post back.
This is at least something to get you started.
Note: I wrote everything by hand. Not tested.
im using GWT uibinder method and my html contains a textbox like
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:idmw="urn:import:com.testing.wid.impl">
<g:HTMLPanel>
<table align="center" valign="center" height="25%">
<tr><td><g:TextBox ui:field='searchS' /></td></tr>
</table>
</g:HTMLPanel>
How can i TURN OFF autocorrect and autocapitalize for this Textbox??
i tried
<g:TextBox ui:field='searchS' autocapitalize="off" autocorrect="off"/>
but i get
[ERROR] Class TextBox has no appropriate setAutocorrect()
method Element <g:TextBox autocapitalize='off' autocorrect='off' ui:field='searchS'>
Any other way i can do this???
Thanks
As already pointed by #Boris Brudnoy there is no built-in way to do it with TextBox. Takin futher his suggestion it will be nice to extract this into new custom component (to simplify reuse and support):
Add new package (for example com.app.shared.customcontrol)
Add new CustomTextBox:
public class CustomTextBox extends TextBox {
public void setAutocomplete(String value){
this.getElement().setAttribute("autocomplete", value);
}
public void setAutocapitalize(String value){
this.getElement().setAttribute("autocapitalize", value);
}
}
Declare new namespace using UI binder and use your component:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:c="urn:import:com.app.shared.customcontrol">
<g:HTMLPanel ...>
<c:CustomTextBox ui:field="..." autocomplete="off" autocapitalize="off" />
</g:HTMLPanel>
</ui:UiBinder>
As alternative way if you want apply these settings system wide you can do it via constructor:
public class CustomTextBox extends TextBox {
public CustomTextBox() {
this.getElement().setAttribute("autocomplete", "off");
this.getElement().setAttribute("autocapitalize", "off");
}
....
}
What you've tried will not work since GWT doesn't translate UiBinder attributes directly into HTML element properties. Instead, as your error message hints, it looks up widget setter methods of the form set[UiBinder_attribute]. Since there is neither setAutocorrect nor setAutocapitalize method in the TextBox class, the errors you're getting are expected.
What you could do is drop to the element level and write something like this, e.g. in your widget's constructor:
public MyWidget() {
initWidget(uiBinder.createAndBindUi(this));
searchS.getElement().setProperty("autocapitalize", "off");
searchS.getElement().setProperty("autocorrect", "off");
}
I'm new-ish to struts and I'm particularly stuck on an area of struts code which has to do with the radio button. No matter what I do I can't get anything but a false value from the following: (CostForm)
<td align="left" width="200px" colspan="2">
<html:radio property="responsableBool" value="false"/>No
<html:radio property="responsableBool" value="true"/>Yes
</td>
It is then initialised from this piece of code:
CostForm costform = (CostForm) form;
Cost cost = new Cost();
costform.populateModel(cost);
and the populateModel just has: PropertyUtils.copyProperties(cost,this);
The only thing I can think of is that struts doesn't allow the radio buttons to reference the same property with different values.
Given the form:
public class CostForm extends ActionForm {
private boolean responsableBool; // And getter/setter
}
The HTML:
<html:form action="/costsub">
<html:radio property="responsableBool" value="false"/>No
<html:radio property="responsableBool" value="true"/>Yes
<html:submit/>
</html:form>
The action:
public ActionForward execute([args elided]) throws Exception {
CostForm costForm = (CostForm) form;
System.out.println(costForm.isResponsableBool());
// etc.
When I click "No" and "Yes" I get the expected boolean value inside the action.
I'd double-check things like spelling (the English spelling would be "responsible"; perhaps it's spelled correctly in Cost?), action/form mismatches (are you using the right form "name" in the action mapping?), and so on.
I am building a WebPages site and have an issue when I try to pass ModelState data to a partial page.
Here is the code for Create.cshtml:
#{
Page.Title = "Create Fund";
var name = "";
if (IsPost) {
name = Request.Form["name"];
if (!name.IsValidStringLength(2, 128)) {
ModelState.AddError("name", "Name must be between 2 and 128 characters long.");
}
}
}
#RenderPage("_fundForm.cshtml", name)
Here is the code for _fundForm.cshtml:
#{
string name = PageData[0];
}
<form method="post" action="" id="subForm">
<fieldset>
#Html.ValidationSummary(true)
<legend>Fund</legend>
<p>
#Html.Label("Name:", "name")
#Html.TextBox("name", name)
#Html.ValidationMessage("name", new { #class = "validation-error" })
</p>
<p>
<input type="submit" name="submit" value="Save" />
</p>
</fieldset>
</form>
The issue I am having is when there is an error for "name", the validation error does not display. Is there a special way to pass ModelState between the two pages?
_fundForm is going to be shared between Create.cshtml and Edit.cshtml.
ModelState is a readonly property of System.Web.WebPages.WebPage class. Its backing field is a private ModelStateDictionary and is initialized at first access. I can't see any way to force ModelState across pages, apart from doing it via reflection as seen in SO question: Can I change a private readonly field in C# using reflection?
Otherwise, you can simply use a third parameter in the invocation, like this:
#RenderPage("_fundForm.cshtml", name, ModelState);
In effect, the first parameter after the page name will become the Model of the new page, so there is enough space (i.e. the next parameter) to pass the ModelState.
In your "_fundForm.cshtml" merge the ModelState received by the calling page with the local one, like this:
#{
//In _fundForm.cshtml
var ms = PageData[1];
ModelState.Merge((ModelStateDictionary)ms);
}