The params doesn't give the right values in the ActionResult - c#

I have a process to import Excel-data. That works fine. But for starting the process I have to select some values. "Year from", "Year to" and the parameter isTest.
I want to change the range of the import-values. I have tried several solutions. But nothing works.
After changing the values and by clicking on the submit-button, I want to see the new values.
What do I have to change in the code?
This is the ActionResult:
[HttpPost]
public IActionResult Importeren(int jaarVan, int jaarTm, bool isTest)
{
//minValue = 1981; maxValue = 2004; isTest = true;
_importProces.Start(jaarVan, jaarTm, isTest);
return View();
}
This is the html-page:
#using (Html.BeginForm("Importeren", "Home", new { jaarVan = ViewBag.JaarVan, jaarTm = ViewBag.JaarTm, isTest = ViewBag.IsTest }, FormMethod.Post))
{
<div class="form-group">
<div class="col-md-2 col-xl-2">
#Html.Label("Jaar vanaf:")
</div>
<div class="col-md-2 col-xl-2">
#Html.Hidden("#JaarVan")
<span id="lblJaarVan">#ViewBag.JaarVan</span><input type="range" id="jaarVan" value="#ViewBag.JaarVan" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1"
onshow="showJaarVan(this.value)" oninput="showJaarVan(this.value)" onchange="showJaarVan(this.value)" />
</div>
<div class="col-md-2 col-xl-2">
#Html.Label("Jaar tot/met:")
</div>
<div class="col-md-2 col-xl-2">
#Html.Hidden("#JaarTm")
<span id="lblJaarTm">#ViewBag.JaarTm</span><input type="range" id="jaarTm" value="#ViewBag.JaarTm" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1"
onshow="showJaarTm(this.value)" oninput="showJaarTm(this.value)" onchange="showJaarTm(this.value)" />
</div>
<div class="col-md-2 col-xl-2">
#Html.Label("Is test:")
#Html.Hidden("#IsTest")
<input type="checkbox" id="isTest" value="#ViewBag.IsTest" checked="#ViewBag.IsTest" />
</div>
#*<progress*#
<p> <input class="text-primary" type="submit" value="Import Exceldata" name="Opslaan" /></p>
</div>
}
<script>
//$(document).ready(function () {
// $('#frmsubmit').submit(function () {
// var jaarVan = document.getElementById("#jaarVanRange").value;
// var jaarTm = document.getElementById("#jaarTmRange").value;
// var isTest = document.getElementById("#isTest").value;
// })
//});
function showJaarVan(newVal) {
document.getElementById("lblJaarVan").innerHTML = newVal;
ViewViewBag.JaarVan = newVal;
}
function showJaarTm(newVal) {
document.getElementById("lblJaarTm").innerHTML = newVal;
ViewViewBag.JaarTm = newVal;
}
</script>

I believe the model binding for MVC form posts relies on the name attribute of your inputs, try adding names to your inputs to match the parameters in your action:
<input type="range" id="jaarTm" name="jaarTm" value="#ViewBag.JaarTm" min="#ViewBag.MinValue" max="#ViewBag.MaxValue" step="1" onshow="showJaarTm(this.value)" oninput="showJaarTm(this.value)" onchange="showJaarTm(this.value)" />
I also don't think you need to include the route values object in the BeginForm() call because form data has higher precedence in model binding. That object is also evaluated before the post and hold the original years and not the new ones that you set in your range inputs.

Related

ASP.Net Core MVC how to post unknown number of fields against strongly-typed model

