View takes values from query string instead of model - asp.net-mvc-4

I have an action in my controller:
public PartialViewResult MyAction(int? myId, int? myId2)
{
MyModel model = new MyModel() { MyId = 10, MyId2 = 20 }
return PartialView(model);
}
Here is my view:
#model StartSite.Models.Shared.MyModel
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.MyId)
#Html.HiddenFor(m => m.MyId2)
<p>
<input type="submit" value="Submin" />
</p>
}
Lets call MyAction with params myId=1&myId2=2. But the model is created with different values new MyModel() { MyId = 10, MyId2 = 20 }. And what should be rendered in view? As I expect it should be:
<input id="MyId" name="MyId" type="hidden" value="10">
<input id="MyId2" name="MyId2" type="hidden" value="20">
But in fact the result is:
<input id="MyId" name="MyId" type="hidden" value="1">
<input id="MyId2" name="MyId2" type="hidden" value="2">
As I guess the Html.HiddenFor takes values not from my model but from Reauest.QueryString which is myId=1&myId2=2 at the moment the view is rendered.
Why it happens? Is it expected behaviour?
UPDATE 1:
I've edited my question to be more clear.

to have access to the Model in the submit try with this
[HttpPost]
public virtual PartialViewResult MyAction(MyModel model)
{
//MyModel model = new MyModel();
// if (myId != null)
// model.MyId= myId;
// else if (myId2 != null)
// model.MyId2= myId2;
//now you have access to your model values
return PartialView(model);
}

This is expected behavior. Firstly, an Action without any Attributes is automatically a HttpGet. Next, your Action expects a value for 'myId'. This means, if the url calling your Action has a querystring that matches, it'll accept the value. Finally, the value your Action accepts is case-insensitive. Therefore, you do not need to manually set your model values. You can simply do this:
public virtual PartialViewResult MyAction(MyModel model)
{
return PartialView(model);
}
So when you go to your url, e.g. localhost/myaction?myId=2, model.MyId will be set to 2.
If you do NOT want your model set by a querystring, you must change your Action to not accept any values.

Related

Asp.Net Core - Display string list values in textarea?

I have a list whose values ​​are taken from the database,I want each of these values ​​to be displayed in a line in textarea...
Controller :
public async Task<IActionResult> AddOrEditPoll(Guid Id)
{
var polloptionList = await _admin.GetQuestionsListByPollId(Id);
PollViewModel model = new PollViewModel();
model.AnswerList = new List<string>();
foreach (var item in polloptionList)
{
model.AnswerList.Add(item.Answer);
};
return View(model);
}
View :
<div class="form-group">
<label class="control-label">Answer</label>
<textarea asp-for="AnswerList" class="form-control"></textarea>
</div>
ّI want it to be displayed as follows:
Can you guide me if you have a good solution?
You can try to replace asp-for with id and name,asp-for will set the value of textarea with AnswerList,and then convert AnswerList to string.Here is a demo:
Action:
public IActionResult AddOrEditPoll() {
PollViewModel model = new PollViewModel();
model.AnswerList = new List<string> { "answer1", "answer2" , "answer3" };
return View(model);
}
View:
<div class="form-group">
<label class="control-label">Answer</label>
<textarea name="AnswerList" class="form-control" style="text-align:right">#string.Join("\n ", Model.AnswerList)</textarea>
</div>
result:

First option in Blazor InputSelect displayed but value is null

