ASP.NET Cross session list - html

I'm attempting to make a simple page that will compare multiple form submissions.
I have a html page with a form, and a for-loop that generates a div for each item in a list of form submissions. The list is passed from the controller. I am trying to maintain the list in the controller rather than rely on a database.
When I try to resubmit the form, which should add another object to the list, the list re initializes.
In debugging, I see that the list is empty when the form gets submitted. I'm unsure as to the correct terminology, but it seems that the list is emptied whenever the view is rendered. Is there a way to maintain list contents?
I know there are better ways to do this, and welcome any advice. I'm still learning, so pleas go easy.
Thanks!
This is the simplified controller.
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
List<paymentPlan> plansList = new List<paymentPlan>();
public ActionResult Index()
{
return View(plansList);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
plansList.Add(Project);
return View(plansList);
}
}
}
This is my simplified view.
#model List<MvcApplication2.Models.paymentPlan>
#using (Html.BeginForm("index", "home", FormMethod.Post, new { Id = "signupForm" }))
{
<label for="customerName">Customer Name:</label>
<input type="text" name="customerName" class="form-control required" />
#Html.ValidationSummary(true)
<input type="submit" value="Calculate" class="btn btn-primary" />
}
#{
bool isEmpty = !Model.Any();
if (!isEmpty)
{
foreach (var i in Model)
{
<div>
Name: #i.customerName
</div>
}
}
}
This is my simplified model.
namespace MvcApplication2.Models
{
public class paymentPlan
{
public string customerName { get; set; }
}
}

I think that's a question of controller and asp.Net MVC lifecycle !
A controller lifetime is the same as the request, for each request a new controller is created and once the work is done it's disposed!
So try to remove this List<paymentPlan> plansList = new List<paymentPlan>(); and work with TempData[] or ViewData[] or Session[] like this :
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
Session["plansList"] = ((List<paymentPlan>)Session["plansList"])!=null? (List<paymentPlan>)Session["plansList"] : new List<paymentPlan>();
return View((List<paymentPlan>)Session["plansList"]);
}
[HttpPost]
public ActionResult Index(FormCollection collection)
{
paymentPlan Project = new paymentPlan();
Project.customerName = Convert.ToString(collection["customerName"]);
((List<paymentPlan>)Session["plansList"]).Add(Project);
return View(plansList);
}
}
check this : http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application

Related

Passing wrong model into partial