Totally stumped on this. I have a page where users can click an "Add Step" button, and some javascript appends rows with additional inputs from a <template> tag to a form. When debugging from the controller, the values are empty.
I looked at the following posts, but neither answer seem to help here (not sure if it's because I'm using .Net core - probably not):
Get post data with unknown number of variables in ASP MVC
Passing data from Form with variable number of inputs - MVC 5
Model (Procedure.cs)
public class Procedure
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public int MinNumber { get; set; }
public int MaxNumber { get; set; }
}
Controller (ProceduresController.cs)
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([FromForm]List<Procedure> model)
{
if (ModelState.IsValid)
{
foreach(var field in model)
{
// Database things will happpen eventually, but at this point the Count = 0
}
}
return View(model);
}
Currently the Create action signature includes the [FromForm] attribute, but I have no idea if that's required here. I also tried List<Procedure> model.
And the View (Create.cshtml)
I've stripped out a lot of the UI and javascript to make things easier to read
#model List<Procedure>
/* also tried #model Procedure */
#{
ViewData["Title"] = "Create Procedure";
ViewData["Sidebar"] = "App";
}
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="All" class="field-validation-summary"></div>
<div class="builder">
<div class="field-row">
<div class="field-group">
<label class="field-label">Instruction Name</label>
<input type="text" name="Name" class="field type:text" placeholder="Enter an instruction name" />
</div>
<div class="field-group">
<label for="Type" class="field-label">Instruction Type</label>
<select id="Type" name="Type" class="field type:select stretch">
/* removing for brevity */
</select>
</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label">Minimum Number</label>
<input type="number" name="MinNumber" class="field type:number" />
</div>
<div class="field-group">
<label class="field-label">Maximum Number</label>
<input type="number" name="MaxNumber" class="field type:number" />
</div>
</div>
</div>
<div class="field-row">
<div class="field-group">
<div class="add-step">Add Step</div>
</div>
</div>
<div class="field-row">
<div class="field-group">
<input type="submit" value="Submit Procedures" class="button button-submit" />
</div>
</div>
</form>
<template id="template">
<details class="accordion" open>
<summary>Procedure Step</summary>
<div class="details-content">
<div class="field-row">
<div class="field-group">
<label class="field-label">Instruction Name</label>
<input type="text" name="Name" class="field type:text" placeholder="Enter an instruction name" />
</div>
<div class="field-group">
<label for="Type" class="field-label">Instruction Type</label>
<select id="Type" name="Type" class="field type:select stretch">
/* removing for brevity */
</select>
</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label">Minimum Number</label>
<input type="number" name="MinNumber" class="field type:number" />
</div>
<div class="field-group">
<label class="field-label">Maximum Number</label>
<input type="number" name="MaxNumber" class="field type:number" />
</div>
</div>
</div>
</details>
</template>
#section Scripts {
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
const modal = new Modal();
function getParent(element, selector) {
for (; element && element !== document; element = element.parentNode) {
if (element.classList.contains(selector)) {
return element;
}
}
return null;
}
this.Builder = function () {
this.template = document.querySelector('#template');
this.builder = document.querySelector('.builder');
this.addStep = document.querySelector('.add-step');
this.clone = null;
this.counter = 0;
this.addStep.addEventListener('click', this.add.bind(this));
};
Builder.prototype = {
add: function () {
this.counter++;
this.clone = document.importNode(this.template.content, true);
this.builder.appendChild(this.clone);
// i'm doing things here to set the new field attribute values - removed for brevity
}
};
let builder = new Builder();
builder.add();
</script>
}
In the javascript do I need to set the new field name values a specific way? Should they look like <input type="text" name="Name[index]"> or <input type="text" name="#Model.Procedure[index].Name">?
I tried both but the Count is still 0 when posting back. I've pretty much hit a wall, so I appreciate any and all help.
.NET requires a little more effort for model binding on complex types. Naming of elements is important.
<input name="model[index].Name" />
<input name="model[index].Type" />
<input name="model[index].MinNumber" />
<input name="model[index].MaxNumber" />
The naming convention to bind is: Name of the parameter in your controller (model), index, property name. This will properly pass all objects to your list.
The same is true if you are passing objects to a list inside your model.
For example
public class Example {
public int Id { get; set; }
public List<Procedure> Procedures { get; set; }
}
in this case we just need to tell .NET what property you are mapping too.
<input name="model.Procedures[index].Name" />
In some cases you may need to also add a hidden field for the index: https://www.red-gate.com/simple-talk/dotnet/asp-net/model-binding-asp-net-core/

How to pass data from the View textbox to Controller using ASP.NET MVC

