multiple ajax forms in the same mvc 4 view doesn't work properly - after the second time of submit my model is empty - asp.net-mvc-4

I've got a class Cart with property Lines which returns me a collection of type CartLine. here is my two classes Cart ad CartLine
public class Cart
{
private List<CartLine> lineCollection = new List<CartLine>();
public String Currency { get; set; }
public void UpdateCart(int ArticleID, string selectedQuantityType, int Quantity)
{
CartLine line = lineCollection.Where(p => p.Product.ArticleID == ArticleID).FirstOrDefault();
if (line != null)
{
line.SelectedQuantityType = selectedQuantityType;
line.Quantity = Quantity;
}
}
public IEnumerable<CartLine> Lines
{
get { return lineCollection; }
}
}
<
And here is my class CartLine
public class CartLine
{
public CartLine()
{
this.QuantityType= new List<QuantityType>();
}
public WebServiceBeaMenu Product { get; set; }
public int Quantity { get; set; }
public string Currency { get; set; }
public string SelectedQuantityType { get; set; }
}
I've got two actions which returns me strongly typed PartialView - CartIndexViewModel with two properties - one from Type Cart
public ActionResult CartPartial(string returnUrl)
{
return PartialView(new CartIndexViewModel
{
Cart = GetCart(),
ReturnUrl = returnUrl
});
}
[HttpPost]
public ActionResult CartPartial(CartLine line)
{
Cart crt = GetCart();
crt.UpdateCart(line.Product.ArticleID, line.SelectedQuantityType, line.Quantity);
return PartialView(new CartIndexViewModel
{
Cart = crt
});
}
private Cart GetCart()
{
Cart cart = (Cart)Session["Cart"];
if (cart == null)
{
cart = new Cart();
Session["Cart"] = cart;
}
return cart;
}
ANd here is my view whih loop through all thre cartlines of my cart and display their quantity and quantityType.
The problem is as you see for each CartLine I'm making an ajax form so when there is a change in the quantity of a CartLine (when there is a change in this helper #Html.TextBoxFor(x => line.Quantity,new { id = #liine.Product.ArticleID, onchange="SubmitForm(this.id)" })) you see that I submit the form to the httppost form of my CartPartial Action. The first time when I submit the form there is no problem and thanks to mvc model binding my CartLine parameter is filled with the data I need. The problem is when this action returns me the same view with the updated data after that if I try to change a quantity and submit the form for the second time my CartLine parameter is empty. What may be the reason. Thankls in advance.
#model MvcBeaWeb.Models.CartIndexViewModel
<div id="cartContainer">
<table width="100%" align="center" style="margin:auto;border-collapse:collapse;">
<tbody>
#foreach(var line in Model.Cart.Lines)
{
<tr style="height:90px" >
#using (Ajax.BeginForm("CartPartial", null, new AjaxOptions { UpdateTargetId = "centerCartBody", InsertionMode = InsertionMode.Replace }, new { id = "form" + #line.Product.ArticleID }))
{
<td align="left">#line.Product.SpecialWord</td>
<td >
<div class="inner-content">
#Html.TextBoxFor(x => line.Quantity,new { id = #liine.Product.ArticleID, onchange="SubmitForm(this.id)" })
<span class="qty-type">
#Html.DropDownListFor(x => line.SelectedQuantityType, new SelectList(line.QuantityType, "QuantityID", "Description"))
</span>
</div>
</td>
#Html.HiddenFor(x => line.Product.ArticleID, "Value");
}
</tr>
}
</tbody>
</table>
<div >
#using (Html.BeginForm("Index/","Confirmation"))
{
<input type="submit" id="btnCheckOut" />
}
</div>
</div>
</div>
</div>
script>
function SubmitForm(id)
{
$("#form_" + id).submit();
}
</script>

Related

Where i must get Ajax Helper or how to create it to use #AjaxExtensions.ActionLink

