2015-04-11 10 views
15

Wie kann ich dynamische ajax.actionlinks erstellen, die dynamische Teilansichten aufrufen.MVC Ajax mit dynamischer Erstellung von Teilansichten

Zum Beispiel:

  • Ich habe eine Seite, die x Anzahl der Kommentare
  • Jeder Kommentar kann abgestimmt bis generieren oder unten (einzeln)
  • Die Anzahl der oben Stimmen und nach unten Stimmen werden in eine einzige ganze Zahl gezählt
  • Jeder Kommentar div wird seine eigenen ajax.actionlink haben
  • Jeder ajax.actionlink wird an die Steuerung der übergeben ID des Kommentars
  • Der Controller berechnet die Gesamtstimmen und ruft die Teilansicht auf, um sie mit der richtigen ID im Div anzuzeigen.

Was habe ich bisher getan:

  • konnte ich erfolgreich Ajax.ActionLink

  • schaffen, die einen Controller anrufen und ergibt die Summe der Stimmen

  • Das ruft die Teilansicht auf und zeigt die Stimmen an

was das Problem ist

  • Ich will nicht zu hart Code 30-100 verschiedene ajax.actionlinks 30-100 hart codierte Teilansichten zu nennen.

Wie kann ich dies dynamisch erreichen?

Bestehende Code:

Mein Ajax.ActionLink in meiner Rasierer Ansicht

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

Mein div innerhalb der gleichen Rasierer Ansicht, die die Ergebnisse zu der Teilansicht angezeigt werden soll.

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

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

Und schließlich meine Teilansicht (_TotalVotes.cshtml)

@ViewBag.SumVotes 

Jetzt für Viewpost meine Hauptansicht zeigt die Kommentare in einer Schleife die viewbag verwenden.

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

ich die Login umgesetzt haben Stimmen zu erhöhen oder verringern nach oben und unten:

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

Das ist aber alles Code funktioniert für 1 Ajax-Aufruf ganz gut, aber was ich brauche, ist, getrennte Ajax anzuzeigen ruft separate divs dynamisch auf.

+0

Der Behälter (div mit ID) ist ein Teil der Teilansicht? Ich bin mir nicht sicher, ob ich die Frage verstehe (vielleicht Beispielcode einbeziehen?) –

+0

Nein. Also ruft der Link Ajax.Action eine Aktion auf, die im Gegenzug eine Teilansicht aufruft, die ein div auffüllt. Das Problem besteht darin, divs und partielle Views on the fly basierend darauf zu erstellen, wie viele benötigt werden. Ich werde später einen Beispielcode veröffentlichen. –

+0

Sie entwachsen Ajax Helpers. Sie möchten wirklich eine Javascript MVC Sprache wie AngularJS oder EmberJS. Mit einem robusten Template-Framework können Ihre Viewdaten an JS-Variablen gekoppelt werden. Daher kann derselbe Aufruf an einen Endpunkt zur Registrierung einer Abstimmung auch eine Stimmenanzahl zurückgeben. Nachdem Sie Ihre JS-Variablen mit Vote-Count aktualisiert haben, kann Ihre View die neuen Daten ohne DOM-Manipulation oder -Laden widerspiegeln. –

Antwort

5

Versuchen Sie, diese Art und Weise es.

Hauptansicht

Ich bin der Annahme, Sie haben ein Modell mit einer Sammlungseigenschaft Comments von Comment Artikel

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

Und Ihre Controller gibt nur eine Klasse VoteResult als JSON genannt.

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

    return Json(model); 
} 

Jetzt alle die sich mit einem jQuery Event-Handler und Setup-Haken einen AJAX-Aufruf

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

}); 

Aber ich möchte eine Teilansicht html fagment.

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

    return PartialView("_TotalVotes", model); 
} 

_TotalVotes Teil

@model MyNamespace.VoteResult 

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

Und die Callback-AJAX anpassen

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

Nun könnte man einen Helfer für den Link Fragment schreiben, aber es verschleiert Dinge meiner Meinung nach (es ist ein Urteilsspruch). Alles, was Sie wirklich brauchen, ist der Klassenname und die Daten-ID, die Ihr Javascript bindet.

+0

Sie meinen, übergeben Sie die Klassen-ID an den Controller, damit er weiß, welcher zu aktualisieren ist? –

+0

Das Klassenattribut 'vote-link' verknüpft Ihre Ankerelemente mit dem Javascript-Klick-Handler. Das Attribut "Daten-ID" enthält die ID, die Sie benötigen, um den spezifischen Kommentar zu verfolgen (den Ihre Controller-Aktion zum Aufzeichnen von Stimmen verwendet). Die 'link'-Variable enthielt den Verweis auf den Anker, der den Klick ausgelöst hatte, und der AJAX-Rückruf verwendet 'link', um das korrekte Element zu aktualisieren. – Jasen

+0

Die Lösung war nah an Ihrem Vorschlag. Ich musste die div CSS ID im laufenden Betrieb generieren und den gleichen Namen an den Controller weitergeben. Es war so einfach. Aber ich hätte ohne Ihre Antwort darüber nachgedacht. So bekommst du die Punkte. –

4

Die Verwendung der Ajax Helfer scheint hier ein unnötiger Overhead und ich schlage vor, Sie nur jquery Methoden verwenden, um das DOM zu aktualisieren. Ihr aktueller Code weist darauf hin, dass Ihnen möglicherweise eine gewisse Logik fehlt, um ein System zur Kommentierauswahl zu aktivieren, einschließlich der Angabe, welche Aktion der Benutzer möglicherweise bereits ausgeführt hat. Zum Beispiel (und wenn Sie möchten, dass es ähnlich wie SO funktioniert), sollte ein Benutzer, der zuvor eine Abstimmung vorgenommen hat, durch Klicken auf den Aufwärts-Abstimmungs-Link die Anzahl der Stimmen um 1 verringern, aber auf den Abwärts-Abstimmungs-Link klicken die Anzahl der Stimmen beträgt 2 (die vorherige Aufwärtswahl plus die neue Abwärtswahl).

-this fiddle Siehe, wie dies könnte gestylt werden und sich verhalten, wenn die Elemente Abstimmung klicken

Ihre Ansicht Modell für einen Kommentar wie

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

aussehen könnte und unter der Voraussetzung haben Sie ein Modell, das eine Sammlung enthält Kommentare

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

und die damit verbundene DisplayTemplate

/Ansichten/Geteilt/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> 

dann in der Hauptansicht

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

und die Controller-Methode

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

Stephen, danke für deine Hilfe. Ich werde Jasens Antwort wählen. –

Verwandte Themen