I am building my first web app with .net core razor pages.
I am trying to move my menu to be partial, so I can reuse it as I wish but I must be doing something wrong.
The error is:
The model item passed into the ViewDataDictionary is of type 'BrambiShop.UI.Pages.IndexModel', but this ViewDataDictionary instance requires a model item of type 'BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel'.
I know there are few topics on that, but I am struggling " days after work now and I couldnt find any exact solution. I quess I kinda understand what I am doing wrong. But I dont know how to do it right.
I am trying to load the partial from index as follows:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
#{ await Html.RenderPartialAsync("_SideCategoriesMenu"); }
The partial is:
#page
#model BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel
#*
For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*#
#foreach (var category in Model.Categories)
{
<button class="font-weight-bold" data-toggle="collapse" href="#MenuCollapse_#Html.DisplayFor(modelItem => category.Id)"
aria-expanded="false" aria-controls="MenuCollapse_#Html.DisplayFor(modelItem => category.Id)">
#Html.DisplayFor(modelItem => category.Name)
</button>
<!-- Collapsible element -->
<div class="collapse text-center" id="MenuCollapse_#Html.DisplayFor(modelItem => category.Id)">
#foreach (var subCategory in Model.SubCategories.Where(x => x.CategoryId == category.Id))
{
<form action="/url/to/action" Method="GET">
<input type="hidden" name="Property" value="#Html.DisplayFor(modelItem => subCategory.Id)" />
<button type="submit">
<i class="fas fa-caret-right pl-1"></i>#Html.DisplayFor(modelItem => subCategory.Name)
</button>
</form>
}
</div>
<!-- / Collapsible element -->
}
And at last the cshtml.cs
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages.Shared
{
public class _SideCategoriesMenuModel : PageModel
{
private readonly IApiClient _Client;
public _SideCategoriesMenuModel(IApiClient client)
{
_Client = client;
}
public IList<Category> Categories { get; set; }
public IList<SubCategory> SubCategories { get; set; }
public async Task OnGet()
{
Categories = await _Client.GetCategoriesAsync();
SubCategories = await _Client.GetSubCategoriesAsync();
}
}
}
Can someone help me with that please? I did watch about 10 hours of tutorials to build api and the ground of the web, but this damn partial. I just cant find answer to that anywhere.
Thank you very much for any effort.
EDIT
--- index model
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages
{
public class IndexModel : PageModel
{
public IndexModel()
{
}
public async Task OnGet()
{
}
}
}
Your Index view has a model of type IndexModel. When you put this line
#{ await Html.RenderPartialAsync("_SideCategoriesMenu"); }
The same model will be passed to _SideCategoriesMenu which is IndexModel. This is why you are receiving the exception because the model passed to the _SideCategoriesMenu is different since it expects BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel.
To go around that, you have to specify the model in the RenderPartialAsync method. ex:
#{ await Html.RenderPartialAsync("_SideCategoriesMenu",YOUR_MODEL_HERE); }
You can replace YOUR_MODEL_HERE with whatever instance of type _SideCategoriesMenuModel. You can have it as a property inside IndexModel. ex:
public class IndexModel : PageModel
{
public IndexModel()
{
}
public SideMenuModel SideMenu { get; set;}
public BrambiShop.UI.Pages.Shared._SideCategoriesMenuModel SideMenuModel{ get; set;}
public async Task OnGet()
{
}
}
}
And then use it in your index view
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
#{ await Html.RenderPartialAsync("_SideCategoriesMenu",Model.SideMenuModel); }
The model you use to create the page needs to contain a complete model for the Menu. Then you need to pass the model in the RenderPartial.
IE:
#{ await Html.RenderPartialAsync("_SideCategoriesMenu", Model.SideMenu); }
Edit--
Add the SideMenu property to your Model so you can access it on the page in the above example.
using BrambiShop.API.Models;
using BrambiShop.UI.Services;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BrambiShop.UI.Pages
{
public class IndexModel : PageModel
{
public IndexModel()
{
}
public SideMenuModel SideMenu { get; set;}
public async Task OnGet()
{
}
}
}

Use SQL Query for Dropdown - ASP.NET Core 2.0; RazorPages; VS 2017; Entity Framework Core