I want to use #AjaxExtensions.ActionLink(AjaxHelper, String, String, String, RouteValueDictionary, AjaxOptions) in cshtml file (ASP.Net 6 MVC).
#model List<Shift>
#using System.Web.Mvc.Ajax;
#{
AjaxOptions deleteOptions = new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "table"
};
}
#foreach (Shift shift in Model)
{<td>
#AjaxExtensions.ActionLink( ???,"Delete", "DeleteShift", "MainDoctor", new{id=shift.Id}, deleteOptions)
</td>
}
As this document said, AjaxExtensions.ActionLink is only applies to ASP.NET MVC 5.2, and the correct usage should be:
#Ajax.ActionLink("Delete", "DeleteShift", "MainDoctor", new { id = shift.Id }, deleteOptions)
In ASP.NET 6, you need use an alternative way like below:
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#table" data-ajax-url="#Url.Action("DeleteShift", "MainDoctor", new { id = shift.Id})">Delete</a>
A simple working demo you could follow:
Model:
public class Test
{
public string Id{ get; set; }
}
View(Views/Home/Index.cshtml):
#model Test
<div id="table">
#Model.Id
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#table" data-ajax-url="#Url.Action("DeleteShift", "MainDoctor", new { id = 1 })">Delete</a>
</div>
#section Scripts
{
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js" integrity="sha256-v2nySZafnswY87um3ymbg7p9f766IQspC5oqaqZVX2c=" crossorigin="anonymous"></script>
}
Controller:
public class MainDoctorController:Controller
{
public IActionResult DeleteShift(int id)
{
var model = new Test()
{
Id = "bb"
};
return PartialView("/Views/Home/Index.cshtml", model);
}
}
public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
var model = new Test()
{
Id = "aaa"
};
return View(model);
}
}
Result:

SelectList not returning the value of text only returns ID

I am quite new to asp net core and am trying to implement a select list while passing values from view to controller. All else is working fine only problem I am facing is only the ID is being passed to controller and not the text/name.
Can someone tell me where I am going wrong? Below is my code.
View Snippet
<div class="form-group">
<label>Financial Year</label>
<select asp-for="FinancialYear" asp-items="ViewBag.FinancialYear" class="selectpicker" data-dropup-auto="false" data-size="5">
</select>
</div>
Model Snippet
public class VMOM
{
public int FinancialYear { get; set; }
}
public class VMDropDown
{
public int ID { get; set; }
public string Text { get; set; }
}
Controller Snippet
[HttpGet]
public IActionResult Create()
{
VMOM vmOM = new VMOM();
ViewBag.FinancialYear = new SelectList(GetFinancialYearList(), "ID", "Text", 0).ToList();
return View(vmOM);
}
[HttpPost]
public IActionResult Create(VMOM vmOM)
{
return View(vmOM);
}
private List<VMDropDown> GetFinancialYearList()
{
List<VMDropDown> vmDropdowns = new List<VMDropDown>
{
new VMDropDown() { ID = 1, Text = "2019" },
new VMDropDown() { ID = 2, Text = "2020" }
};
return vmDropdowns;
}
A SS of the values received in action method; note that in Financial Year only the ID of the year is being diplayed and not the text value i.e, 2020
If you don't mind a little bit of javascript you can easily achieve what you want.
We add a hidden input field where its value is updated on the select change.
So when we submit the form, the hidden input's value will be submitted and binded with our model (See screenshot below).
Razor:
<form asp-action="Post" method="post">
<select class="form-control" asp-items="#ViewBag.List" asp-for="#Model.Id" id="FYear">
</select>
<input type="hidden" id="FYearText" asp-for="#Model.Year" readonly="readonly" hidden/>
<button type="submit" class="btn btn-success">Submit</button>
</form>
Model
public class VMOM
{
public int Id { get; set; }
public string Year { get; set; }
}
Controller:
[HttpGet]
public IActionResult Index()
{
var data = new List<VMOM> {
new VMOM { Id = 1, Year = "2018" },
new VMOM { Id = 2, Year = "2019" },
new VMOM { Id = 3, Year = "2020" },
new VMOM { Id = 4, Year = "2077" }
};
ViewBag.List = new SelectList(data, "Id", "Year");
return View("Index", new VMOM());
}
JS
$(document).ready(function(){
$("#FYear").on("change", function(){
$("#FYearText").val($(this).find("option:selected").text());
});
});
Result:
P.S, I am using jQuery in this example for brevity.
The simplest way is changing the ID value the same as the Text.
List<VMDropDown> vmDropdowns = new List<VMDropDown>
{
new VMDropDown() { ID = 2019, Text = "2019" },
new VMDropDown() { ID = 2020, Text = "2020" }
};

