2015-04-11 5 views
15

Как создать динамические ajax.actionlinks, которые будут вызывать динамические частичные представления.MVC Ajax с созданием динамического частичного представления

Например:

  • У меня есть страница, которая будет генерировать й количество комментариев
  • Каждого комментарий может быть проголосовавшими вверх или вниз (по отдельности)
  • числа до голосов и вниз голоса пересчитываются в одно целое
  • Каждый комментарий div будет иметь свою собственную ajax.actionlink
  • Каждая строка ajax.action будет передана контроллеру 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 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 вызова просто прекрасно, но то, что мне нужно, чтобы отобразить отдельный Аякса вызовы для отдельных divs динамически.

+0

Контейнера (Div с идентификатором) является частью частичного вида? Я не уверен, что я понимаю вопрос (может быть, примерный код?) –

+0

Нет. Таким образом, ссылка Ajax.Action вызовет действие, которое в ответ вызовет частичное представление, которое будет заполнять div. Проблема заключается в создании divs и частичных представлений «на лету» на основе того, сколько из них необходимо. Я напишу позже пример кода. –

+0

Вы перерастаете помощников Ajax. Вам действительно нужен Javascript MVC-язык, такой как AngularJS или EmberJS. Благодаря надежной шаблонной структуре ваши данные могут быть связаны с переменными JS. Таким образом, тот же призыв к конечной точке для регистрации голосования может также вернуть подсчет голосов. После того, как вы обновите переменные JS для подсчета голосов, ваш вид может отражать новые данные без манипуляций DOM или загрузки. –

ответ

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 частичные

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

+0

вы имеете в виду передать идентификатор класса контроллеру, чтобы он знал, какой из них нужно обновить? –

+0

Атрибут class 'vote-link' связывает ваши элементы привязки с обработчиком javascript. Атрибут 'data-id' содержит идентификатор, необходимый для отслеживания конкретного комментария (который действие вашего контроллера использует для записи голосов). Переменная 'link' содержала ссылку на якорь, который вызвал щелчок, и обратный вызов AJAX использует' link' для обновления правильного элемента. – Jasen

+0

Решение было близко к вашему предложению. Мне пришлось генерировать div css id на лету и передавать одно и то же имя контроллеру. Это было так просто. Но я бы подумал об этом без вашего ответа. Таким образом, вы получаете очки. –

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

Стивен, спасибо вам за помощь. Я собираюсь выбрать ответ Ясена. –

Смежные вопросы