I am practicing ASP and MVC by making a website like Twitter and I am having trouble with using a PartialView.
I have my User Home Page Controller here, which gets called from my Login Page Controller.
public class UserController : Controller
{
private readonly Twitter _twitter = null;
private readonly TwitterCloneDBContext _context;
public UserController(TwitterCloneDBContext context)
{
_twitter = new Twitter(context);
_context = context;
}
public ActionResult Index()
{
List<Tweet> tweets = _twitter.TweetList();
ViewBag.Tweets = tweets.Count;
ViewBag.Followers = 2;
ViewBag.Following = 3;
return View();
}
public PartialViewResult TweetList()
{
List<Tweet> tweetList = _twitter.TweetList();
return PartialView("TweetList",tweetList);
}
}
Here is the View for it.
@{
ViewData["Title"] = "Home Page";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using Microsoft.AspNetCore.Http;
@model IEnumerable<Tweet>
@if (Context.Session.GetString("FullName") != null)
{
<div class="text-center">
<h1 class="display-4">Welcome, @Context.Session.GetString("FullName")!</h1>
</div>
<hr />
<div class="row">
<div class="col-sm-2">
<div class="text-left">
<h4>Follow</h4>
<form asp-action="Search">
<div class="input-group mb-3">
<input type="text" name="searchName" class="form-control" placeholder="Search User" />
<div class="input-group-append">
<button class="btn btn-primary" type="submit">Search</button>
</div>
</div>
</form>
<span class="text-danger">@ViewBag.SearchFail</span>
</div>
<div>
<h6>@ViewBag.Tweets Tweets</h6>
<br />
<h6>@ViewBag.Followers Followers</h6>
<br />
<h6>@ViewBag.Following Following</h6>
</div>
</div>
<div class=border></div>
<div class="col-4">
<h4>Tweet your thoughts!</h4>
<form asp-action="Tweet">
<textarea rows="5" cols="100" name="message" class="form-control"> </textarea>
<br />
<input type="submit" value="Tweet" class="btn btn-primary" />
</form>
<span class=" text-danger">@ViewBag.ErrMessage</span>
<div>
@Html.PartialAsync("TwitterList",Model)
</div>
</div>
</div>
}
else
{
<h2>You are not Authorized!</h2>
<script type="text/javascript">
window.setTimeout(function () {
window.location.href = '/Home/UserLogin';
}, 1000);
</script>
}
The Partial Line is supposed to call TwitterList View which is working perfectly and displaying List as it is supposed to.
@model IEnumerable<TwitterClone.Models.Tweet>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.User_ID)
</th>
<th>
@Html.DisplayNameFor(model => model.Message)
</th>
<th>
@Html.DisplayNameFor(model => model.Created)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.User_ID)
</td>
<td>
@Html.DisplayFor(modelItem => item.Message)
</td>
<td>
@Html.DisplayFor(modelItem => item.Created)
</td>
</tr>
}
</tbody>
</table>
But when I call it using the @Html.PartialAsync() It fails by saying
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=TwitterClone.Views
StackTrace:
at AspNetCore.Views_User_TweetList.<ExecuteAsync>d__0.MoveNext() in C:\Users\Cloud\source\repos\TwitterClone\TwitterClone\Views\User\TweetList.cshtml:line 19
and giving me this in the main page
See below Tweet Button, that is my Partial View, if I do the Partial View any other way I get NullReferenceException in both the places. This is the closes I've gotten to this and I cannot figure it out.
EDIT:
My ViewBags are in another Actions that are not relevant here. They basically print an error message.
The NullExceptions are gone, my Model is not empty when I open the Table View on its own. But its empty when being called through Partial. I suspect its because something isn't being called in the chain.
If I change my Index Action to like this,
public ActionResult Index()
{
List<Tweet> tweets = _twitter.TweetList();
ViewBag.Tweets = tweets.Count;
ViewBag.Followers = 2;
ViewBag.Following = 3;
return View("Index",tweets);
}
I get this error.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[TwitterClone.Models.Tweet]', but this ViewDataDictionary instance requires a model item of type 'TwitterClone.Models.Person'.
I have no Idea where ViewDataDictionary is setting Person to. The only place where I can think of that connects to UserController is my HomeController.
EDIT
This was a dumb mistake, I left out an @model Person in my Layout file. Sorry.
EDIT
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly TwitterCloneDBContext _context;
public HomeController(ILogger<HomeController> logger, TwitterCloneDBContext context)
{
_logger = logger;
_context = context;
}
//GET: Home/UserLogin
public ActionResult UserLogin()
{
return View();
}
//POST: Home/UserLogin
[Microsoft.AspNetCore.Mvc.HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UserLogin(Person userLogin)
{
var login = _context.Person.Where(a => a.UserID.Equals(userLogin.UserID)
&& a.Password.Equals(userLogin.Password)).FirstOrDefault();
if (login != null)
{
Person session = _context.Person.SingleOrDefault(u => u.UserID == userLogin.UserID);
session.Active = 1;
HttpContext.Session.SetString("FullName", login.FullName);
HttpContext.Session.SetString("UserID", login.UserID);
_context.SaveChanges();
return RedirectToAction("Index", "User");
}
else
{
ViewBag.ErrMsg = "Invalid Credentials";
return View();
}
}
}
And its View
@model Person
@{
ViewData["Title"] = "UserLogin";
Layout = "~/Views/Shared/LoginLayout.cshtml";
}
<h1>Welcome!</h1>
<h4>Enter Credentials</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="UserLogin">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserID" class="control-label">User ID</label>
<input asp-for="UserID" class="form-control" />
<span asp-validation-for="UserID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label">Password</label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
New User? <a asp-action="Signup">Signup</a>
</div>