I want to pass data from Textbox value to the controller. The Textbox Value its string and on the controller, it must be converted to double. But I seem to fail on the presented code below. I don't know why the code does not work.
===VIEW CODE===
#using (Html.BeginForm("OnceOff"))
{
<div class="container">
<div class="bg hidden-sm"></div>
<div class="row">
<div class="col">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Once-Off Payment</h3>
</div>
<div class="panel-body">
<input type="text" id="smsbundles" name="smsbundles" value="50" />
</div>
<div class="text-right">
#Html.ActionLink("Buy Now", "OnceOff", "Home", new { area = "" }, new { #class = "btn btn-block btn-success" })
</div>
</div>
</div>
</div>
</div>
}
===CONTROLLER===
public ActionResult OnceOff(string smsbundles)
{
double bundle = Convert.ToDouble(smsbundles);
var onceOffRequest = new PayFastRequest(this.payFastSettings.PassPhrase);
// Merchant Details
onceOffRequest.merchant_id = this.payFastSettings.MerchantId;
onceOffRequest.merchant_key = this.payFastSettings.MerchantKey;
onceOffRequest.return_url = this.payFastSettings.ReturnUrl;
onceOffRequest.cancel_url = this.payFastSettings.CancelUrl;
onceOffRequest.notify_url = this.payFastSettings.NotifyUrl;
// Buyer Details
onceOffRequest.email_address = "";
// Transaction Details
onceOffRequest.m_payment_id = "***";
onceOffRequest.amount = bundle; //30 or 50
onceOffRequest.item_name = "Once off option";
onceOffRequest.item_description = "Some details about the once off payment";
// Transaction Options
onceOffRequest.email_confirmation = true;
onceOffRequest.confirmation_address = "****";
var redirectUrl = $"{this.payFastSettings.ProcessUrl}{onceOffRequest.ToString()}";
return Redirect(redirectUrl);
}
Your input smsbundles is an input element inside a form.
You are using ActionLink helper, that generates <a> element that does not submit the form. That's the reason your controller action code is called with a null value of smsbundles.
Replace you ActionLink to a regular submit button:
<input type="submit" value="Buy Now" class="btn btn-block btn-success" />

Working with attribute routing in mvc

On my login page I have a dropdownlist to change the culture of the application for which I am doing a Ajax call to set the culture. By default I have set to 'en_US'.
My issue is when I am login directly without changing the culture I am able to login successfully, but when I change the culture and tries to login, I am not able to do that. Is this happening because of AJAX call made, which makes custom attribute not registered ?
Also, my login method has a custom attribute defined. Below is the code.
AJAX Call
$('#ddlLanguages').change(function () {
var val = $('#ddlLanguages').val()
createCookie('culturecookie', val, 7);
$.ajax({
type: "POST",
url: '/Account/GetCultureNew',
data: { culturename: val },
success: function (result) {
$("#logo-group").html('');
$(document.body).html('');
$(document.body).html(result);
},
error: function (data) {
//alert('Error');
}
});
});
Ajax Method in controller
[HttpPost]
public ActionResult GetCultureNew(string culturename)
{
if (!string.IsNullOrEmpty(culturename) & culturename.Contains("#"))
{
string[] strdata = culturename.Split('#');
if (strdata.Length > 0)
{
AppTenant tenant = HttpContext.Session.GetObjectFromJson<AppTenant>("TenantInfo");
if (tenant != null)
{
tenant.LoggedInCulture = strdata[0];
tenant.LanguageID = Convert.ToInt32(strdata[1]);
HttpContext.Session.SetObjectAsJson("TenantInfo", tenant);
}
}
}
List<SelectListItem> items = new List<SelectListItem>();
items = HttpContext.Session.GetObjectFromJson<List<SelectListItem>>("LanguageData");
foreach (var item in items)
{
if (item.Value == culturename)
{
item.Selected = true;
}
else
{
item.Selected = false;
}
}
var itemsString = JsonConvert.SerializeObject(items);
CookieOptions obj = new CookieOptions();
obj.Expires = DateTime.Now.AddMonths(3);
Response.Cookies.Append("Languagelist", itemsString, obj);
var viewModel = new LMS_User { ReturnUrl = string.Empty, LanguageList = items };
return View("Login", viewModel);
}
Login Method
[HttpPost]
[AllowAnonymous]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
[Route("Admin/Login/{clietname}")]
public async Task<IActionResult> Login([Bind(include: "Email,Password,RememberMe")] LMS_User model, string returnUrl)
{
// my login logic
}
EDIT :- 1
Login partial View
<div class="col-xs-12 col-sm-12 col-md-5 col-lg-4">
<div class="well no-padding">
<form action="#Url.Action("login", "account")" method="POST" id="login-form" class="smart-form client-form">
<header>
#obj["SingnIn"]
</header>
#Html.AntiForgeryToken()
<fieldset>
<section>
<label asp-for="LanguageList">#obj["LanguageList"] </label>
#Html.DropDownList("Languages", Model.LanguageList, null, new { id = "ddlLanguages", #class = "form-control" })
</section>
<section>
<label asp-for="Email">#obj["Email"]</label>
<label class="input">
<i class="icon-append fa fa-user"></i>
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
<b class="tooltip tooltip-top-right"><i class="fa fa-user txt-color-teal"></i>>#obj["tooltipEmail"]</b>
<span asp-validation-for="Email" class="text-danger"></span>
</label>
</section>
<section>
<label asp-for="Password">#obj["Password"]</label>
<label class="input">
<i class="icon-append fa fa-lock"></i>
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
<b class="tooltip tooltip-top-right"><i class="fa fa-lock txt-color-teal"></i>#obj["tooltippassword"] </b>
<span asp-validation-for="Password" class="text-danger"></span>
</label>
<div class="note">
<i class="fa fa-frown-o"></i> #obj["Forgot_password?"]
</div>
</section>
<section>
<label class="checkbox">
<input asp-for="RememberMe" />
<i></i>#obj["Remember_Me"]
</label>
</section>
<footer>
<button type="submit" class="btn btn-primary">
#obj["SingnIn"]
</button>
</footer>
</fieldset>
</form>
</div>
#{ await Html.RenderPartialAsync("_SocialMedia"); }
Edit 2:-Entire login view
<div id="content" class="container">
<div class="row">
#{ await Html.RenderPartialAsync("_LoginText"); }
#{ await Html.RenderPartialAsync("_LoginPartial"); }
</div>
</div>
However if I add location.reload() in AJAX success function, then by changing the culture I can login successfully.
Any help on this appreciated !
When you do the $(document.body).html(result);
The action part of the form goes missing. Hence it does not know where to post to.
Hope i have been of some help :)