I have encountered a weird behavior of InputSelect element in Razor component.
On my input form, I have several fields bound with the model (Partner). Some of these fields I placed in form of dropdown selection. Because the bound field's (PartnerCategory) value is the id (integer) I fetch a lookup table from DB with a name corresponding to a selected id.
On a page, I can see all names in the dropdown list. But when I try to insert a record from the form to the database it throws an SQL exception, because InputSelect treats the first value in the list as NULL. Just to be clear - there is no blank value in the dropdown list, and all names are shown. It just takes it's value as NULL. Followingly because the data type is an integer and it converts NULL to zero. And because I don't have an id that is zero in my table, the Insert command fails.
Below is my simplified code
<EditForm Model="#partner">
<InputSelect #bind-Value="partner.PartnerCategoryId">
#if (categoryList != null)
{
#foreach (var item in categoryList.OrderBy(x => x.PartnerCategoryId))
{
<option value="#item.PartnerCategoryId">#item.Name</option>
}
}
</InputSelect>
</EditForm>
#code {
Partner partner = new Partner();
private IEnumerable<PartnerCategory> categoryList;
protected override async Task OnInitializedAsync()
{
categoryList = await CategoryService.GetAllAsync();
}
}
How can I handle this? Does it bind values to a model before it fetches data from DB?
To solve this issue you can add <option value="">Select...</option> in your code like this:
<InputSelect #bind-Value="partner.PartnerCategoryId">
#if (categoryList != null)
{
<option value="">Select...</option>
#foreach (var item in categoryList.OrderBy(x => x.PartnerCategoryId))
{
<option value="#item.PartnerCategoryId">#item.Name</option>
}
}
</InputSelect>
And in your PartnerCategory model define the PartnerCategoryId as required. Note that the type of PartnerCategoryId is nullable: int?
[Required]
public int? PartnerCategoryId {get; set;}
This will prevent the 'submission' of your form unless the user has selected a value
To test the new changes:
Add the OnValidSubmit attribute to your EditForm component and set its value to "HandleValidSubmit"
Add a HandleValidSubmit method, like this:
private void HandleValidSubmit()
{
// Put code here to save your record in the database
}
Add a submit button at the bottom of your EditForm:
<p><button type="submit">Submit</button></p>
Run your app, and hit the "Submit" button...As you can see the form is not "submitted", and the select element's borders are painted red.
Here's a complete version of your code:
<EditForm Model="#partner" OnValidSubmit="HandleValidSubmit">
<InputSelect #bind-Value="partner.PartnerCategoryId">
#if (categoryList != null)
{
<option value="">Select...</option>
#foreach (var item in categoryList.OrderBy(x => x.PartnerCategoryId))
{
<option value="#item.PartnerCategoryId">#item.Name</option>
}
}
</InputSelect>
<p><button type="submit">Submit</button></p>
</EditForm>
#code {
Partner partner = new Partner();
private IEnumerable<PartnerCategory> categoryList;
protected override async Task OnInitializedAsync()
{
categoryList = await CategoryService.GetAllAsync();
}
private void HandleValidSubmit()
{
Console.WriteLine("Submitted");
}
}
In case someone is facing the same issue, here is my code which solved the issue:
<div class="mb-3 form-check">
<label for="category" class="form-label">Select category</label>
<InputSelect TValue="int" #bind-Value="subcategory.CategoryId" class="form-control" id="category">
<option value="">Select...</option>
#foreach(var cate in categories)
{
<option value="#cate.CategoryId">#cate.CategoryName</option>
}
</InputSelect>
<ValidationMessage For="#(()=>subcategory.CategoryId)"/>
</div>

Optional query string parameters in ASP.NET Razor pages

In my advanced search form I have around 25 fields. When I submit the form all parameters are passed to the URL even when null. How may I pass the parameters which have only been changed by the user and have a value? All 25 fields are optional.
I have around 25 of these:
[BindProperty(SupportsGet = true)]
[Display(Name = "Foo")]
public int? Foo{ get; set; }
OnGetAsync:
public async Task<IActionResult> OnGetAsync()
{
Properties = await _DarkMatterRepo.FindAsync(Foo, ...)
return Page();
}
How may I pass the parameters which have only been changed by the user and have a value?
To achieve the above the requirement, you can try to check form data and navigate to expected page, like below.
Code of form
<form method="get">
<input type="text" name="name">
<input type="email" name="emailaddress">
<input type="number" name="age">
#*other input fields*#
<input type="submit">
</form>
Dynamically generate QueryString and navigate to specific page
<script>
$( "form" ).on( "submit", function( event ) {
event.preventDefault();
//console.log($(this).serializeArray());
var formdata = $(this).serializeArray();
var parm = "";
$.each(formdata, function (index, item) {
if (item.value!="") {
parm += item.name + "=" + item.value + "&";
}
});
//console.log(parm.substring(0, parm.length - 1));
window.location.href = "#Url.Page("Index")" + "?" + parm.substring(0, parm.length - 1);
});
</script>

Adding a record to the database based on input passed from a link on another form MVC 4

