views:

684

answers:

2

I am trying to implement a feature similar to the Related Questions on StackOverflow, I am doing this in MVC.

$().ready(function() {
   var s = $("#Summary").val();
   $("#Summary").blur(function() { QuestionSuggestions(s); });
});

function GetPastIssues(title) {

$(document).ready(function() {
$.ajax({ type: "POST",
    url: "/Issue/GetSimilarIssues",
    contentType: "application/json; charset=utf-8",
    dataType: "xml",
    dataType: "json",
    data: "{'title':'" + title + "'}",
    processData: false,
    error: function(XMLHttpRequest, textStatus, errorThrown) { ajaxError(XMLHttpRequest, textStatus, errorThrown); },
    success: function(xml) { ajaxFinish(xml); }
  });
});

function ajaxFinish(xml) {
 if (xml.d != "NO DATA") {
    $('#question-suggestions').html(xml.d); //alert(xml.d); // This ALERT IS returning undefined
    $('#question-suggestions').show();
 }
}

The data being returned from my controller is 'undefined', as shown by the commented line in ajaxFinish.
What am I doing wrong?

//[AcceptVerbs(HttpVerbs.Get)]
[JsonParamFilter(Param = "title", TargetType = typeof(string))]
public ActionResult GetSimilarIssues(string title)
{
    var issues = _db.GetSimilarIssues(title).ToList();
    if (title == null || issues.Count() == 0)
       return Json("NO DATA");

    string retVal = null;
    foreach (Issue issue in _db.GetSimilarIssues(title))
    {
        retVal += "<div class='answer-summary' style='width: 610px;'>";
        retVal += "<a href='Issue.aspx?projid=" + issue.ProjectId.ToString() + "&issuetypeid=" + issue.IssueTypeId.ToString() +
                "&issueid=" + issue.IssueId.ToString() + "'>";
        retVal += issue.Summary;
        retVal += "</a>";
        retVal += "</div>";
    }
        return Json(retVal);
  }

EDIT:

I think what will help me learn and implement a solution to my senario is if I can get some insight into how StackOverflow implements this javascript method:

function QuestionSuggestions() {
        var s = $("#title").val();            
        if (s.length > 2) {
            document.title = s + " - Stack Overflow";
            $("#question-suggestions").load("/search/titles?like=" + escape(s));
        }

Looks like a 'Search' folder in the Views folder and a PartialView called 'Title'. A SearchController.cs with the following method:

public ActionResult titles(string like)
{
   // HOW TO IMPLEMENT THIS
   return PartialView("Titles");
}

What goes in the Titles.ascx to display the html?

+3  A: 

The purpose of JSON() is to return a JSON object -- not HTML. JSON object would be something like {html_value: "<li>blah" }. I'm not sure what your ajax request is expecting. If it is expecting JSON (you have dataType set twice), then you can do something like with an anonymous object:

return Json(new {html_value = retVal});

However, if you want to return HTML from your controller -- don't. That's exactly what a view is for. Create a view without any master page and do the loop and return the HTML that way. Ajax apps can take this HTML and drop it wherever necessary.

In fact, while you technically could do the above anonymous object (where you return the html inside of a json object), this isn't what it's for. If you want to use JSON you should be returning values, and letting the javascript on the client format it:

I'm not sure how "heavy" your issues object is, but assume that it only has the three fields you're using. In that case, do:

return Json(issues);

EDIT:

Well, I think "Best Practice" would be to return just the values via JSON and format within the javascript. I'm not familiar enough with JSON(), but I know it works (I'm using it for something simple). Try creating a simple issues object with just those three values and

return Json(issuesTxfr);

You don't need to use partialviews as you're calling from a controller. Just think of it as a very simple view. Here's an example of mine (please don't notice that I'm not following my own JSON advice -- this is from a while back and I now cringe looking at it for a few reasons):

    public ActionResult Controls_Search_Ajax(string q, string el)
    {
        ...

        ViewData["controls"] = controls;
        ViewData["el"] = el;

        return View();
    }

and

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Controls_Search_Ajax.aspx.cs" Inherits="IRSoxCompliance.Views.Edit.Controls_Search_Ajax" %>
<% var controls = ViewData.Get<IEnumerable<IRSoxCompliance.Models.Control>>("controls");
   var el = ViewData.Get<String>("el");

   if (controls != null)
   {
     foreach (var c in controls)
     {
%><%= c.Control_ID %>***<%= c.Full_Control_Name %>***<li id="<%= el %>:li:<%= c.Control_ID %>"><span class="item"><%= Html.BreadCrumb(c, false) %></span><span class="actions"><a href="#" onclick="sx_Remove_Control('<%= el %>', <%= c.Control_ID %>); return false;">Remove</a></span><br></li>
<%   }
   }
%>

Note the fact that there is no master page specified.

James S
I had already tried return Json(issues); but for some reason was not getting any results back. I like the idea of having a new view do the looping and generate the html but I have no idea how to do that. Can you point to a sample? Thanks
Picflight
+1  A: 

You could always return HTML as a string. I'm not saying that this is necessarily the way to go, and I do agree with James Shannon about not using JSON to return HTML.

Phil Haack wrote an excellent article about this on his blog back in May.

Dan Atkinson