【问题标题】:MVC Ajax with Dynamic Partial View Creation具有动态局部视图创建的 MVC Ajax
【发布时间】:2015-04-11 21:49:18
【问题描述】:

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

例如:

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

到目前为止我做了什么:

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

  • 这将调用控制器并对投票进行汇总

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

有什么问题

  • 我不想硬编码 30-100 个不同的 ajax.actionlink 来调用 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 在同一个 razor 视图中显示部分视图的返回结果。

<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 显示循环中的 cmets。

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 调用。

【问题讨论】:

  • 容器(带 ID 的 div)是局部视图的一部分?我不确定我是否理解这个问题(可能包括示例代码?)
  • 没有。因此,Ajax.Action 链接将调用一个 Action,该 Action 作为回报将调用将填充 div 的局部视图。问题是根据需要多少动态创建 div 和部分视图。稍后我会发布一些示例代码。
  • 你的 Ajax Helpers 已经超过了。你真的想要一个像 AngularJS 或 EmberJS 这样的 Javascript MVC 语言。使用健壮的模板化框架,您的视图数据可以耦合到 JS 变量。因此,对端点注册投票的相同调用也可以返回投票计数。一旦你更新了你的 vote-count JS 变量,你的 View 就可以在没有 DOM 操作或加载的情况下反映新的数据。
  • 您有示例链接吗?
  • 如果你已经在视图中渲染了cmets,为什么还要调用一个方法来再次返回视图。只需给每个“投票按钮”一个类名并将关联的评论 ID 存储在 data- 属性中,然后处理 .click() 事件并使用 ajax 将 ID 传递给方法并在成功函数中更新关联的投票计算 DOM 中的值。

标签: asp.net-mvc-4


【解决方案1】:

试试这个方法。

主视图

我假设您有一个模型,其集合属性为 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 部分

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

【讨论】:

  • 你的意思是把class id传给控制器,让它知道要更新哪一个?
  • 类属性vote-link 将您的锚元素链接到javascript 点击处理程序。 data-id 属性包含您需要跟踪特定评论(您的控制器操作用于记录投票)的 id。 link 变量持有对触发点击的锚点的引用,AJAX 回调使用 link 更新正确的项目。
  • 解决方案接近您的建议。我必须即时生成 div css id 并将相同的名称传递给控制器​​。就这么容易。但如果没有你的回答,我会考虑的。所以你得到了积分。
【解决方案2】:

在这里使用Ajax 助手似乎是不必要的开销,我建议您只使用jquery 方法来更新DOM。您当前的代码表明您可能缺少一些使评论投票系统工作的逻辑,包括指示用户可能已经执行的操作。例如(并假设您希望它与 SO 类似),如果用户先前已投票,则单击 up-vote 链接应将投票计数减少 1,但单击 down-vote 链接应减少投票计数为 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; }
}

并假设您有一个包含 cmets 集合的模型

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

【讨论】:

  • 斯蒂芬,谢谢你的帮助。我会选择杰森的答案。
猜你喜欢
  • 2018-01-25
  • 1970-01-01
  • 2017-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多