I have an HTML <TABLE> displaying a list of items in the rows of the table. To add a new item to the list of items I have a form which submits the data to my controller via AJAX using Ajax.BeginForm. Once the action on the controller has finished it returns a partial view containing the markup for a new row to append to my table (eg. <TR><TD>.......</TD></TR>). My question is how do I add the new row my existing table?
I have create an at the top of the as my header with the id "userrightsgridheader" and specified my Ajax.BeginForm as follows:
<% using (Ajax.BeginForm(
"CreateUserRight",
new { workstationId = Model.Id },
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "userrightsgridheader"
}
))
{ %>
The problem is that this does not work. Does anyone have any ideas on how to achieve this?
Thanks!
You can add the following AjaxOption, this executes 'jsfunction' when the Ajax functionality executed successfully:
new AjaxOptions { OnSuccess = "jsfunction" };
You can add the tablerow in the jsfunction.
update
you can define jsfunction as follows:
function jsfunction(ajaxContext) {
//ajaxContext contains the responseText
}
AjaxContext is defined as follows:
AjaxContext ajaxContext = new AjaxContext(request, updateElement, loadingElement, ajaxOptions.InsertionMode);
Related
without refresh
When I click on any page link of the partial view, that related page should be displayed in the render body part without any page refresh. How can I do that ?
You can use the AJAX helper that is used in conjunction with unobtrusive ajax
You can find more information at this page
Install Microsoft.jQuery.Unobtrusive.Ajax NuGet package
Include the script on _Layout <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
You then use the HTML Helper with specific options
Code Sample
#Ajax.ActionLink("View All Student Info", "AllStudent", "Home", new AjaxOptions
{
UpdateTargetId = "divAllStudent",
OnBegin = "fnOnBegin",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
LoadingElementId = "imgloader",
OnSuccess= "fnSuccess",
Confirm="Do you want to get all student info ?????"
},
new { #class = "btn btn-default" })
Then in the controller add a specific [GET] Route (WebAPI)
Code Sample
[HttpGet]
public PartialViewResult AllStudent()
{
using (TempEntities db = new TempEntities())
{
var objAllStudent = db.StudentInfoes.ToList();
return PartialView("AllStudent", objAllStudent);
}
}
The options UpdateTargetId is the HTML ID container of where the AJAX result will put the result content. Usually you want to use Replace. You can use OnBegin and OnSuccess as Javascript methods that do things like show loaders, hide loaders, etc etc
I am creating an umbraco MVC page that has a template that renders a partial view with 2 dropdown lists and a google map. When i load the page it has the master template with a partial view and it shows everything fine.
When i select an item from the dropdown list i pass the value to the controler and return to the partial view other data. The problem is that the return of the partial view shows only the partial view without the master template.
This is the code in the controller that fires when the page is loaded for the first time and this shows everything:
public ActionResult RenderForm()
{
MapFunction MF = new MapFunction ();
MF=SetDropDown();
MF.JSON= SetMarkers();
return PartialView(Partial_View_Folder + "_MapFunction .cshtml", MF);
}
This is the code that fires when the dropdown is selected:
public ActionResult ChangeATM(MapFunction mapFunction)
{
MapFunction MF = new MapFunction ();
mapFunction= ddlATMChange(mapFunction.PTT.ToString());
MF = SetDropDown();
mapFunction.Branch = MF.Branch;
mapFunction.ATM = MF.ATM;
return PartialView(Partial_View_Folder + "_MapFunction.cshtml", mapFunction);
}
My partial view code with the ddl:
#using (Html.BeginUmbracoForm("ChangeATM", "MapFunction", FormMethod.Post))
{
#Html.DropDownListFor(m => m.PTT, Model.Branch, "Chose...", new { onchange = "this.form.submit();" })
}
I just want to refresh the partial view without loosing my master page and with new data.
The problem was the BeginUmbracoForm in the PartialView.
I changed it to:
#using (Ajax.BeginForm("ChangeATM", "MapFunction", null, new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "Parent"
}))
{
#Html.DropDownListFor(m => m.PTT, Model.Branch, "Choose...", new { onchange = "$(this.form).submit();", id = "use-ajax1" })
}
Also you need to add the folowing script to the master page:
<script type="text/javascript">
$(document).on("change", ".use-ajax1", function (e) {
e.preventDefault();
var form = $(this).closest('form');
$(form).submit();
});
The script prevents default actions and submits form using AjaxCall.
I am using an Ajax.BeginForm inside a For Loop with allows me to post to the controller for each item in a the for loop. Initially the for loop renders each item correctly on page load.
When the controller has handled the incoming View Model which is populated correctly from the Ajax.BeginForm, the method sends back a newly generated view model back to the View, however each item in my For loop is now duplicated, and all the textboxes now have the value of the submitted View Model.
Very confused how to structure this code correctly, I need the submission to work based on ajax and partial view. I did look at using JQuery to submit, but was concerned I may lose the AntiForgeryToken and that the Ajax.BeginForm was a more elegant approach. Any help greatly appreciated. Happy to provide more information also.
VIEW
#model Namespace.Models.MyParentViewModel
#for (int i = 0; i < Model.Items.Count; i++)
{
using (Ajax.BeginForm("SaveItem", "Controller", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "pensions" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div>
#Html.Hidden("ID", Model.Items[i].ID)
#Html.TextBox("TheName", Model.Items[i].TheName, new { #class = "form-control", #id = "item-" + i})
<input type="submit" value="Save" class="btn save" name="Command" />
</div>
}
}
CONTROLLER
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("SaveItem")]
public ActionResult SaveItem(MyItemViewModel mivm, string Command)
{
if (ModelState.IsValid)
{
#do some logic
}
// Return a newly populated MyViewModel with updated mivm.
var model = PopulateMyParentViewModel();
return PartialView("_MyPartialView", model);
}
private MyParentViewModel PopulateMyParentViewModel()
{
List<MyItemsViewModel> lstItems = new List<MyItemsViewModel>();
foreach (var item in enity.items.OrderBy(p => p.ID).ToList())
{
var ExistingItem = new MyItemViewModel
{
ID = item.ID,
TheName = item.TheName
};
lstItems.Add(MyItemViewModel);
}
MyParentViewModel.Items = lstItems;
return MyParentViewModel
}
I am not sure what is "pensions" in your code, I guess it is a div surrounding every form generated by the loop? If so your problem is the following:
First, if none of the items are interrelated (that is, triggering some update on an item causes another item to be updated as well), you shouldn't waste resources to send the whole list back to the browser - consider just sending back the updated display of the affected "record" and just update the corresponding item with the response.
What causes items to be duplicated: the AjaxOptions class has a property InsertionMode, and as I can recall, its default value is InsertionMode = InsertionMode.InsertAfter, which causes the duplicates to appear. Why? You send an Ajax request to the server, which sends back an HTML fragment populated by the entire list, instead of just one item, then the browser appends that fragment to the existing records.
Solution
If you remake your project to just send back one record instead of all of them, the just add an uniquely identified (= has an unique id attribute) <div> element around the using(...) block, set InsertionMode to InsertionMode=InsertionMode.Replace and set the UpdateTargetId property to the id of that <div>, like so:
<div id="record-#i">
#using (Ajax.BeginForm("SaveItem", "Controller", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "record-" + i.ToString(), InsertionMode = InsertionMode.Replace }))
{
// ...
}
</div>
That will cause the Ajax response to replace the contents of the container from which the request is sent (by contents I mean that the wrapping element itself is not replaced! You would need to use InsertionMode.ReplaceWith for that behavior). If you mistake this, you end up dumping crazy amounts of nested elements inside the host container, possibly breaking scripts and/or styles which use very specific selectors.
If you stick to sending a whole collection of items back, then just wrap the for loop with a <div> tag, give it an id, set InsertionMode to InsertionMode=InsertionMode.Replace, and the UpdateTargetId property to the id of that <div>.
--------- Update -----------
To answer your comment:
Assume you have a view which - for the sake of simplicity - displays records in a tabular format. Then, the result looks like this:
Name | Age | Salary | Hide
------------------------------------
Peter | 32 | $15k | Hide button
Eva | 28 | $12k | Hide button
Assume what you change with an Ajax request is that whenever you hide a record, you send back a single table row with a button that now displays 'show' instead of 'hide' and the same data about the person which belongs to the button that triggered the request.
Then, when you make a direct (=non-ajax, when you directly navigate to the page) request, you do this:
#model PeopleCollection
...
<table>
<thead>
<tr>
<td>Name</td>
<td>Age</td>
<td>Salary</td>
<td>Hide</td>
</tr>
</thead>
<tbody>
#foreach (Person record in Model) {
#Html.Partial("TableRow_Partial",record)
}
</tbody>
The "TableRow_Partial" view looks like this:
#model Person
#{
AjaxOptions opts = new AjaxOptions() {
UpdateTargetId = "person-row-" + Model.Id.ToString(),
InsertionMode = InsertionMode.ReplaceWith
// etc
};
}
...
<tr id="person-row-#Model.Id">
<td>#Model.Name</td>
<td>#Model.Age</td>
<td>#Model.Salary</td>
<td>
<!-- configure the BeginForm(..) call with the action name and a routeValue anonymous object as "new { id = Model.Id}" -->
#using (Ajax.BeginForm(...)) {
<input type="submit" value="#(Model.IsHidden ? "Show" : "Hide")" />
}
</td>
</tr>
This will simply extract the construction of loops into a separated partial view. The action method procession your Ajax-request will simply return this view, filled with the one single record you have updated, like so:
public ActionResult UpdateRecord(Int64 id)
{
var _record = Repository.Get(id);
// logic to hide and update record
return PartialView("TableRow_Partial",_record);
}
For this to work, you have to set InsertionMode to InsertionMode.ReplaceWith. All this does is that when you send an ajax request to do something with just one record, you only refresh one item - the one that triggered the ajax call, and then you simply return the new row (One row!) and replace the old one with this new one.
If you really want to stick to sending the whole collection of items back to the client every time you do something with the records, just send the entire rendered table to the client and replace the old one with the new one in a similar manner.
Main view contains #Ajax.ActionLink links that updates a div target with partialViews. All works fine.
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "divCurrentView", InsertionMode = InsertionMode.Replace })
<div id="divCurrentView"></div>
Question is: When displaying a partialview and on click currently I update data and return to the same partial view that reloads the data. also reloads dropdownlists..etc `
Calling Partial View:
public PartialViewResult MemberInfo(){
.......
ViewBag.Members= new SelectList(_db.Members, "Id", "Text");
return PartialView("_MemberInfo",model);
}
[HttpPost]
public PartialViewResult MemberInfo(InformationVM model){
....... update data
//if I dont include the ViewBag again it will fail on reload
ViewBag.Members= new SelectList(_db.Members, "Id", "Text");
return PartialView("_MemberInfo",model);
}
instead of return to the same PartialView can I just show a message on the screen "data was updated" within the partial view.?
When you define your ActionLink, you can choose the element that gets replaced:
#Ajax.ActionLink("Do something", "_Methodname", new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "StatusMessage",
InsertionMode = InsertionMode.Replace,
}, new {#class = "text-danger"})
<span id="StatusMessage"></span>
This way, the message gets loaded into the span element.
In that case, I'd include the actionlink inside the div and replace the content of the div (including the actionlink) with the partial, this time including another (different) actionlink.
<div id="divCurrentView">
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions() { HttpMethod = "GET", UpdateTargetId = "divCurrentView", InsertionMode = InsertionMode.Replace })
</div>
Partial:
<div id="divCurrentView">
#Ajax.ActionLink("Update", "MemberInfo", new AjaxOptions {
HttpMethod = "POST", UpdateTargetId = "StatusMessage", InsertionMode = InsertionMode.Replace})
<span id="StatusMessage"></span>
</div>
That way, the second (and subsequent) clicks will POST and use the different method in the controller.
I have the following issue.
My url structure is like this:
/people/edit/usercode
In my controller i have the following:
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult LoanRefresh(string id)
{
PeopleModel p = new PeopleModel();
return PartialView("_LoanHistory", p.getPersonLoanHistory(id));
}
In my view i have:
#Ajax.ActionLink("Refresh", "LoanRefresh", new { id = Model.IdentityCode }, new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "loanHistory", LoadingElementId = "Loading" }, new { #class = "button" })
and
<div id="loanHistory">
#Html.Partial("_LoanHistory", Model.Loans)
</div>
When run the Ajax.ActionLink it gets the data back ok and it updates the div, but the url of the sort links on the webgrid then change their address to:
/People/LoanRefresh/AFU0006?sort=CreatedOn&sortdir=ASC
i need to stay as:
/People/Edit/AFU0006?sort=CreatedOn&sortdir=ASC
Any help would be greatly appreciated.
Well #Nick, that's because your action name is LoanRefresh and not Refresh. To do that you will probably have to do some routing or even redirect your LoanRefresh results to an action named Refresh.
Try setting ajaxUpdateContainerId to an object that is specified in the partial view, rather than an object in the view from which the partial view is originally called. The sort URLs should work correctly then.