I currently have an ASP.NET Core 2.0 Web Application (built in VS 2017 from the default web application template). I have a RazorPage with a dropdown built in that I would like to populate via a SQL Query. I've used Entity Framework Core to build a model of my database as follows:
public partial class INVOPEContext : DbContext
{
public virtual DbSet<PmeFundData> PmeFundData { get; set; }
modelBuilder.HasAnnotation"Relational:DefaultSchema", "Server\\User");
modelBuilder.Entity<PmeFundData>(entity =>
{
entity.ToTable("PME_FUND_DATA", "dbo");
entity.Property(e => e.Firm).HasMaxLength(255);
});
}
public partial class PmeFundData
{
public string Firm { get; set; }
}
I've updated the RazorPage PageModel (pmepe.cshtml.cs) to include the DBContext and Query:
public class pmepeModel : PageModel
{
private readonly INVOPEContext _db;
public pmepeModel(INVOPEContext db)
{
_db = db;
}
public IActionResult dropdowns()
{
List<PmeFundData> firmList = new List<PmeFundData>();
firmList = (from Firm in _db.PmeFundData
select Firm).Distinct().ToList();
firmList.Insert(0, new PmeFundData {Firm = "All Firms" });
ViewBag.ListofFirms = firmList;
return View();
}
}
Finally, the view with the dropdown (pmepe.cshtml) is as follows:
#page
#model pmepeModel
#{
ViewData["Title"] = "pmepe";
}
<select asp-for="dropdowns"
id="firm"
class="dropdown"
asp-items= "#(new SelectList(ViewBag.ListofFirms, "Firm"))">
</select>
I am getting errors that neither ViewBag nor View exist in the current context in the PageModel (no errors in the view - Intellisense picks it up). Every example I've found on the web is for MVC rather than RazorPages. The solution commonly provided for MVC is imbedding the query in a Controller and adjusting the MVC version in the web.config file. But the RazorPages template doesn't have Controllers and I can't find a web.config file - so I haven't been able to get it to work in my app. Any help you can provide would be most appreciated.
There are multiple issues in your code.
PmeFundData should have Id property, otherwise, you will receive error while running update-database command.
public partial class PmeFundData
{
public int Id { get; set; }
public string Firm { get; set; }
}
ViewBag is not supported in RazorPage, you could trace this issue from Add ViewBag to PageModel #6754, you could try ViewData or PageModel Property to bind the object.
Here is a simple code for ViewData.
public class PmepeModelModel : PageModel
{
private readonly CoreRazor2_1.Data.ApplicationDbContext _context;
public PmepeModelModel(CoreRazor2_1.Data.ApplicationDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public int SelectedFirm { get; set; }
[ViewData]
public IList<PmeFundData> ListofFirms { get {
return Dropdowns();
}
}
public IList<PmeFundData> Dropdowns()
{
List<PmeFundData> firmList = new List<PmeFundData>();
firmList = new List<PmeFundData> {
new PmeFundData{ Id = 1, Firm = "F1"},
new PmeFundData{ Id = 2, Firm = "F3"},
new PmeFundData{ Id = 3, Firm = "F2"}
};
//firmList = (from Firm in _context.PmeFundData
// select Firm).Distinct().ToList();
firmList.Insert(0, new PmeFundData { Firm = "All Firms" });
return firmList;
//ViewData["ListofFirms"] = firmList;
}
public async Task<IActionResult> OnPostAsync()
{
var value = SelectedFirm;
if (!ModelState.IsValid)
{
return Page();
}
_context.PmeFundData.Add(PmeFundData);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
View
#page
#model CoreRazor2_1.Pages.PmepeModelModel
#{
ViewData["Title"] = "PmepeModel";
}
<h2>PmepeModel</h2>
<h4>PmeFundData</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<select asp-for="#Model.SelectedFirm"
class="dropdown"
asp-items="#(new SelectList((IEnumerable<PmeFundData>)#ViewData["ListofFirms"], "Id" ,"Firm"))">
</select>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
You may also learn Razor Page from Introduction to Razor Pages in ASP.NET Core

MVC5 Post data from partial view being used in main view

I have already tried many solutions available on web as per my understanding in context to this question but being new in MVC I am still unable to find a solution. Kindly help.
I have a view which is the home page of the website named as "Index.cshtml" and is situated under the following path:
WebsiteName/Areas/Website/Views/CypressHome/Index.cshtml
Secondly, I have a created a user trial form as partial view named as "_PartialHomeFormFreeTrial.cshtml" which is situated under the following path:
WebsiteName/Areas/Website/Shared/_PartialHomeFormFreeTrial.cshtml.
This form I have used inside my "Index.cshtml" as below:
<!--freetrialform-->
#Html.Partial("_PartialHomeFormFreeTrial")
<!--//freetrialform-->
Now, my partial page is posting data comprising of following elements:
#using (Html.BeginForm("Create", "Customer", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div>
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "input__field input__field--kuro" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
............
other fields such as email, phone, date, etc..
<input type="submit" id="" value="SEND REQUEST" />
</div>
}
Now, I have created a controller named "CustomerController" which has the following code to save the data of the form as used as partial view in the main view "Index.cshtml":
public class CustomerController : Controller
{
private WebsiteContext db = new WebsiteContext();
// GET: Website/Customer
public ActionResult Index()
{
return View();
}
// GET: Website/Customer/Create
public ActionResult Create()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View("Index");
}
// POST: Website/Customer/Create
[HttpPost]
public ActionResult Create([Bind(Include = "CustomerID,CustomerName,CustomerEmail,CustomerPhone,DateOfRegistration,StatusPlanID")] Customer customer)
{
if (ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status", customer.StatusPlanID);
return View(customer);
}
}
I have tried many changes in my controller, return in the views and
many other things but I am getting the same error always. Neither the
validations are working nor the validated data is getting saved.
The error is as below:
Server Error in '/' Application.
The view 'Create' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/Website/Views/Customer/Create.aspx
~/Areas/Website/Views/Customer/Create.ascx
~/Areas/Website/Views/Shared/Create.aspx
~/Areas/Website/Views/Shared/Create.ascx
~/Views/Customer/Create.aspx
~/Views/Customer/Create.ascx
~/Views/Shared/Create.aspx
~/Views/Shared/Create.ascx
~/Areas/Website/Views/Customer/Create.cshtml
~/Areas/Website/Views/Customer/Create.vbhtml
~/Areas/Website/Views/Shared/Create.cshtml
~/Areas/Website/Views/Shared/Create.vbhtml
~/Views/Customer/Create.cshtml
~/Views/Customer/Create.vbhtml
~/Views/Shared/Create.cshtml
~/Views/Shared/Create.vbhtml
And the url is changing as below:
1. On running the system initially: http://localhost:53872/
2. On clicking on submit: http://localhost:53872/Areas/Website/Customer/Create along with the
error as stated above.
For more information my WebsiteAreaRegistration.cs file contains the below code:
public class WebsiteAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Website";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Website_home",
"",
new { controller = "CypressHome", action = "Index", id = UrlParameter.Optional }
);
context.MapRoute(
"Website_default",
"Areas/Website/{controller}/{action}/{id}",
new { controller = "CypressHome", action = "Index", id = UrlParameter.Optional }
);
}
}
Though I have understood the problem but unable to figure out. Kindly help.
In your code last return statement is return View(customer). That's means after POST data it return a View (using HTTP GET method) as same name as Action that's Create. But your description you have a Create Action but you have no view page.
So please create a Create.cshtml with a Model corresponding customer Object.
Or change the return statement.
Based on your comment you can follow this Change.
1.Remove
public ActionResult Create()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View("Index");
}
2.then change
public ActionResult Index()
{
ViewBag.StatusPlanID = new SelectList(db.StatusPlans, "StatusPlanID", "Status");
return View(new Customer());
}
3.in Index.cshtml
#Model Customer
#Html.Partial("_PartialHomeFormFreeTrial",Model)
4.Then
return View("Index",customer);

