1

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

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>
Serge
  • 40,935
  • 4
  • 18
  • 45
Cloud
  • 25
  • 6
  • Does this answer your question? [Pass parameter to Partial View in ASP.NET Core](https://stackoverflow.com/questions/46100200/pass-parameter-to-partial-view-in-asp-net-core) – yogihosting Jun 16 '21 at 15:04
  • @yogihosting Looks like ViewData.Evail is for single item strings. I am working with Lists so I don't think it answers it. Unless there are other ways to use it. – Cloud Jun 16 '21 at 15:29

1 Answers1

1

What is

<span class="text-danger">@ViewBag.SearchFail</span>

I can't see ViewBag.SearchFail or ViewBag.ErrMessage in your action.

You have a big bug. Move redirect from Index view to action, otherwise it will be always running after creating the page.

 public ActionResult Index()
    {
      if (Context.Session.GetString("FullName") != null)
        {
 
        List<Tweet> tweets = _twitter.TweetList();
         .....
       
        return View("Index", tweets);
       }
         RedirectToAction("Login", "Users")
    }

Since you are using the same model for both, try to replace

    @Html.PartialAsync("TwitterList",Model)  

with

    <partial name="TwitterList" />

and fix the model of both views

@model List<TwitterClone.Models.Tweet>

also fix the partial view

@if(@Model!=null && Model.Count >0)
{
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model[0].User_ID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model[0].Message)
            </th>
          ......
}

Serge
  • 40,935
  • 4
  • 18
  • 45
  • Thanks for the reply, I am getting the same NullReferenceExceptions in both views now, – Cloud Jun 16 '21 at 15:14
  • @Cloud I updated the answer. Pls try again. – Serge Jun 16 '21 at 15:18
  • Null reference can't go throw partial view since you should have already added to your code" if (@Model!=null)" – Serge Jun 16 '21 at 15:33
  • Sorry, what I wanted to say was that my Model.Tweet (in my SQLDB) is filled with Data yet it's showing up as Null in model. Even though the controller is passing the list. – Cloud Jun 16 '21 at 15:37
  • Changing the Index Action results in this: `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 wanted to avoid this error so I was going in a roundabout way of doing this. I have no idea where this Models.Person is being set. – Cloud Jun 16 '21 at 15:42
  • You should post List tweets = _twitter.TweetList(); if you have a problem with an empty model – Serge Jun 16 '21 at 15:43
  • @Cloud I updated my answer again. Move redirect from view to action. – Serge Jun 16 '21 at 16:14