Null model with ajax post - c#

I am trying to pass a model to the controller using an ajax post, but every time I do so, the model is null and I lose all the data I am trying to persist.
The ajax call:
$('#next').click(function (e) {
e.preventDefault();
var url = '#Url.Action("Manage")'
var _model = JSON.stringify(#Html.Raw(Json.Encode(Model)));
var _page = #(Model.pager.CurrentPage + 1);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: url,
data: JSON.stringify({ 'model': _model, 'page': _page }),
success: function(result) {
console.log(result);
}
});
});
When I look at the serialized object, it looks like a correctly formatted JSON object with no errors thrown in the developer console.
The link that is triggering this jQuery call is just a basic action link:
#Html.ActionLink(">", "Manage", "Announcements", null, new { id = "next" })
My model is a little more complicated...
public class ManageViewModel : IEnumerable<EditViewModel>
{
[Display(Name="Start Date")]
[DataType(DataType.DateTime)]
public DateTime? StartDate { get; set; }
[Display(Name="End Date")]
[DataType(DataType.DateTime)]
public DateTime? EndDate { get; set; }
public string SearchString { get; set; }
public Pager pager { get; set; }
public List<EditViewModel> Data { get; set; }
public List<CategoryViewModel> Categories { get; set; }
public ManageViewModel()
{
Data = new List<EditViewModel>();
Categories = new List<CategoryViewModel>();
}
public IEnumerator<EditViewModel> GetEnumerator()
{
return Data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
}
public class Pager
{
public int TotalItems { get; private set; }
public int CurrentPage { get; private set; }
public int PageSize { get; private set; }
public int TotalPages { get; private set; }
public int StartPage { get; private set; }
public int EndPage { get; private set; }
public Pager(int totalItems, int? page, int pageSize = 10)
{
int totalPages = (int)Math.Ceiling((decimal)totalItems / (decimal)pageSize);
int currentPage = page ?? 1;
int startPage = currentPage - 5;
int endPage = currentPage + 4;
if(startPage <= 0)
{
endPage -= (startPage - 1);
startPage = 1;
}
if(endPage > totalPages)
{
endPage = totalPages;
if(endPage > 10)
{
startPage = endPage - 9;
}
}
TotalItems = totalItems;
TotalPages = totalPages;
CurrentPage = currentPage;
PageSize = pageSize;
EndPage = endPage;
StartPage = startPage;
}
}
I can't convert the links in to a form because that breaks the pagination. Or maybe I'm just not understanding the full picture here.
Here is the section of the View where the pagination is occurring
if (Model.pager.EndPage > 1)
{
<div style="color:#337AB7; padding-bottom: 0px;">Page #Model.pager.CurrentPage of #Model.pager.TotalPages</div>
<ul class="pagination">
#if (Model.pager.CurrentPage > 1)
{
<li>
#Html.ActionLink("<<", "Manage", new { model = Model, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
<li>
#Html.ActionLink("<", "Manage", new { model = Model, page = Model.pager.CurrentPage - 1, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
#for (var _page = Model.pager.StartPage; _page < Model.pager.EndPage + 1; _page++)
{
<li class="#(_page == Model.pager.CurrentPage ? "active" : "")">
#Html.ActionLink(_page.ToString(), "Manage", new { model = Model, page = _page, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
#if (Model.pager.CurrentPage < Model.pager.TotalPages)
{
<li>
#Html.ActionLink(">", "Manage", "Announcements", null, new { id = "next" })
</li>
<li>
#Html.ActionLink(">>", "Manage", new { model = Model, page = Model.pager.TotalPages, start = Model.StartDate, end = Model.EndDate, query = Model.SearchString }, null)
</li>
}
</ul>
}
The action links attempting to pass the model, obviously don't work but I left them because I am focusing on getting one to work, the one listed previously, before getting all the others configured.
I have looked at the following SO posts and have had no luck with them:
Post an MVC model with AJAX?
Model properties null on Ajax post from list box change event
How to send a model in jQuery $.ajax() post request to MVC controller method
Pass Model To Controller using Jquery/Ajax
How to pass model in ajax post request?
Any ideas on how I might be able to do this? I need the model data to persist for searching/filtering which is done through a form. Thank you in advance for taking a look and giving your insights!

You need to include a parameterless constructor for your models. From the screenshot you have sent it seems there isn't a parameterless constructor for public Pager model.

Related

ASP.NET Core MVC paging binding

I want to add paging to my table in ASP.NET Core MVC.
Issue: how can I link TotalRecords, PageNo, & PageSize from controller to the view? In Razor page its simple to bind them, but I don't know how to do this in MVC.
TotalRecords = Query.AsQueryable().Count();
PageNo = p;
PageSize = s;
Controller:
// Display Data
public async Task<IActionResult> DisplayData(int p = 1, int s = 20)
{
try
{
IQueryable<MyModel> query = await _services.GetData();
// Add Paging
query = await AddPaging(p, s, query);
// Display Data
List<MyModel> myData = await query.AsNoTracking().ToListAsync();
return View(myData); // return view with data
}
catch
{
return View(); // return empty view
}
}
// add paging
private async Task<IQueryable<MyModel>> AddPaging(int p, int s, IQueryable<MyModel> Query)
{
TotalRecords = Query.AsQueryable().Count();
PageNo = p;
PageSize = s;
Query = Query.Skip((p - 1) * s).Take(s);
return Query;
} // end of paging
View:
#model List<MyModel>
<table>
....
</table>
<paging total-records="Model.TotalRecords"
page-no="Model.PageNo"
show-prev-next="false"
show-first-last="false">
</paging>
I suggest to solve this problem with OOP standards.
First make a class that will do pagination.
Example:
public class Paginate<T>
{
public Paginate(List<T> datas, int currentPage, int totalPage)
{
Datas = datas;
CurrentPage = currentPage;
TotalPage = totalPage;
}
public List<T> Datas { get; set; }
public int CurrentPage { get; set; }
public int TotalPage { get; set; }
public bool HasPrev
{
get
{
return CurrentPage > 1;
}
}
public bool HasNext
{
get
{
return CurrentPage < TotalPage;
}
}
}
Then send data to view by this class.
public async Task<IActionResult> Index(int page = 1, int take = 5)
{
var datas = await _context.Datas
.Skip((page - 1) * take)
.Take(take)
.AsNoTracking()
.ToListAsync();
int dataCount = await _context.Datas.CountAsync();
int initCount = (int)Math.Ceiling((decimal)count / take);
Paginate<Categories> result = new Paginate<Datas>(datas , page, initCount );
return View(result);
}
Last step is sending initial datas to controller from view.
First your view will take data from pagination class.
#model Paginate<List<MyModel>>
Then your pagination component will send data to controller:
<ul>
<li class="#(Model.HasPrev?"":"disabled")">
<a asp-action="Index" asp-route-page="#(Model.CurrentPage-1)">Previous</a>
</li>
#for (int i = 1; i <= Model.TotalPage; i++)
{
<li class="#(i==Model.CurrentPage ? "disabled":"")">
<a asp-action="Index" asp-route-page="#i">#i</a>
</li>
}
<li class="#(Model.HasNext?"":"disabled")">
<a asp-action="Index" asp-route-page="#(Model.CurrentPage+1)">Next</a>
</li>
</ul>
If you analize all code I think it will be useful. Or you can take the business logic and apply it to your DisplayData and AddPaging methods.

Is it possible to use link inside DropDownList html helper?

I have a model class named categories that retrieves data from database :
public class HomeController : Controller
{
private StoreContext db;
public HomeController()
{
db = new StoreContext();
}
public ActionResult Categories()
{
return View(db.Categories.ToList());
}
}
I want to use DropDownList helper method to display them in the view and I want all the categories inside it to be clickable, say, when you click them it has to adress you to the specified url belongs to the clicked category. Is there a way to make this happen with DropDownList helper? if yes then how?
You can do this but you have to use Jquery . If you ask how?
For example:
My sample entity
public class Category
{
public int Id { get; set; }
public string Url{ get; set; }
public string Name { get; set; }
}
My action:
public IActionResult Categories()
{
var list = new List<Category>();
for (int i = 0; i < 10; i++)
{
list.Add(new Category(){Id = i, Url = "https://stackoverflow.com", Name = "stackoverflow" });
}
var selectList = list.Select(x => new SelectListItem() {Value = Url, Text = x.Name})
.ToList();
return View(selectList);
}
in my View:
#Html.DropDownList("url",Model, "Choose a URL", new { id = "url_list" })
and then using jquery you could subscribe for the change event of this dropdownlist and navigate to the corresponding url:
$(function() {
$('#url_list').change(function() {
var url = $(this).val();
if (url != null && url != '') {
window.location.href = url;
}
});
});

load one item per page [ Pagination ]

I have a viewpage like below
this is the controller method for above view
public ActionResult Add_Product()
{
var model = new AddNewProduct
{
ListProductFields = db.AB_ProductTypeCategoryField.ToList()
};
return View(model);
}
this is model class for above view
public class AddNewProduct
{
public string Product_ID { get; set; }
public string ProductTypeID { get; set; }
public string ProductCategoryID { get; set; }
public string Subsidary_ID { get; set; }
public IList<AB_ProductTypeCategoryField> ListProductFields { get; set; }
}
public partial class AB_ProductTypeCategoryField
{
public string Field_Value_EN { get; set; }
public string Field_Value_AR { get; set; }
}
this is viewpage
#model albaraka.Models.AddNewProduct
#using (Html.BeginForm())
{
#for (int i = 0; i < Model.ListProductFields.Count; i++)
{
#Html.TextAreaFor(m => m.ListProductFields[i].Field_Value_EN, new { #class = "form-control summernote", #row = 5 })
#Html.TextAreaFor(m => m.ListProductFields[i].Field_Value_AR, new { #class = "form-control summernote", #row = 5 })
}
}
Now I want to add pagination for above view page and limit one ListProductFields per one page , for that I following this Tutorial
So I change my code to like this
public ActionResult Add_Product(int? page)
{
var dummyItems = db.AB_ProductTypeCategoryField.Select(x => "Item " + x);
var pager = new PaginationModel.Pager(dummyItems.Count(), page);
var model = new AddNewProduct
{
Items = dummyItems.Skip((pager.CurrentPage - 1) * pager.PageSize).Take(pager.PageSize).ToList(),
Pager = pager
};
return View(model);
}
But then I'm getting following Run-time Error
Values of type 'AB_ProductTypeCategoryField' can not be converted to string.
Just try with following code , so you cannot use Skip method alone. so have use OrderBy before it
var dummyItems = db.AB_ProductTypeCategoryField;
var pager = new PaginationModel.Pager(dummyItems.Count(), page);
var model = new AddNewProduct
{
ListProductFields = dummyItems.OrderBy(i => i.SomeProperty).Skip((pager.CurrentPage - 1) * pager.PageSize).Take(pager.PageSize).ToList(),
Pager = pager
};
return View(model);
The issue of your observation is below line
var dummyItems = db.AB_ProductTypeCategoryField.Select(x => "Item " + x);
Since you are trying to select object with string concatenate operation ("Item " + x) that's why exception is thrown as AB_ProductTypeCategoryField is an object. I don't think you require "Item " + x at all.
You can change AddNewProdcut action implementation as
public ActionResult Add_Product(int? page)
{
var pager = new PaginationModel.Pager(dummyItems.Count(), page);
var model = new AddNewProduct
{
ListProductFields = db.AB_ProductTypeCategoryField.Skip((pager.CurrentPage - 1) * pager.PageSize).Take(pager.PageSize).ToList(),
Pager = pager
};
return View(model);
}

How do I keep this scenario to 1 round trip?

I have a page that is a list of users. The controller's Index function is the action responsible for showing the page of users. The user can select those users and chose to delete them.
I perform the delete action with an ajax request, however then the page's list of users is out of date. So I reload the page because I want to re-use the index action, and all the query string parameters are still there. This means I'm performing two round trips. How do I avoid this?
function DeleteUsers()
{
var selectedUserIds = ;
$.post("/Account/DeleteUsers",
{
userIds: selectedUserIds
},
function (data) {
if( data.status == "success"){
location.reload();
}
});
}
Index function:
[AuthorizeActionFilter]
public ActionResult Index(UserModel model)
{
ViewData["PageTitle"] = ServiceSite.Resources.Resources.REGISTERED_USERS;
ViewBag.MaxUsersPerPage = PAGE_MAX_COUNT;
if(model == null)
{
model = new UserModel();
}
int totalCount = 0;
//Get users
model.Users = CADC.GetUsers(AccountController.GetRegionID(), model.CompanyID, out totalCount,
model.SortField, model.PageNumber * PAGE_MAX_COUNT, PAGE_MAX_COUNT, model.Ascending, User.Identity.Name);
model.TotalUserCount = totalCount;
int totalPages = totalCount / PAGE_MAX_COUNT;
model.TotalPages = (totalCount % PAGE_MAX_COUNT) == 0 ? totalPages : totalPages + 1;
return View(model);
}
and the model:
public class UserModel
{
public bool Ascending { get; set; }
public int PageNumber { get; set; }
public string SortField { get; set; }
public int TotalUserCount { get; set; }
public int TotalPages { get; set; }
public long CompanyID { get; set; }
public List<User> Users { get; set; }
public UserModel()
{
this.Ascending = true;
this.PageNumber = 0;
this.SortField = "FirstName";
this.CompanyID = 0;
this.TotalUserCount = 0;
this.TotalPages = 0;
}
}
I agree with #David's comments. In the AJAX success block, remove the rows just deleted based on what you requested to delete.
The pagination is indeed more complicated; either adjust it in the same way, or revamp everything to use a proper view model in Javascript with bound elements--see AngularJS or other such frameworks.

ASP.NET MVC 3 - Javascript breaks SelectedValue from DropDownList

I have a ViewModel and a DropDownList with some values on my page:
public class MyViewModel
{
public string SelectedItemDrop1 { get; set; }
public string SelectedItemDrop2 { get; set; }
public string SelectedItemDrop3 { get; set; }
public List<OptionViewModel> Options { get; set; }
}
public class OptionViewModel
{
public string Number { get; set; }
public string Option { get; set; }
}
And, into my View:
#using (Html.BeginForm("Save", "Controller", FormMethod.Post))
{
<ul id="cursos">
<li>
#Html.DropDownListFor(c => c.SelectedItemDrop1,
new SelectList(Model.Options, "Number", "Option", Model.SelectedItemDrop1))
Choose 1
</li>
<li>
#Html.DropDownListFor(c => c.SelectedItemDrop2,
new SelectList(Model.Options, "Number", "Option", Model.SelectedItemDrop2))
Choose 2
</li>
<li>
#Html.DropDownListFor(c => c.SelectedItemDrop3,
new SelectList(Model.Options, "Number", "Option", Model.SelectedItemDrop3))
Choose 3
</li>
</ul>
}
When I use Javascript to change options from these select elements, my return is null. What's the problem? Thank you so much!!!
EDIT:
My javascript code:
$("#cursos li select").each(function (i, item) {
$(this).change(function () {
updateCursos($(this), 7);
});
$(this).blur(function () {
if ($(this).val() != -1) {
$(this).attr('disabled', 'disabled');
}
});
});
function updateCursos(select, avaiableCourses) {
var selecteds = '';
$("#cursos li select").each(function (i, item) {
var selected = $(item).val();
if (selected != -1)
selecteds += selected + ',';
});
var arr = selecteds.split(',');
$("#cursos li select").each(function (i, item) {
if ($(item).val() != select.val()) {
var oldValue = $(item).val();
if ($(item).val() == -1) {
var options = "<option value='-1'></option>";
for (i = 1; i <= avaiableCourses; ++i) {
if (select.val() != i && !contains(i, selecteds)) {
options += "<option value='" + i + "'>" + i + "ª option</option>";
}
}
options += "<option value='0'>Don't want it</option>";
$(item).children("option").remove();
$(item).html(options);
$(item).val(oldValue);
}
}
});
}
This way I'm sure that works, not even being javascript.
If the values ​​are not being filled are coming from the database, with fixed values ​​example: active, inactive, do the following
Create an enumerated type with these values
After the controller transforms this type enumerated in list using an internal class, turn it into a enumerable and go to the dropdownlist
example of usage:
# Html.DropDownListFor (model => model.chamados.UrgenciaId, new SelectList (ViewBag.urgencia, "Id", "Name"))

Categories

Resources