How to pass an array from main view to partial view in asp.net mvc 5

I've a main view that contains an array received from the corresponding Action and it also contains a partial view reference below
Create.cshtml :
#model HrAndPayrollSystem.Models.EmployeeMasterA
#using (Html.BeginForm())
{
ViewData["fs_lbls"] = ViewBag.FS_lbls as string[];
#Html.Partial("~/Views/EmployeeMasterA/FinalSettlementTAB.cshtml", Model)
}
and the referenced partial view above is defined below
FinalSettlementTAB.cshtml :
#model HrAndPayrollSystem.Models.EmployeeMasterA
#Html.DropDownList("DeptId", null, "Department")
/* Print "ViewData["fs_lbls"]" array defined in the Main View `Create.cshtml` here */
I've an array defined in the Create.cshtml, now, I want to pass it into the partial view HR_EmployeeFinalSettlementTAB.cshtml and print it, What is the proper way to to this?
What I've tried :
I changed the #Html.Partial() line into below :
#Html.Partial("~/Views/EmployeeMasterA/FinalSettlementTAB.cshtml", null, new ViewDataDictionary { { "fs_lbls", ViewData["fs_lbls"] } })
and modified the FinalSettlementTAB.cshtml file as below :
#model HrAndPayrollSystem.Models.EmployeeMasterA
#Html.DropDownList("DeptId", null, "Department")
#foreach (var i in ViewData["fs_lbls"] as string[])
{
#i
}
But it throws an exception InvalidOperationException at line #Html.DropDownList("DeptId", null, "Department") by saying :
There is no ViewData item of type 'IEnumerable' that has the key 'DeptId'.
It throws the above exception whenever I try to pass the array data to the partial view using ViewDataDictionary, otherwise, it is working fine, when I'm not.
How do I get rid of the above exception and properly pass array data from main view to the partial view?
I suggest that you add a new property to EmployeeMasterA to store the labels, so that you do not need to use ViewData at all.
public class EmployeeMasterA
{
public string[] fs_lbls { get; set; }
public string SelectedLabel { get; set; }
public List<SelectListItem> Labels
{
get
{
if (this.fs_lbls == null)
{
return Enumerable.Empty<SelectListItem>().ToList();
}
return (from label in fs_lbls
select new SelectListItem
{
Text = label,
Value = label
}).ToList();
}
}
}
Create.cshtml
#model WebApplication1.Controllers.EmployeeMasterA
#using (Html.BeginForm())
{
#Html.Partial("FinalSettlementTAB", Model)
<input type="submit" value="Save"/>
}
FinalSettlementTAB.cshtml
#model WebApplication1.Controllers.EmployeeMasterA
#Html.DropDownList("SelectedLabel", Model.Labels)
Controller
public ActionResult Create()
{
var viewModel = new EmployeeMasterA();
viewModel.fs_lbls = new[] {"Label1", "label 2"};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(EmployeeMasterA viewModel)
{
return View();
}
You can set the content of fs_lbls in the controller action method, before returning the Create view. When you post the form, the SelectedLabel property will contain the selected item from the dropdown list. Obviously you will need to change the property names to suite your needs, but hopefully this will give you an idea.

How to use pagedlistpager when passing a viewmodel to my action

What do I use for my second parameter to '#Html.PagedListPager'?
If I have a action in my controller that accepts a viewmodel like this
[HttpPost]
[AllowAnonymous]
public ActionResult Search(HomePageViewModel viewModel)
{
var pagedList = repository.GetYogaList(viewModel.SearchQuery, viewmodel.Date)
viewModel.YogaList = pagedList;
if (Request.IsAjaxRequest())
{
return PartialView("_mySpaces", viewModel);
}
return View(viewModel);
}
and a partial page containing the paged list html helper
here is the partial '_mySpaces.html'
#model HomePageViewModel
<div id="yogaSpaceList">
<div class="pagedList">
#Html.PagedListPager(Model.YogaSpaces, page => Url.Action("Search", new { Model }), PagedListRenderOptions.MinimalWithItemCountText)
</div>
#foreach (var space in Model.YogaSpaces) {
<div>
<h4>#space.Overview.Title</h4>
<div>
#space.Overview.Summary
</div>
</div>
}
</div>
Ok you can pass a viewmodel back to an action but as previously state it needs to be a GET method so mark your action with the HttpGet attribute. The MVC framework will translate/bind the query string to your view model.
Your controller should look something like this:
[HttpGet]
public ActionResult Search(ViewModel viewModel)
{
\\ do something useful here and return ActionResult .....
}
You will need to add a page property to your ViewModel and a method allows your .cshtml code to set the page number and returns the viewmodel as an object. MVC will translate the object into a query string for the action link.
Public class ViewModel {
// other properties and stuff
public int? Page { get; set; }
public object ToPagedListParameters(int page)
{
this.Page = page;
return this;
}
}
Then all that is need is a small adjustment to your .cshtml file
<div class="pagedList">
#Html.PagedListPager(viewModel, page => Url.Action("Search",viewModel.ToPagedListParameters(page))
</div>
Note: I have only got this working with simple viewModels and have not tried it with viewModels that contain collections or lists.