I have an #Ajax.ActionLink that replaces his own wrapper, with another content that also has #Ajax.ActionLink, that returns first html content to wrapper. It's link to partial that replaces it's self and viceversa. My problem is that when i want some aditional functionaliti on click to ActionLink, my Jquery returns old partial, the one that has been click, and not the new, in firebug i clearly see my new elements, but cannot reach them, i tried, OnSuccess and OnComplete, but didnt succed! Any ideas?
Here's example:
VIEW
index.cshtml
<div id="box">
#Html.Partial("_PartialOne")
</div>
_PartialOne.cshtml
#Ajax.ActionLink("Get partial two", "getPartial2", "Home",
new AjaxOptions
{
UpdateTargetId = "box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
},
new { #class="first"}
)
<div id="testBox1">Hello, I am partial 1</div>
_PartialTwo.cshtml
#Ajax.ActionLink("Get partial one", "getPartial1","Home",
new AjaxOptions
{
UpdateTargetId = "box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
},
new { #class="second"}
)
<div id="testBox2">Hello there this is partial 2</div>
CONTROLLER
HomeController.cs
public PartialViewResult getPartial1()
{
return PartialView("_PartialOne");
}
public PartialViewResult getPartial2()
{
return PartialView("_PartialTwo");
}
JS
$("#box a.first").live('click', function () {
//how to fetch new data, that was filled with getPartial2()
console.log($(this).parent().children());
//this returns [a.first, div#testBox1]
// and I need [a.second, div#testBox2]
});
How to select returned data to let's say substring if neccesary??
EDIT
using OnComplete = "function"
VIEW
#Ajax.ActionLink("Get partial two", "getPartial2", "Home",
new AjaxOptions
{
UpdateTargetId = "box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnComplete = "getNewData"
},
new { #class="first"}
)
<div id="testBox1">Hello, I am partial 1</div>
JS
function getNewData()
{
console.log($(this));
//this returns [Object] of ajax call, niether
// $(this).parent/s(), or $(this).children() is posible
}
How to select children of element that called this ajax?
I guess I am not 100% what you are looking for, but I think maybe your issue is caching. If you tack a unique parameter onto the GET request, do you get the result you are looking for?
#Ajax.ActionLink("Get partial two", "getPartial2", "Home", new { aparam = DateTime.Now.Ticks },
new AjaxOptions
{
UpdateTargetId = "box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnComplete = "completeGetThing"
},
new { #class="first"}
)
<div id="testBox1">Hello, I am partial 1</div>
Related
I'm working on the table paging, where each page calls the controller to get part of the data, then return the partial view and update it into HTML table.
Here's my controller look like:
public ActionResult SearchData(FormModel model, int? page)
{
/*Codes to get data from DB*/
return PartialView("_DataTable", model);
}
The form input is something like this:
#model Project.ViewModel
#using (Ajax.BeginForm("SearchData", "Player", new AjaxOptions
{ HttpMethod = "GET", UpdateTargetId = "data-table", InsertionMode = InsertionMode.Replace }))
{
#Html.LabelFor(m => m.FormModel.Username)
#Html.TextBoxFor(m => m.FormModel.Username, new { placeholder = "[Username]" })
#*And some more input, all are in FormModel class.*#
}
<div id="data-table">
#Html.Partial("_DataTable")
</div>
The way I generate the page is as follows:
for (var pageNum = Model.PlayerList.Pager.StartPage; pageNum <= Model.PlayerList.Pager.EndPage; pageNum++)
{
if (pageNum == Model.PlayerList.Pager.CurrentPage)
{
<td><span>#pageNum</span></td>
}
else
{
<td>
#Ajax.ActionLink(pageNum.ToString(), "SearchData", new {page = pageNum}, new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "data-table"
})
</td>
}
}
I can only pass in the page value, is it possible to include the form input Model into Ajax.ActionLink? Something like this:
new {model = Model.FormModel, page = pageNum}
I tried the method above, but didn't work. Any help would be appreciated.
you need add properties of FormModel like this
new {Username="some username",page = pageNum}
I came out with another solution. I put the form model inside TempData just before the controller returns the partial view. At the beginning of the controller, I check if the page value is null, the form input should take from TempData instead. This way, we only pass in the page value since the form input model should always the same, using Ajax.ActionLink as I posted on the question.
Here's the code:
public ActionResult SearchData(FormModel model, int? page)
{
if (page != null)
{
model = TempData["FormModel"] as FormModel ?? new FormModel();
}
/*Codes to get data from DB*/
TempData["FormModel"] = model;
return PartialView("_DataTable", model);
}
do it like this and change fields names and values as your model
note: unfortunately, you should send each value one by one, but not the whole model
#Ajax.ActionLink(pageNum.ToString(), "SearchData",
new
{
FielNameInModel1= Model.FieldNameInModel,
FielNameInModel2 = "xyz",
FielNameInModel3 = 5,
FielNameInModel4 = pageNum.ToString(),
},
new AjaxOptions { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "AllProjectsDiv", OnComplete = "AjaxFormCompleted" }
, new { #class = "className") })
I have a DIV (tenant-reference-photos) that holds a partial view that display photos. Beside each photo is a delete button. When it's clicked, I want the photo to be removed from the list and only the DIV to be updated by Ajax.
I'm using an Ajax.ActionLink for the delete action:
<div>
#Ajax.ActionLink("Delete", "DeleteReference",
new { id = photo.ImageId },
new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST",
UpdateTargetId = "tenant-reference-photos" },
new { #class = "btn btn-primary" })
</div>
Controller actions:
[HttpGet]
public ActionResult DeleteReference(int id)
{
return View(refPhotoRepository.Find(id));
}
[HttpPost, ActionName("DeleteReference")]
public ActionResult DeleteReferenceConfirmed(int id)
{
try
{
refPhotoRepository.Delete(id);
refPhotoRepository.Save();
return PartialView("_TenantReferencePhotosPartial");
}
catch (Exception e)
{
// handle exception
}
return View();
}
When I click delete, the action fires and record is deleted from the database. The problem is with return PartialView("_TenantReferencePhotosPartial");. When the action tries to return the partial, I get a NullReferenceException at #if (Model.ReferencePhotos.Count == 0).
_TenantReferencePhotosPartials.cshtml
<div>
#if (Model.ReferencePhotos.Count == 0)
{
<h3>You haven't uploaded any references!
#Ajax.ActionLink("Upload now?",
"TenantUploadReference",
new AjaxOptions
{
UpdateTargetId = "tenant-reference-photos",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
LoadingElementId = "ajax-loader"
})</h3>
}
else
{
foreach (var photo in Model.ReferencePhotos)
{
<ul class="thumbnails">
<li class="span8">
<a href="#" class="thumbnail">
<img src="#Url.Action("GetReference", "Tenants", new { id = photo.ImageId })" alt="#photo.Name") />
</a>
</li>
<li class="span4 btn-group btn-group-vertical">
Edit
#Ajax.ActionLink("Delete", "DeleteReference",
new { id = photo.ImageId },
new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", UpdateTargetId = "tenant-reference-photos" },
new { #class = "btn btn-primary" })
</li>
</ul>
}
}
</div>
Even if there are several photos in the collection and one is deleted, the exception is still thrown at the line mentioned previously. Some help would be appreciated.
How do I resolve the above error?
I've had some trouble with jQuery $ not being defined in partials even
though it's there in the source
I believe this is in connection with your problem. When I encounter this on a partial view, what always works for me is to include it in a script section. So on your _Layout.cshtml where you write your scripts you can add this:
// Do this after you referenced jquery
#RenderSection("scripts", required: false)
<script>
// some script references
</script>
Then on your view you do this:
#section scripts {
$(function() {
<script>
function onDeleteReferenceSuccess(data, status, xhr) {
if (data.error) { /* handle error */ }
else {
window.location.reload(true);
}
}
</script>
});
}
Once above is resolved, how do I update the only the DIV?
Since you deleted the item I would assume you would want to delete the div - that represents the item.
If my assumption is correct then why not give it an Id so you can delete it afterwards in your else statement:
else {
// instead of this -> window.location.reload(true);
$("#id_of_the_div").remove();
}
UPDATE:
Your updated question throws off my original answer. But let me include the updated answer here first (and then clean-up afterwards). So the reason you are having a null reference error is because you are not returning the model:
refPhotoRepository.Delete(id);
refPhotoRepository.Save();
// build the model here and pass it to the partial view
// this will most probably involve querying your data store again
var model = goBuildTheModel();
return PartialView("_TenantReferencePhotosPartial", model;
I have the following code in a MVC c# Razor view:
#{string url = "/Projects/_MonthRangesScriptsPartial";}
#using (Ajax.BeginForm(
"_MonthRanges",
"Projects",
new { id = ViewBag.InputResourceID },
new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "MonthRanges",
InsertionMode = InsertionMode.Replace,
OnComplete = "updategraphArrayScript(#url,#ViewBag.InputResourceID)"
}))
The code works almost perfectly, but I would like to resolve the c# variables to their values in the OnComplete line.
That is to say, the Razor engin should resolve the code to (for example):
<form action="/Projects/_MonthRanges/55"
data-ajax="true" data-ajax-complete="updategraphArrayScript(/Projects/assets,55)"
...>
Rather than:
<form action="/Projects/_MonthRanges/55"
data-ajax="true" data-ajax-complete="updategraphArrayScript(#url,#ViewBag.InputResourceID)"
...>
Simply create another variable string:
#{
string url = "/Projects/_MonthRangesScriptsPartial";
string onComplete = String.Format("updategraphArrayScript({0}, {1})", url, ViewBag.InputResourceID);
}
#using (Ajax.BeginForm(
"_MonthRanges",
"Projects",
new { id = ViewBag.InputResourceID },
new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "MonthRanges",
InsertionMode = InsertionMode.Replace,
OnComplete = #onComplete
}))
Take off the # when you assign your OnComplete value. You don't need it since you're already in the context of server side code.
# is used to switch html rendering context to server side code, and <text></text> is used to switch from server side context to html rendering. In example given above, you're already in the server side code context of the BeginForm() method so the # is not needed. You're not writing the value of onComplete to the page, BeginForm() will handle all that.
I have email function that send 5 person at one time. I call this function in controller called submitemail. I want to put loading when the email function triggered. I want to show message to user that email sending is successful or not. How to do it?
Call your controller's action with ajax...show a loading image before ajax call...and hide the loading image in oncomplete event of ajax function...and also show appropriate success and error message.
You can use
#using (Ajax.BeginForm("ActionName", null, new AjaxOptions
{UpdateTargetId = "dialog-model", InsertionMode = InsertionMode.Replace
}, new { id ="AjaxForm" }))
{
...form here
}
AjaxOptions is an object that can get succes in succes you can show message
new AjaxOptions()
{
UpdateTargetId = "divPlaceholder",
InsertionMode = InsertionMode.Replace,
OnSuccess = "alert('OnSuccess')",
OnBegin = "alert('OnBegin')",
OnComplete = "alert('OnComplete')",
OnFailure = "alert('OnFailure')"
}
How do I create a default AjaxOptions? For example, I have a menu with some links, I want to make the entire website to use the same loading element and same error handling.
#Ajax.ActionLink("Home", "Index", "home", <AjaxOptions>)
new AjaxOptions()
{
OnFailure = "handleError",
LoadingElementId = "loading"
});
But then I have some links that update the content and I want to set UpdateTargetId for each of those links. How can I keep a default error handling and loading element on all the views and edit only UpdateTargetId or OnSuccess (or another property) for each link?
Something like
#Ajax.ActionLink("home", "Index", "home", ajaxOption.UpdateTargetId = "content")
#Ajax.ActionLink("menu", "Foo", "home", ajaxOption.UpdateTargetId = "side-content")
I want something equivalent to jQuery.setup where I can set the default values to ajax requests and when I make an ajax request I only tell the parameters I want to override...
You could do something like:
public static class AjaxExtensions
{
public static IHtmlString DefaultLink(this AjaxHelper helper, string text,
string action, string controller, string updateTargetId = "",
string onSuccess = "")
{
// Build your link here eventually using
// the arguments passed
var options = new AjaxOptions
{
OnSuccess = onSuccess,
UpdateTargetId = updateTargetId,
OnFailure = "handleError",
LoadingElementId = "loading"
// etc...
}
// return a normal ActionLink passing your options
return helper.ActionLink(text, action, controller, options);
}
}
Note I'm using optional parameters in the signature to benefit from the flexibility of multiple overloads without the nuisance of maintaining them. Expand as needed :)
Then just use it as follows:
#Ajax.DefaultLink("home", "Index", "home", updateTargetId: "content")
I found it easier to inherit the AjaxOptions class and instantiate DefaultAjaxOptions in my view. This means you can use it for things like Ajax.ImageActionLink if you have one of those without creating a separate extension method. See example below, the only thing I needed to change was the target so I allowed this to be passed into the constructor.
public class DefaultAjaxOptions : AjaxOptions
{
public DefaultAjaxOptions(string target)
{
InsertionMode = InsertionMode.Replace;
UpdateTargetId = target;
OnBegin = "BeginLoadingSection()";
OnFailure = "FailureLoadingSection()";
OnSuccess = "SuccessLoadingSection()";
}
}
then in the view use:
#{ var ajaxOptions = new DefaultAjaxOptions("name-of-content-div"); }