具有动态部分视图创建的MVC Ajax

时间:2015-04-11 21:49:19

标签: asp.net-mvc-4

如何创建将调用动态部分视图的动态ajax.actionlinks。

例如:

  • 我有一个会生成x条评论的页面
  • 每条评论都可以上下投票(单独)
  • 向上投票数和向下投票数计入一个整数
  • 每个评论div都有自己的ajax.actionlink
  • 每个ajax.actionlink都会将注释的ID传递给控制器​​
  • 控制器将计算总票数并调用局部视图以显示回具有正确ID的div。

到目前为止我做了什么:

  • 我已经能够创建成功的ajax.actionlink

  • 这将调用控制器并总结投票

  • 这将调用局部视图并显示投票

问题是什么

  • 我不想硬编码30-100个不同的ajax.actionlinks来调用30-100个硬编码的部分视图。

如何动态完成此操作?

现有代码:

我的剃刀视图中的ajax.actionlink

 @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\" />"))

我的div在同一个剃刀视图中显示部分视图的返回结果。

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

我的控制器

    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");
    }

最后是我的部分视图(_TotalVotes.cshtml)

@ViewBag.SumVotes

现在,Viewpost的主视图使用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;
            }

我已实施登录以上下增加或减少投票:

 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");
        }

现在所有这些代码都适用于1 ajax调用,但我需要的是动态地为单独的div显示单独的ajax调用。

2 个答案:

答案 0 :(得分:5)

以这种方式试试。

主视图

我假设您的模型具有评论项的集合属性Comments

@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>

您的控制器只返回一个名为VoteResult的类作为JSON。

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

    return Json(model);
}

现在用jQuery事件处理程序挂起所有这些并设置一个AJAX调用

$(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);
        });
    });

});

但我想要部分查看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>
}

调整AJAX回调

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

现在你可以为链接片段写一个帮助器,但是在我看来它混淆了一些东西(它是一个判断调用)。这里你真正需要的是你的javascript将绑定的类名和数据ID。

答案 1 :(得分:4)

在这里使用Ajax助手似乎是一种不必要的开销,我建议你只使用jquery方法来更新DOM。您当前的代码表明您可能缺少某些逻辑来使注释投票系统工作,包括指示用户可能已执行的操作。例如(并且假设您希望它与SO类似),如果用户之前已经投票,则点击向上投票链接应该将投票计数减1,但点击向下投票链接应该减少投票数量为2(之前的投票加上新的投票)。

有关如何设置样式并在单击投票元素

时的行为,请参阅this fiddle

评论的视图模型可能如下所示

public enum Vote { "None", "Up", "Down" }
public class CommentVM
{
  public int ID { get; set; }
  public string Text { get; set; }
  public Vote CurrentVote { get; set; }
  public int TotalVotes { get; set; }
}

并假设您的模型包含一组注释

public class PostVM
{
  public int ID { get; set; }
  public string Text { get; set; }
  public IEnumerable<CommentVM> Comments { get; set; }
}

以及相关的DisplayTemplate

/Views/Shared/DisplayTemplates/CommentVM.cshtml

@model CommentVM
<div class="comment" data-id="@Model.ID" data-currentvote="@Model.CurrentVote">
  <div class="vote">
    <div class="voteup" class="@(Model.CurrentVote == Vote.Up ? "current" : null)"></div>
    <div class="votecount">@Model.TotalVotes</div>
    <div class="votedown" class="@(Model.CurrentVote == Vote.Down ? "current" : null)"></div>
  </div>
  <div class="commenttext">@Html.DisplayFor(m => m.Text)</div>
</div>

然后在主视图中

@model PostVM
.... // display some properties of Post?
@Html.DisplayFor(m => m.Comments)

<script>
  var voteUpUrl = '@Url.Action("VoteUp")';
  var voteDownUrl = '@Url.Action("VoteDown")';
  $('.voteup').click(function() {
    var container = $(this).closest('.comment');
    var id = container.data('id');
    var voteCount = new Number(container.find('.votecount').text());
    $.post(voteUpUrl, { id: id }, function(response) {
      if (!response) {
        // oops, something went wrong - display error message?
        return;
      }
      container.find('.votecount').text(response.voteCount); // update vote count
      if (response.voteCount < voteCount) {
        // the user previously upvoted and has now removed it
        container.find('.voteup').removeClass('current');
      } else if (response.voteCount == voteCount + 1) {
        // the user had not previously voted on this comment
        container.find('.voteup').addClass('current');
      } else if (response.voteCount == voteCount + 2) {
        // the user previoulsy down voted
        container.find('.votedown').removeClass('current');
        container.find('.voteup').addClass('current');
      }
    });
  });
  $('.votedown').click(function() {
    ... // similar to above (modify logic in if/elseif blocks)
  });

</script>

和控制器方法

public JsonResult VoteUp(int id)
{
  int voteCount = // your logic to calculate the new total based on the users current vote (if any) for the comment
  return Json(new { voteCount = voteCount });
}