Asp.net Core how to use ReflectionIT.Mvc.Paging with ViewModel?

I want to use paging with a ViewModel in Asp.net Core 2.2.
You can see my code below
public class UserQuestionListComplexViewModel
{
//There are 2 ViewModel in this Class
public UserPanelViewModel Model1 { get; set; }
public List<UserQuestionListViewModel> Model2 { get; set; }
}
And in my Controller
public class UserHomeController : Controller
{
private readonly UserManager<ApplicationUsers> _userManager;
private readonly IQuestionRepository _iq;
public UserHomeController(UserManager<ApplicationUsers> userManager,
IQuestionRepository iq)
{
_userManager = userManager;
_iq = iq;
}
[HttpGet]
public async Task<IActionResult> QuestionList(UserQuestionListComplexViewModel model,
int page = 1)
{
var query = _iq.UserQuestionList(_userManager.GetUserId(HttpContext.User), page);
model.UQVM = await query;
return View(model);
}
}
And below is QuestionRepository
public async Task<List<UserQuestionListViewModel>> UserQuestionList(string UserID,
int page = 1)
{
var questionQuery = (from q in _db.QuestionTbl
where q.UserID == UserID
select new UserQuestionListViewModel()
{
....
})
.AsNoTracking()
.Where(q => q.qflag == 0)
.OrderBy(q => q.QuestionID);
var pagedResult = await PagingList<UserQuestionListViewModel>.CreateAsync(
questionQuery, 1, page);
return pagedResult;
}
At the end View.cshtml
#model UserQuestionListComplexViewModel
#using ReflectionIT.Mvc.Paging
#await Component.InvokeAsync("UserInfo", Model.Model1)
<div>
<table>
<thead class="thead-dark">
<tr>
<td>...</td>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Model2)
{
<tr>
<td>...</td>
</tr>
}
</tbody>
</table>
<nav class="pagenav">
#await this.Component.InvokeAsync("Pager", new { PagingList = this.Model })
</nav>
</div>
But i get below error
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'ReflectionIT.Mvc.Paging.PagingList`1[porseman.Models.ViewModels.UserQuestionListViewModel]', but this ViewDataDictionary instance requires a model item of type 'porseman.Areas.UserPanel.Models.UserComplexViewModel.UserQuestionListComplexViewModel'.
Look at your PagingList creation function ,the type is UserQuestionListViewModel:
var pagedResult = await PagingList<UserQuestionListViewModel>.CreateAsync(questionQuery, 1, page);
But when you config the PagingList in view , you are setting type UserQuestionListComplexViewModel , so replace this line :
#await this.Component.InvokeAsync("Pager", new { PagingList = this.Model })
With :
#await this.Component.InvokeAsync("Pager", new { PagingList = this.Model.Model2 })
Also , you might need to change the type Model2 to PagingList<UserQuestionListViewModel> in your view model :
public PagingList<UserQuestionListViewModel> Model2 { get; set; }

MVC : System.NullReferenceException model when submit form

