ASP.NET Core - MVC - Model variable in view - null exception - asp.net-core

In an attempt to learn ASP.NET Core MVC I've made a simple project and am trying to pass a model instance created in the controller to the view.
Controller Code - I create a simple list, then pass it to the view, being explicit about which view
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> {"A", "B"};
ViewBag.Title = "Tables";
return View("/Pages/Table.cshtml", modelData);
}
}
View Code
#page
#model List<string>
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
When I set a breakpoint in the Controller the object I pass in as the model parameter is not null:
However, when I step through into the view code I get this:
I've looked at a few other "Model is null" posts but they were due mismatching types between whats passed in the View() model parameter and whats expected in the view given by the #model declaration.
It's probably something really simple but I'm not sure where I've gone wrong?

I had the same exception, the solution was to remove #page in Index.cshtml and then Boom, everything was there. Took me 5-6 hours to "resolve" this exception but better late, than never.

In asp.net MVC,View does not mean Razor Page.You can use a View page,And add a Table folder in Views.And then add a Index.cshtml(Razor View Page)to it.
Here is a demo worked:
Controller(return View() inIndex action will find a Index.cshtml in Views/Table(Views/ControllerName)):
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> { "A", "B" };
ViewBag.Title = "Tables";
return View(modelData);
}
}
View(Don't use #page,it's used in Razor Page):
#model List<string>
#{
ViewData["Title"] = "Table_Index";
}
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
Views folder structure(Each folder means a controller except Shared):
Result:

Related

Set Cookie value when link is clicked in Razor Pages

In a NET Core 7 using Razor Pages I have a HeaderViewComponent:
public class HeaderViewComponent : ViewComponent {
public HeaderViewComponent() { }
public IViewComponentResult Invoke() {
return View();
}
}
Where part of the HTML is:
<ul>
#foreach (String culture in cultures) {
<li>
<a href='#Url.RouteUrl(new RouteValueDictionary { { "culture", #culture } })'>#culture</a>
</li>
}
</ul>
I have this View Component in all my Razor Pages.
When one of the links is clicked I want more than redirect.
I also need to set the value of a cookie named "Culture".
What would be the best way to do this?

How to properly refresh a PartialView in Razer page .Net Core 3.1

I have a simple page that also includes a PartialView so that I can refresh the data every 10 seconds returned by the PartialView. It really is a trivial task but I'm not proficient enough with the whole web part and especially JavaScript.
How can I run the LoadData() Method every X seconds so that my partialView shows the data?
I have found the following JS code which I think should be able to refresh the PartialView but I don't know how to adapt this to my situation:
<script type="text/javascript">
$(function () {
setInterval(loadTable, 1000); // invoke load every second
loadTable(); // load on initial page loaded
});
function loadTable() {
$('#data').load('/controller/tabledata');
}
</script>
My index.cshtml looks like this:
#page
#model IndexModel
<div class="data">
<partial name="~/Pages/Shared/_BezoekersData.cshtml" />
</div>
The indexModel:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.SharePoint.Client;
using PnP.Framework;
using System.Security;
namespace MyNameSpace.Pages
{
public class IndexModel : PageModel
{
public IActionResult OnGetPartial() => Partial("\\Shared\\_BezoekersData.cshtml");
// properties left out for clarity
public void OnGet()
{
LoadData();
}
public void LoadData()
{
// dataRetrieval part here.
}
}
}
_BezoekersData.cshtml:
#{
ViewData["Title"] = "Home page";
}
<div style="position:fixed; left:65px; top:25px; color:#c2a149; font-size:75px; ">#Model.Vestiging</div>
<div style="position:fixed; left:1100px; top:180px; color:#c2a149; font-size:50px">#Model.MaxAantal</div>
<div style="position:fixed; left:1100px; top:285px; color:#c2a149; font-size:50px">#Model.HuidigAantal</div>
You should add one more function to call the partial view and refresh it, check this:
public void OnGet()
{
LoadData();
}
public void LoadData()
{
MyModel=new MyModel {thetime=DateTime.Now };
}
public PartialViewResult OnGetMyPartial() // extra method to get data
{
MyModel = new MyModel { thetime = DateTime.Now };
return Partial("_BezoekersData", MyModel);
}
View:
Mind it must be ** div id="data"**, because you use #data to call jquery function.
Pass model data to partial, add model attribute.
And to call the new method, using handler:
In main view: #Model.MyModel.thetime
<div id="data">
<partial name="~/Pages/Shared/_BezoekersData.cshtml" model="Model.MyModel"/> //add a model attribute
</div>
#section Scripts
{
<script type="text/javascript">
$(function () {
setInterval(loadTable, 1000);
loadTable();
});
function loadTable() {
$('#data').load("/Index?handler=MyPartial"); //handler to call razor method
}
</script>
}
This is the partial view:
#model He621.Models.MyModel
In partial:<div style="position:fixed; left:65px; top:100px; color:#c2a149; font-size:75px; ">#Model.thetime</div>
Result:
The time refreshes every 1 seconds, and will only refresh the partial:

Return a partial view from a razor page Handler

I am having a problem returning a partial view from a razor page, my scenario is
I have a partial view which is a form and that has a model. I have 3 forms residing on a single razor pages
Form A post a ModelA
Form B post ModelB
My problem is, i want to handle thier specific post event on the parent Page which is a razor page.
How would i return this partial view
OnPostModelA(ModelA model)
{
if(! ModelState.IsValid)
return Partialview("_CreateModelA", model);
}
Is this possible using razor pages or this is not possible?
I just want to return the partialview with its designated model using ajax.
As you know ,Razor Pages have no equivalent PartialView method on the PageModel. If you do want to invoke different parial views in PageModel method , simply add a PartialView Helper Method in you PageModel:
[NonAction]
public virtual PartialViewResult PartialView(string viewName, object model)
{
ViewData.Model = model;
return new PartialViewResult()
{
ViewName = viewName,
ViewData = ViewData,
TempData = TempData
};
}
Here I use a ViewData.Model to store your model object , let's say your Model type is named as X1Model :
you can use it across the partial views .
Create a simple partial view named as _CreateModelA.cshtml :
#model HelloModel
AAAAA
<div>
#Model.Model.Welcome
</div>
and another partial view named as _CreateModelB.cshtml :
#model HelloModel
BBBBBBBB
<div>
#Model.Model.Welcome
</div>
At last , you can return PartialView in your PageModel:
public class HelloModel : PageModel
{
public X1Model Model { get; set; }
public ActionResult OnGet(int rand = 0)
{
var flag = rand % 2 == 0 ? true : false;
var model = new HelloModel() {
Model = new X1Model {
Welcome = "Hello,world",
}
};
if (flag)
{
return PartialView("_CreateModelA", model);
}
else
{
return PartialView("_CreateModelB", model);
}
}
[NonAction]
public virtual PartialViewResult PartialView(string viewName, object model)
{
// ...
}
}
Here's a screenshot :
However , it is not recommended to put partial view logic in PageModel . Using it in the Page file as below is much nicer:
#if(){
<partial name="" />
}else{
<partial name="" />
}
In asp dotnet core 2.2, Microsoft added a Partial method to the PageModel class that works similar to the PartialView method on the Controller class. It however doesn't allow you to pass ViewData to the view. So, if you need to do that, then you can create your own PartialViewResult like so:
var resultViewData = new ViewDataDictionary<YourModelType>(ViewData, model);
resultViewData[YourViewDataProperty] = yourViewDataValue;
return new PartialViewResult
{
ViewName = "_Branch",
ViewData = resultViewData,
TempData = TempData
};

ViewBags using #Html.Action to render form on partial view

