I've seen some examples of how to do this, but they are older than the beta release and none seems to handle the entire process of posting to the server, and then displaying a result from that data on the screen using JQuery. JQuery makes this process really simple (is anything really hard with JQuery?) but it took a bit of figuring so I thought I'd share. All of the methods were also different so I have my own take. Just remember that there are lots of ways to do things with JQuery, so modify to your needs. For my example I am again using Spark, as I think it kicks serious butt.
What I have is essentially a thread or discussion listing. It looks like:
<table cellpadding="7" border="1" width="100%">
<thead>
<tr>
<td colspan="2">
<h1>${thread.Topic}</h1>
Replies: ${thread.NumberOfPosts - 1} | Views: ${thread.Views} | Score: ${thread.Score}
</td>
<td align="right">Email me when this is replied to</td>
</tr>
</thead>
<tbody id="posts">
<for each="var message in messages">
<tr>
<td>${message.PostedBy.NickName}</td>
<td>Posted: ${message.Posted}</td>
<td align="right">Quote Message</td>
</tr>
<tr>
<td valign="top" nowrap="nowrap">
Joined: ${message.PostedBy.Joined.ToShortDateString()}<br />
Standing: ${message.PostedBy.Standing}<br />
Location: ${message.PostedBy.Location}
</td>
<td colspan="2">
${message.Comment}
</td>
</tr>
<tr>
<td><a href="javascript:scroll(0,0)">Back to top</a></td>
<td>PM | Email</td>
<td align="right"></td>
</tr>
</for>
</tbody>...
I would like to allow the user to add another message to this thread. So I give them something like:
<form action="~/Message/New" method="post" id="newMessage">
<input type="hidden" id="threadId" name="threadId" value="${thread.Id}" />
Post Comment<br />
<textarea id="comment" name="comment" style="width:100%;" rows="4"></textarea>
<input type="button" value="Post Message" id="postMessage" />
And over in my MessageController I'd have something like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(FormCollection form)
{
Message message = new Message();
message.Thread = _repository.Get<Thread>(int.Parse(form.Get("threadId")));
message.Posted = DateTime.Now;
message.PostedBy = _userService.FindUserBy(User.Identity.Name);
message.Comment = form.Get("comment");
message.Score = 0;
_repository.Save(message);
ViewData["Message"] = message;
return View("New");
}
Here, I tried to use the TryUpdateModel but the threadId wasn't playing nice with Message's Thread property. Instead I had to load it up. If you know a work around I'd love to know about it. Now, if you don't need anything back, replace ActionResult with void and don't return anything. In my case though I want to show the posted message on the page, so I shove the message into my ViewData and render the New.spark view. That looks like a slice of the above thread html. In fact I copy and pasted it like this:
<viewdata message="Message" />
<content name="page">
<tr>
<td>${message.PostedBy.NickName}</td>
<td>Posted: ${message.Posted}</td>
<td align="right">Quote Message</td>
</tr>
<tr>
<td valign="top" nowrap="nowrap">
Joined: ${message.PostedBy.Joined.ToShortDateString()}<br />
Standing: ${message.PostedBy.Standing}<br />
Location: ${message.PostedBy.Location}
</td>
<td colspan="2">
${message.Comment}
</td>
</tr>
<tr>
<td><a href="javascript:scroll(0,0)">Back to top</a></td>
<td>PM | Email</td>
<td align="right"></td>
</tr>
</content>
This brings me to a hiccup that I encountered. You might need to modify your master page (Application.spark). A "use" tag around the entire master page will allow you to only output what we need, and not the entire page. Normally we'd want to insert content within the master page, but for this we just want a snippet of html. To further illustrate what I'm talking about, here's a sample from Application.spark:
<use content="page">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
</html>
</use>
This makes sure that I only output the tr snippet from above. There might be a different work around for this, and again I'm all ears, but for now this worked great. Now that we have all of that in place it's time for us to bring it home with some JQuery script.
<script language="javascript">
$(document).ready(function() {
$("#postMessage").click(function() {
$("#newMessage").fadeOut("slow");
var form = $("#newMessage");
var action = form.attr("action");
var serializedForm = form.serialize();
$.post(action, serializedForm, postAdded, "html");
return false;
});
function postAdded(html) {
$('#posts').append(html);
$("#newMessage").fadeIn("fast");
}
});
</script>
I'm using the click event, but I believe you could also turn it into a submit button and handle a onsubmit event. This might work better for browsers with javascript not enabled, but I'll have to investigate that further. Once it's clicked I hide the postMessage box, grab the form, get it's action attribute, serialize the data from the form, and then use the .post to post to my controller in an ajax fashion. Within the post you can see that my callback method is postAdded, this will fire when my New controller is finished with the snippet on my view. I've specified "html", but it seems that this is not needed. I kept it to remind myself that there are other data types that can be returned like "json", in case I decide to construct the html within the postAdded method instead or something.
Now that the html snippet is returned and passed into the postAdded method, I simply grab my post's tbody tag , #posts and then appending the snippet. Then I make the comment box reappear. Nifty!