I've been trying to pass a model to a partial view with a form. Some of the model fields are already assigned in the GET request. When the form loads I can see the model fields values but after
submiting the form I get this error in this line: #Html.Hidden("From",Model.From):
Object reference not set to an instance of an object
Why these two fields are assigned with null on submit?
My controllers:
[HttpGet]
public ActionResult SendPrivateMessage(string from, List<string> to)
{
// two of the fields are already assigned
return PartialView("SendMessage", new MessageModel(from,to));
}
[HttpPost]
public ActionResult SendPrivateMessage(MessageModel m)
{
string fullname = "";
LoginModel loginData = (LoginModel)(Session["user"]);
if (Session["user"] != null)
{
fullname = loginData.LoginDS.Tables[0].Rows[0][loginData.LoginDS.Tables[0].Columns["fullname"].Ordinal].ToString();
}
m.fullname = fullname;
m.Send();
return PartialView("SendMessage");
}
The partial view:
#model HaifanetMobile.Models.MessageModel
<div id="contact_form">
<a id="back_contact" href="#" style="float:left">
<img style="height:20px; width:30px;" src="~/Images/back_btn.gif" alt="back" />.
</a>
<div id="contactus_title">
<div id="close_contactus" style="float:right"><img style="height:20px; width:20px;" src="~/Images/close_btn.gif" /></div>
</div>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<br />
<fieldset>
#Html.Hidden("From", Model.From) //this is where I get the error
#Html.Hidden("To", Model.To)//this is where I get the error
<div>
#Html.TextBoxFor(m => m.Subject, new { #class = "", placeholder = "subject:", id = "msg_subject", onfocus = "this.placeholder = ''", onblur = "this.placeholder = 'subject:'" })
#Html.ValidationMessageFor(m => m.Subject, "required")
</div>
<div>
#Html.TextAreaFor(m => m.Content, new { #class = "", id = "msg_textarea" })
#Html.ValidationMessageFor(m => m.Content, "required")
</div>
</fieldset>
<p>
<input type="submit" value="send" />
</p>
}
</div>
The Model:
public class MessageModel
{
public string From { get; set; }
public List<string> To { get; set; }
public string Subject {get; set;}
public string Content { get; set; }
public string fullname { get; set; }
public MessageModel(string from, List<string> to)
{
// TODO: Complete member initialization
this.From = from;
this.To = to; ;
}
public MessageModel() {
}
public void Send()
{
ServiceReference2.WebService1Soap ws = new ServiceReference2.WebService1SoapClient();
if (!ws.SendMessage(this.From, this.Content, this.Subject, this.To.ToArray() ,this.fullname))
throw new Exception();
}
}
Thanks in advance
You're forgetting to pass the model to your view.
When you return this view, instead of this:
return PartialView("SendMessage");
you must do this:
return PartialView("SendMessage", m);
Where m is your model. That's why the model is null inside your view.

MVC 4 multiple buttons in form - why isn't this code working