I have been using ASP.NET MVC 4 for a while but I have not yet come across a situation where i need to insert a value into the database from a scaffolded Create view which is based on a value passed from another view. I have tried to infer from the Edit view to try and modify my code to work but I have run into a snag. I got an error similar to this post. Here is my code from the view passing the value
#Html.ActionLink("Allocate", "Create", "Allocation", new { id=item.requestID}, null)
this is from the list of requests already in the database from the Index view
here is my code on the controller that is trying to force the Create method to use the ID passed from the link above
public ActionResult Create(int id = 0)
{
Request request = db.Requests.Find(id);
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View(request);
}
then here is the posting code to the db
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Allocation allocation)
{
if (ModelState.IsValid)
{
db.Allocations.Add(allocation);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", allocation.requestID);
return View(allocation);
}
Basically what I am trying to do is allocate funds to a request made where by the allocation is entered into the db based on the request id. I am trying to prevent the user from choosing the request id from a drop down list. When I run this i get an error
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Request_A52006F7570E0448EE323CB36858E4D13EED0BAD958340B32FF166708545DA8C', but this dictionary requires a model item of type 'BudgetAllocation.Models.Allocation'.
If theres anyone out there who can help me out with this please do as soon as you can. I appreciate all the effort offred!!!!!
//EDIT
Here is my Create view
#model BudgetAllocation.Models.Allocation
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Allocation</legend>
#Html.HiddenFor(model => model.requestID)
<div class="editor-label">
#Html.LabelFor(model => model.allocAmount, "Amount")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.allocAmount)
#Html.ValidationMessageFor(model => model.allocAmount)
</div>
<p>
<input type="submit" value="Allocate" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The problem is your view id strongly typed with BudgetAllocation.Models.Allocation while in get action of Create you are passing object of type BudgetAllocation.Models.Request thats why you are getting the exception.
You have to pass object of type BudgetAllocation.Models.Allocation in Create get action as well because you view is strongly typed to it.
public ActionResult Create(int id = 0)
{
Request request = db.Requests.Find(id);
return View(request) // <-------------- here is the mistake
}
it should return allocation object, something like this, it is just an example may be you need to do some other thing instead of selecting:
public ActionResult Create(int id = 0)
{
Allocation allocation = db.Allocations.Find(x=>x.requestID == id);
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View(allocation);
}
UPDATE:
you simply need to do like this not return allocaiton object return simply View:
public ActionResult Create(int id = 0)
{
ViewBag.requestID = new SelectList(db.Requests, "requestID", "requestID", request.requestID);
return View();
}

mvc passing parameter to controller

I am very new to MVC
I need some help to over come the issue of passing parameter to a controller on form submit
what i have got is the following controller and the view
public ActionResult Index(string method ="None")
{
if (Request.HttpMethod == "POST")
{
switch (method)
{
case "Add10":
_bag.GetBag = Get100Products().Take(10).ToList<Product>();
break;
case "Clear":
_bag = null;
_bag.GetBag = null;
_bag = new Models.Bag();
break;
case "Add":
if ((Request.Form["Id"] != null) && (Request.Form["Id"] != ""))
{
if (_bag.GetBag.Count < 100)
{
var p = GetProduct(Request.Form["Id"]);
int qnt = Convert.ToInt16(Request.Form["qnt"]);
if (p.ItemNumber != null)
{
p.Quantity = qnt;
p.Index++;
_bag.Item = p;
}
}
}
break;
}
}
return View(_bag.GetBag);
}
and the view part of the view
<div style="vertical-align:middle">
#using (Html.BeginForm("", "Home", new { method = "Add10" }, FormMethod.Post))
{
<!-- form goes here -->
<input type="submit" value="Add 10 Items to bag" />
}
#using (Html.BeginForm("GetDiscount", "Home", FormMethod.Post))
{
<div>
<!-- form goes here -->
<input type="submit" value="Get Discount" />
With MAX time in seconds <input type="text" name="time" maxlength="2" value="2" />
</div>
}
#using (Html.BeginForm("", "Home", new { method = "Clear" }, FormMethod.Post))
{
<input type="submit" value="Empty the bag" />
}
</div>
so i am expecting when the use clicked button Add 10 Items to bag to pass the method value "Add10" to the index controller and when clicked Empty the bag to pass "Clear" the method value in index controller
but it always shows as "None"
what have I done wrong?
</form>
First of all, you have to add [HttpPost] to your controller in order to accept POST requests:
[HttpPost]
public ActionResult Index(string method ="None")
{
You should differentiate GET and POST actions.
You can do like this:
// [HttpGet] by default
public ActionResult Index(Bag bag = null)
{
// "bag" is by default null, it only has a value when called from IndexPOST action.
return View(bag);
}
[HttpPost]
public ActionResult Index(string method)
{
// Your logic as specified in your question
return Index(_bag.GetBag);
}
EDIT:
Your code is wrong, for example you will get a NullReferenceException because your try to call a property on a null object (_bag):
_bag = null;
_bag.GetBag = null; // NullReferenceException: _bag is null!
Also your code would be cleaner and more easier to maintain if we split this Action into several actions and follow the technology philosophy.
Do you consider refactoring this piece of code into smaller and more understandable chunks?