While I would have liked a pure Ajax.ActionLink solution (if possible), @SimonGoldstone's more jQuery-based approach (similar to this answer) gets the job done. Here's how it turned out:
User/Index.cshtml
@model IEnumerable<MvcHoist.Models.UserProfileViewModel>
<table>
@foreach (var user in Model)
{
@Html.Partial("_User", user)
}
</table>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script>
var toggleFollowUnfollowLinks = function (followLink, unfollowLink) {
// based on https://stackoverflow.com/a/5545969/103058 and https://stackoverflow.com/a/19689745/103058
var fout = followLink.is(":visible") ? followLink : unfollowLink;
var fin = !followLink.is(":visible") ? followLink : unfollowLink;
fout.fadeOut("fast", function () {
fin.fadeIn();
});
};
</script>
}
_User.cshtml partial
@model MvcHoist.Models.UserProfileViewModel
@{
var unfollowLinkId = Guid.NewGuid().ToString();
var followLinkId = Guid.NewGuid().ToString();
var onSuccess = String.Format("toggleFollowUnfollowLinks($('#{0}'), $('#{1}'))", followLinkId, unfollowLinkId);
}
<tr>
<td>
@Ajax.ActionLink("Unfollow", "Unfollow", new { id = Model.UserProfile.UserId }, new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = @onSuccess
}, new { id = @unfollowLinkId, @style = @Model.IsFollowed ? "display:block" : "display:none" })
@Ajax.ActionLink("Follow", "Follow", new { id = Model.UserProfile.UserId }, new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = @onSuccess
}, new { id = @followLinkId, @style = !@Model.IsFollowed ? "display:block" : "display:none" })
</td>
</tr>
Generating unique IDs (using Guid.NewGuid()) was necessary to prevent an action on e.g. the third row from affecting the Ajax.ActionLink in the first row.