How to pass string object data to mvc controller for posting?

This is my code
<form class="form-horizontal formtext col-xs-12 col-sm-10 col-lg-10 row">
<div class="form-group">
<div class="col-xs-12 col-sm-5 col-lg-3 paddnone">
<label class="control-label">First Name</label>
</div>
<div class="col-sm-5" ng-if="!newRecord">
<input type="text" class="form-control" ng-model="userDetails.firstname" />
</div>
<div class="col-sm-5" ng-if="newRecord">
<input type="text" class="form-control" ng-model="userDetails.firstName" />
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-sm-5 col-lg-3 paddnone">
<label class="control-label">Last Name</label>
</div>
<div class="col-sm-5" ng-if="!newRecord">
<input type="text" class="form-control" ng-model="userDetails.lastname" />
</div>
<div class="col-sm-5" ng-if="newRecord">
<input type="text" class="form-control" ng-model="userDetails.lastName" />
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-sm-5 col-lg-3 paddnone">
<label class="control-label">Email Id</label>
</div>
<div class="col-sm-5" ng-if="!newRecord">
<input type="text" class="form-control" ng-model="userDetails.emailid" />
</div>
<div class="col-sm-5" ng-if="newRecord">
<input type="text" class="form-control" ng-model="userDetails.emailId" />
</div>
</div>
<div class="col-xs-12 col-sm-9 col-lg-7 btnbg">
<button class="btn btn-primary " ng-click="create(userDetails)">
SAVE
</button>
<button class="btn btn-primary ">
Cancel
</button>
</div>
$scope.create = function (userDetails) {
var userData = angular.toJson(userDetails);
return dataFactory.post("/Usercontroller/Save", userData)
.then(function (result) {
alert("save successfully");
});
public class UserDataModel
{
public UserDataModel ();
public Dictionary<string, string> Data { get; set; }
public string FormName { get; set; }
}
public async Task<JsonNetResult> SaveUser(string applicationId, dynamic user)
{
Dictionary<string, string> dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(user);
UserDataModel userDataModel = new UserDataModel ();
//userDataModel .Data = dict;
userDataModel.FormName = "one";
userDataModel = await Post<UserDataModel>(new UserDataModel{ Data = dict });
return new JsonNetResult { Data = userDataModel };
}
In the above code when i click save button data pass to create function in js,then the entire data convert to string and pass to mvc controller , but the dynamic user got object , now how to deserialze and store to webapi.
So you have two options here, 1) You should use razor cshtml instead
of html, it will ease your life. Following link will help you.
http://blog.michaelckennedy.net/2012/01/20/building-asp-net-mvc-forms-with-razor/
If you want to use existing html code:
a) You have to make the name of all fields same as your class object variable, it will automatically parse it to object when server side c# code got the control after form submission.
b) Make the parameter of c# form submission handler function to FormCollection
Following links will help you,
ASP.NET MVC 3 Razor: Passing Data from View to Controller
Deserialization:
var json = JsonConvert.DeserializeObject<dynamic>(sampleJson);
var data = ((JObject)json.data).Children();
Deserialize JSON with dynamic objects
Have your HTML code like this, using Razor Syntax
#using (Html.BeginForm("Upload", "ModelClass")
{
#Html.ValidationSummary(true)
//HTML elements which are based on your model class members.
}
In your controller class method, it would send this form data to the Upload Method.
[HttpPost]
public ActionResult Upload(ModelClass model)
{
//Send model to the model object to Model class for processing
return RedirectToAction("Upload");
}
EDIT: This method I have used in ASP.NET MVC 5 on Visual Studio community edition 2013.