View:
<p>Parent ViewData: #ViewData["Test"]</p>
#Html.Action("MemberSignup","MemberSignupSurface")
PartialView:
<p>PartialView ViewData: #ViewData["Test"]</p>
#using (Html.BeginUmbracoForm<MemberSignupSurfaceController>
("MemberSignupSubmit", "MemberSignupSurfaceController",FormMethod.Post))
{
<!-- Some form controls -->
<input type="submit" value="Signup" />
}
Controller:
public class MemberSignupSurfaceController : SurfaceController
{
public ActionResult MemberSignup()
{
ViewData["Test"] = "From MemberSignup";
// Do database stuff and create model from that
return PartialView("MemberSignupView", model);
}
[HttpPost]
public ActionResult MemberSignupSubmit(MemberViewModel model)
{
ViewData["Test"] = "From MemberSignupSubmit";
if (ModelState.IsValid)
{
// Redirect to success using TempData
}
else
{
return CurrentUmbracoPage();
}
}
}
When my page load MemberSignup is called and the page shows
Parent ViewData:
PartialView ViewData: From MemberSignup
Then when i submit the form on the partial view with invalid input so it won't validate and it calls CurrentUmbracoPage() in the action MemberSignupSubmit
I get the following:
Parent ViewData: From MemberSignupSubmit
PartialView ViewData: From MemberSignup
If i use #Html.Partial to render my partial view both viewbags shows the same value set from the submit action.
I've tried TempDatabut it is not working either. Is there really no way to pass anything back to the partial view after i return from the submit action when using #Html.Action to render a partial view form.
The overall problem I am trying to solve is to popluate a dropdown in my form with values from the database. Using #Html.Partial don't allow me to do this but have a working viewbag.
I did this to render a dynamic dropdown list with values from a database. Maybe it will help someone.
It is a music player which needs a dynamic db populated menu to list the playlists
I made a base controller which all other controllers inherit from. In that base class, I have a PlaylistPopupMenu action which gets the list of playlists from a db.
public PartialViewResult PlaylistPopupMenu()
{
try
{
return PartialView("_PlaylistPopupMenu", db.GetPlaylists(1).ToList());
}
catch (Exception)
{
throw;
}
}
Then I have a _PlaylistPopupMenu partial view as follows:
#model List<OneMusic.Models.GetPlaylists_Result>
#if (Model.Count > 0)
{
<li style="height:2px" class="divider"></li>
foreach (var item in Model)
{
<li style="height:30px">#Html.DisplayFor(p => item.Name)
#Html.ActionLink(item.Name, "AddSong", "Playlist", new { playlistId = #item.PlaylistId, songId = 1 }, "")
</li>
}
}
this renders the dynamic parts of the menu (ie the playlists)
Finally the main page has this to build the dynamic part of the menu:
<ul class="dropdown-menu" style="margin-top:10px"><p class="text-primary" style="margin-left:18px; margin-top:6px">Add To</p>
<!-- other static menu items here-->
<li style="margin-top:-60px; height:0px">#Html.Action("PlaylistPopupMenu")</li>
</ul>

Gracefully handling permalinks when using pushstate for partial view pagination

I'm looking for a clean way to handle permalinks when my application uses pushstate to load ASP.NET MVC partial views for pagination
If my controller returns a partial view (currently) then the permalink will display the partial view alone without the rest of the site defined by _Layout.cshtml.
If my controller return a regular view, the permalink works but loading the entire page into the container provided only for part of the page creates a type of Droste Effect.
The only solution I've found is to redirect the 404 to the homepage and navigate to correct page using the relevant portion of the url.
However, this prevents me from using my custom error page (which just happens to be an awesome error page).
Ok I figured out how to make this work with a double-controller setup.
NavigationController handles ajax calls and returns partial views (note there is no "Index")
public class NavigationController : Controller
{
public PartialViewResult Home()
{
return PartialView("Home", null);
}
public PartialViewResult About()
{
return PartialView("About", null);
}
...
}
Separate PermalinkController handles permalinks by returning entire regular Views (note this has an "Index" but no "Home":
public class PermalinkController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
...
}
Routes:
routes.MapRoute(
"PermalinkRouter",
"{action}",
new { controller = "Permalink", action = "Index" },
new { }
);
routes.MapRoute(
"NavigationRouter",
"Navigation/{action}/{id}",
new { controller = "Navigation", action = "Home", id = UrlParameter.Optional },
new { }
);
Views:
~/Views/Shared/_Layout.cshtml:
...
<header></header>
<div id="pageViewContainer">
#RenderBody()
</div>
<footer></footer>
...
~/Views/Navigation/Home.cshtml PartialView:
<div> Home Content </div>
~/Views/Permalink/Index.cshtml View:
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("~/Views/Navigation/Home.cshtml", null)