1

So I have 2 controller classes;

AnnouncementsController, this just generates a homepage of sorts with posts from users on it.

// GET: Announcements
    public async Task<IActionResult> Index()
    {
        var announcements = await _context.Announcement.ToListAsync();
        announcements = announcements.OrderByDescending(x => x.CreatedOn).ToList();
        foreach (var ann in announcements)
        {
            ann.TimeAgo = ann.CreatedOn.TimeAgo();
        }
        
        var users = await _context.Users.ToListAsync();
        var comments = await _context.Comment.ToListAsync();
        AnnouncementAndCommentsViewModel aacVM = new AnnouncementAndCommentsViewModel();

        AnnouncemenstViewModel announcemenstViewModel = new AnnouncemenstViewModel();
        announcemenstViewModel.Announcement = announcements;
        announcemenstViewModel.User = users;
        announcemenstViewModel.Comment = comments;

        CommentViewModel commentViewModel = new CommentViewModel();

        aacVM.announcemenstViewModel = announcemenstViewModel;
        aacVM.commentViewModel = commentViewModel;

        ViewData.Add("currentUserID",GetCurrentUser().Id);

        return View(aacVM);
    }

Then I have the CommentsController

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(CommentViewModel commentViewModel)
    {
        if (ModelState.IsValid)
        {
            var comment = new Comment();
            comment.CreatedOn = DateTime.Now;
            comment.Body = commentViewModel.Comment.Body;

            Announcement announcement = GetAnnouncement(commentViewModel.Announcement.AnnouncementID);
            comment.Announcement = announcement;
            ApplicationUser user = GetCurrentUser();
            comment.User = user;

            _context.Add(comment);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(commentViewModel);
    }

From my AnnouncementController Index view, I have a list of posts and then I want to be able to write and add comments directly in that view as opposed to going to another view.

I have 2 ViewModels, one for storing announcements and one for comments.

I'm trying to call the Create Post method in the Comments Controller with its argument that accepts a CommentViewModel although I can't figure it out. I keep getting sent to the Create Get Method with the body filled in.

Here's my index view and how I'm trying to post the data to the CommentsController

@model Treharris.Models.AnnouncementAndCommentsViewModel;
.
.
.
@foreach (var item in Model.announcemenstViewModel.Announcement)
        {
.
.
.
<div class="announcement-body">
                    <p>@Html.DisplayFor(modelItem => item.Body)</p>
                </div>
                <div class="comments">
                    <p id="comment">Comments</p>
                    @using(Html.BeginForm("Create","Comments", FormMethod.Post, 
                     new { cvm = Model.commentViewModel }))
                    {
                        @@Model.commentViewModel.Announcement = item;
                        @Html.HiddenFor(m => m.commentViewModel.Announcement)
                        @Html.TextBoxFor(m => m.commentViewModel.Comment.Body, 
                        new { placeholder = "Comment body" })
                        <input type="submit" value="Create" />

                    }
1
  • For model binding to work, your form element name should match with the action method parameter property name/hierarchy. Create simple flat view models as possible. Commented Dec 10, 2017 at 19:08

1 Answer 1

2

As @Shyju commented, for model binding to work using the form extensions, the Model that you pass to the View has to be the same that the post action receives.

If you look at the HTML that gets generated,

@Html.HiddenFor(m => m.commentViewModel.Announcement)

outputs something as

<input type="hidden" name="commentViewModel.Announcement" value="propVal" />.

On the Action you expect a CommentViewModel object that has no property named commentViewModel, but has a property named Announcement. That is why the binding does not occur.

Either change the parameter on the post action method to match the View Model, but be aware that all properties that do not exist on the form are posted as null,

OR

drop the Html form extension methods that are being deprecated in .NET Core and use simple Html for these type of bindings as the follow:

<form asp-controller="Comments" asp-action="Create" method="post">
        <input type="hidden" name="Announcement" value="@Model.commentViewModel.Announcement" />
        <input type="text" name="Comment.Body" placeholder="Comment body" value="@Model.commentViewModel.Comment.Body" />
        <input type="submit" value="Create" />
</form>

Remember to include all the information regarding the Announcement object as hidden fields, as you cannot post complex objects on forms as you are trying to do. That is an HTML feature/limitation.

For example, simple include the announcement id in the form as:

<input type="hidden" name="Announcement.Id" value="@item.id" />
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.