Ajax query refreshing whole page instead of part

I have simple View where I have a ajax form I use for filtering records:
#using (Ajax.BeginForm("Index", new AjaxOptions()
{
InsertionMode=InsertionMode.Replace,
UpdateTargetId="dane"
}))
{
#Html.Partial("SearchTab")
}
#Html.Partial("ShowPartial") // <--- id="dane"
Partial SearchTab:
<div class="row">
<div class="col-lg-3">
<div class="input-group">
<div class="input-group-addon name">
User name:
</div>
<input type="text" name="name" class="form-control" />
</div>
</div>
<div class="col-lg-3">
<div class="input-group">
<div class="input-group-addon surname">
User surname:
</div>
<input type="text" name="surname" class="form-control" />
</div>
</div>
<div class="col-lg-3">
<div class="input-group">
<div class="input-group-addon devicename">
Device name:
</div>
<input type="text" name="deviceName" class="form-control" />
</div>
</div>
<div class="col-lg-3">
<div class="input-group">
<div class="input-group-addon devicemanufacturer">
Device Manufactuer:
</div>
<input type="text" name="deviceManufacturer" class="form-control" />
</div>
</div>
</div>
<input type="submit" class="btn btn-default" value="Filter" id="filter"/>
<br />
Controller action:
public ActionResult Index(string name, string surname ,string deviceName, string deviceManufacturer, string Page)
{
bool RoleId = ad.CheckIfAdmin(Request.LogonUserIdentity.Name.Substring(Request.LogonUserIdentity.Name.LastIndexOf(#"\") + 1));
ViewBag.RoleId = RoleId;
var deviceusages = db.DeviceUsages.Include(d => d.DeviceInstance).Include(d => d.Storage).Include(d => d.User).Where(w=>w.UserId!=6).Skip((int.Parse(Page)-1)*30).Take(30);
if(name!="" && name!=null)
{
deviceusages = deviceusages.Where(w => w.User.Name.Contains(name));
}
if (surname != "" && surname != null)
{
deviceusages = deviceusages.Where(w => w.User.Surname.Contains(surname));
}
if (deviceName != "" && deviceName != null)
{
deviceusages = deviceusages.Where(w => w.DeviceInstance.Device.Name.Contains(deviceName));
}
if (deviceManufacturer!= "" && deviceManufacturer != null)
{
deviceusages = deviceusages.Where(w => w.DeviceInstance.Device.Manufacturer.Contains(deviceManufacturer));
}
return View(deviceusages.ToList());
}
After writing something into input field and pressing filter. Ajax should refresh ShowPartial and keep values in the input fields from SerchTab but instead I get filtered records and inputs are getting empty. Can anyone suggest me edits to change this behaviour
If you are intending to
return View()
you should also return the original model so that control values can be populated with your captured values.
Alternatively if you just need to return the list:
deviceusages.ToList()
Then you can return a PartialView() e.g.;
return PartialView(deviceusages.ToList());
Ok I found cause of a problem.
I need to change this code
<div class="input-group">
<div class="input-group-addon name">
User name:
</div>
<input type="text" name="name" class="form-control" />
</div>
into same thing made wit use of .net Html helpers.
After this change everything works!
#Html.Label("User name: ", new { #class = "input-group-addon" })
#Html.TextBox("name", null, new { #type = "text", #class = "form-control" })

Categories