Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Ajax with Dynamic Partial View Creation

How can I create dynamic ajax.actionlinks that will call dynamic partial views.

For example:

  • I have a page that will generate x number of comments
  • Each comment can be voted up or down (individually)
  • The number of up votes and down votes are counted into a single integer
  • Each comment div will have its own ajax.actionlink
  • Each ajax.actionlink will pass to the controller the ID of the comment
  • The controller will calculate the total votes and call the partial view to display back into the div with the correct ID.

What have I done so far:

  • I have been able to create successful ajax.actionlink

  • That will call a controller and sum the votes

  • That will call the partial view and display the votes

What is the issue

  • I don't want to hard code 30-100 different ajax.actionlinks to call 30-100 hard coded partial views.

How can I accomplish this dynamically?

Existing Code:

My ajax.actionlink inside my razor view

 @Html.Raw(Ajax.ActionLink("[replacetext]", "VoteUp",
                new { UserPostID = @Model.Id },
                        new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "CountVote" }).ToHtmlString().Replace("[replacetext]",
                        "<img src=\"/Images/up_32x32.png\" />"))

My div inside the same razor view to display the returning results from the partial view.

<div id="CountVote" class="postvotes"></div>

My controller

    public PartialViewResult VoteUp(int UserPostID)
    {
        try
        {
            UserVotes vote = new UserVotes();
            vote.SubmitedVote = 1;
            vote.UserId = Convert.ToInt32(Session["id"]);
            vote.UserPostID = UserPostID;
            ViewBag.SumVotes = postRepository.InsertUserPostVote(vote);

        }
         catch (Exception e)
        {
            xxx.xxx.xxxx().Raise(e);
        }
        return PartialView("_TotalVotes");
    }

And finally my partial view (_TotalVotes.cshtml)

@ViewBag.SumVotes

Now my main view for Viewpost shows the comments in a loop using the viewbag.

foreach (var item in (List<UserComment>)ViewData["Comments"])
            {
                CommentVote = "cv" + i.ToString();
    <div class="postlinewrapper">
        <div class="postvotesframe">
            <div class="postvotes">
                @Html.Raw(Ajax.ActionLink("[replacetext]", "VoteUp",
                        new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "CountVote" }).ToHtmlString().Replace("[replacetext]",
                        "<img src=\"/Images/up_32x32.png\" />"))
            </div>

            <div id="@CommentVote" class="@CommentVote">0</div>
            <div class="postvotes">
                @Html.Raw(Ajax.ActionLink("[replacetext]", "VoteDown",
                        new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "CountVote" }).ToHtmlString().Replace("[replacetext]",
                        "<img src=\"/Images/down_32x32.png\" />"))
            </div>
        </div>
        <div class="postleftbar">
            @Html.Raw(item.Comment)
        </div>
        <div class="postrightbar">
            <div>
                <div class="post_spec">
                    <div class="post_spec_title">Call Sign:  </div>
                    <div class="post_spec_detail">@item.CallSign</div>
                </div>
                <div class="post_spec">
                    <div class="post_spec_title">When:  </div>
                    <div class="post_spec_detail">@item.CommentDate.ToString("dd/MM/yyyy")</div>
                </div>
            </div>
            <br />
            <br />
        </div>
    </div>
                i += 1;
            }

I have implemented the login to increase or decrease votes up and down:

 public PartialViewResult VoteUp(int userPostId)
        {
            try
            {
                UserVotes vote = new UserVotes();
                vote.SubmitedVote = 1;
                vote.UserId = Convert.ToInt32(Session["id"]);
                vote.UserPostID = userPostId;
                ViewBag.SumVotes = postRepository.InsertUserPostVote(vote);

            }
             catch (Exception e)
            {
                xxxx.xxxx.xxxx().Raise(e);
            }
            return PartialView("_TotalVotes");
        }

        public PartialViewResult VoteDown(int userPostId)
        {
            try
            {
                UserVotes vote = new UserVotes();
                vote.SubmitedVote = -1;
                vote.UserId = Convert.ToInt32(Session["id"]);
                vote.UserPostID = userPostId;
                ViewBag.SumVotes = postRepository.InsertUserPostVote(vote);

            }
            catch (Exception e)
            {
                xxx.xxxx.xxxx().Raise(e);
            }
            return PartialView("_TotalVotes");
        }

Now all this code works for 1 ajax call just fine, but what I need to is to display separate ajax calls for separate divs dynamically.

like image 971
Internet Engineer Avatar asked Apr 11 '15 21:04

Internet Engineer


1 Answers

Try it this way.

Main view

I'm supposing you have a model with a collection property Comments of Comment items

@model MyNamespace.CommentAndOtherStuff

<ul>
    @foreach(item in Model.Comments)
    {
      <li>
          <a href="@Url.Action("VoteUp", "VoteControllerName", new { UserPostId = item.Id })" 
             class="vote-link"
             data-id="@item.Id">@item.Votes</a><img src="vote.jpg" />
      </li>
    }
</ul>

And your controller just returns a class called VoteResult as JSON.

[HttpPost]
public ActionResult VoteUp(int UserPostID)
{
    ...
    var model = new VoteResult
    {
        UserPostID = UserPostID,
        Votes = service.tallyVote(UserPostID)
    };

    return Json(model);
}

Now hook all of those up with a jQuery event handler and setup an AJAX call

$(document).ready(function() {

    $("a.vote-link").on("click", function(event) {
        event.preventDefault();
        var link = $(this);  // the link instance that was clicked
        var id = link.attr("data-id");
        var url = link.attr("href");

        $.ajax({
            url: url,
            type: "post"
        })
        .done(function(result) {
            // JSON result: { UserPostID: 1, Votes: 5 }

            // replace link text
            link.html(result.Votes);
        });
    });

});

But I want a partial view html fagment.

[HttpPost]
public ActionResult VoteUp(int UserPostID)
{
    ...
    var model = new VoteResult
    {
        UserPostID = UserPostID,
        Votes = service.tallyVote(UserPostID)
    };

    return PartialView("_TotalVotes", model);
}

_TotalVotes partial

@model MyNamespace.VoteResult

@if (Model.Votes < 0)
{
    <span class="unpopular">@Model.Votes</span>
}
else
{
    <span class="awesome">@Model.Votes</span>
}

And adjust the AJAX callback

.done(function(result) {
    link.html(result);
});

Now you could write a helper for the link fragment but it obfuscates things in my opinion (it's a judgement call). All you really need here is the class name and the data-id which your javascript will bind.

like image 96
Jasen Avatar answered Sep 30 '22 06:09

Jasen