I am trying to use a variation of the code from this page:
Multiple button in MVC
But everytime I click on the buttons it goes to the index actionresult method and not one of the button methods. Index is the view name but the button clicks are happening in a partial view called "P_NewPersonForm.cshtml"
P_NewPersonForm.cshtml
#using (Html.BeginForm())
{
<div id="divClaimType">
#Html.Label("Claim type:")
#Html.DropDownListFor(m => m.modelClaim.ClaimType, new List<SelectListItem>
{
new SelectListItem{ Text="Legal", Value = "Legal" },
new SelectListItem{ Text="Immigration", Value = "Immigration" },
new SelectListItem{ Text="Housing", Value = "Housing" }
})
</div>
<div id="divClaimStatus" style="padding: 5px;">
#foreach(var item in Model.LinkerStatusOfClaim)
{
#Html.Label("Claim status:")
#Html.DropDownListFor(m => m.LinkerStatusOfClaim[0].ClaimStatusID, new SelectList(Model.modelClaimStatus, "ClaimStatusID", "ClaimStatus"))
#Html.LabelFor(m => m.LinkerStatusOfClaim[0].Notes)
#Html.TextAreaFor(m => m.LinkerStatusOfClaim[0].Notes)
#Html.LabelFor(m => m.LinkerStatusOfClaim[0].StartDate)
#Html.TextBoxFor(m => m.LinkerStatusOfClaim[0].StartDate, new { #id = "datepicker", #Value = DateTime.Now, #readonly = true, Style = "background:#cccccc;" })
<br />
#Html.ValidationMessageFor(model => model.LinkerStatusOfClaim[0].StartDate)
<br />
}
<input type="submit" value="Add another status to this claim..." name="action:add"/>
<input type="submit" value="Delete status." name="action:remove"/>
#* #Ajax.ActionLink("Add another status to this claim...", "AddClaim", "Client", new AjaxOptions { HttpMethod = "POST"})*#
</div>
}
</div>
I have one button for adding to the collection of claims and another to remove one from the collection.
ClientController
public ActionResult Index()
{
var Model = new modelPersonClaim();
// Add one item to model collection by default
LinkerStatusOfClaim LinkerStatusOfClaim = new LinkerStatusOfClaim();
Model.LinkerStatusOfClaim.Add(LinkerStatusOfClaim);
DataLayer.RepositoryClient RC = new RepositoryClient();
Model.isValidModel = true;
RC.GetClaimTypes(Model, PersonTypes.NewPerson.ToString());
return View(Model);
}
[HttpPost]
public ActionResult P_NewPersonForm(modelPersonClaim Model)
{
DataLayer.RepositoryClient RC = new RepositoryClient();
RC.GetClaimTypes(Model, PersonTypes.NewPerson.ToString());
Model.isValidModel = ModelState.IsValid;
if (ModelState.IsValid)
{
RC.CreatePerson(Model);
Model.SuccessfulInsert = true;
Model.InsertString = "Person data has been successfully inserted into the database.";
if (Model.modelClaim.ClaimMade)
{
RC.CreateClaim(Model);
}
}
else
{
Model.SuccessfulInsert = false;
Model.InsertString = "Person data could not be inserted into the database. Missing key fields.";
}
return View("Index", Model);
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited = true)]
public class MultiButtonAttribute : ActionNameSelectorAttribute
{
public string Name { get; set; }
public string Argument { get; set; }
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
var isValidName = false;
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
if (value != null)
{
controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
isValidName = true;
}
return isValidName;
}
}
[HttpPost]
[MultiButtonAttribute(Name = "action", Argument = "Add another status to this claim...")]
public ActionResult AddClaimStatus(modelPersonClaim Model)
{
Model.LinkerStatusOfClaim.Insert(Model.LinkerStatusOfClaim.Count, new LinkerStatusOfClaim());
return View("Index", Model);
}
[HttpPost]
[MultiButtonAttribute(Name = "action", Argument = "Delete status.")]
public ActionResult RemoveClaimStatus(modelPersonClaim Model)
{
// Can't remove IF only 1
if (Model.LinkerStatusOfClaim.Count == 1)
{
}
else
{
Model.LinkerStatusOfClaim.RemoveAt(Model.LinkerStatusOfClaim.Count);
}
return View("Index", Model);
}
When I click one the buttons it hits the public override bool IsValidName twice. Once for each button. But then because the action name is always index, it goes to the index method and not one of the button methods.
Does anyone have any ideas how to fix this?
Something is wrong with this part:
var keyValue = string.Format("{0}:{1}", Name, Argument);
var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);
Your attribute is this:
[MultiButtonAttribute(Name = "action", Argument = "Add another status to this claim...")]
So in that case keyValue will become: "action:Add another status to this claim..." while your HTML states: <input type="submit" value="Add another status to this claim..." name="action:add"/>, so I think Argument in your attribute should be add.