I have a panelbar with some items and I want to set the action associated to them to be performed by Ajax.
Example code:
So far I have this (no ajax):
#(Html.Kendo().PanelBar()
.Name("left-menu-module")
.Items(items =>
{
items.Add()
.Text("<div class=\"text-item-container\"><span class=\"left-menu-module-level1-text\">" + "item1" + "</span></div>").Encoded(false)
.ImageUrl("link to an icon")
.ImageHtmlAttributes(new { width = 30 })
.Action("Action1", "Controller");
items.Add()
.Text("<div class=\"text-item-container\"><span class=\"left-menu-module-level1-text\">" + "item2" + "</span></div>").Encoded(false)
.ImageUrl("link to an icon")
.ImageHtmlAttributes(new { width = 30 })
.Action("Action1", "Controller");
}))
This generate something like:
//...
<li class="k-item k-state-default" role="menuitem">
<a class="k-link k-header" href="/MyController/Action1">
<img alt="image" class="k-image" src="link to an icon" width="30"><div class="text-item-container"><span class="left-menu-module-level1-text">item1</span></div>
</a>
</li>
//...
But I would like to have something like:
//...
<li class="k-item k-state-default" role="menuitem">
<a class="k-link k-header" href="/MyController/Action1" data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#target">
<img alt="image" class="k-image" src="link to an icon" width="30"><div class="text-item-container"><span class="left-menu-module-level1-text">item1</span></div>
</a>
</li>
//...
So, it's something similar to Ajax.ActionLink() helper.
How can I achieve that?
I have found solution for this..
Use content section inside div.
#(Html.Kendo().PanelBar()
.Name("panelbar")
.ExpandMode(PanelBarExpandMode.Single)
.Items(panelbar =>
{
panelbar.Add().Text("Client Info")
.Expanded(true)
.Content(
#<div>
#Ajax.ActionLink("Organization Detail", "OrganizationDetail", "SetupSystem", null, new AjaxOptions { UpdateTargetId = "divBody", LoadingElementId = "divLoading" }, new { id = "btnOrgDetails", #class = "list-group-item" })
#Ajax.ActionLink("Benefit Center", "GetBenefitsOverview", "SetupSystem", null, new AjaxOptions { UpdateTargetId = "divBody", LoadingElementId = "divLoading" }, new { id = "btnBenefitCenter", #class = "list-group-item"})
#Ajax.ActionLink("Services", "ServiceFilter", "SetupSystem", null, new AjaxOptions { UpdateTargetId = "divBody", LoadingElementId = "divLoading" }, new { id = "btnServices", #class = "list-group-item" })
#Ajax.ActionLink("Key Dates", "GetKeyDatesOverview", "SetupSystem", null, new AjaxOptions { UpdateTargetId = "divBody", LoadingElementId = "divLoading" }, new { id = "btnKeyDates", #class = "list-group-item" })
</div>);
}); )
I actually solved this using a function that was added in one of the recent Telerik updates:
#(Html.Kendo().PanelBar()
.Name("left-menu-module")
.Items(items =>
{
items.Add()
.Text("<div class=\"text-item-container\"><span class=\"left-menu-module-level1-text\">" + "item1" + "</span></div>").Encoded(false)
.ImageUrl("link to an icon")
.ImageHtmlAttributes(new { width = 30 })
.Action("Action1", "Controller")
.LinkHtmlAttributes(/* Anonymous object OR Dictionary with the data- attributes */);
}))
I'm using kendo version 2014.3.1316.440.
Related
I'm new to MVC. I have a view which displays the products attached to a Quote (the QuoteDetails). I also have an Ajax.ActionLink)_ for "Add product" which loads a partial view for another product to be entered. The problem is that when the partial view is loaded, edits to the other products not in the partial view are not saved. If no partial view is loaded, edits to the listed products are saved just fine.
Here is the relevant code for the main view:
#model CMSUsersAndRoles.Models.QuoteViewModel
....
#Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="~/Scripts/jquery.mask.min.js"></script>
#using (Html.BeginForm())
{
....
#Html.HiddenFor(model => model.CustomerId)
#Html.LabelFor(model => model.QuoteId)
#Html.EditorFor(model => model.QuoteId, new { htmlAttributes = new { #readonly = "readonly", #class = "form-control" } })
#Html.ValidationMessageFor(model => model.QuoteId)
.... // more controls for properties of Quote
#Html.LabelFor(model => model.QuoteDetail)
<div id="QuoteDetails">
#for (int i = 0; i < Model.QuoteDetail.Count; i++)
{
#Html.HiddenFor(model => model.QuoteDetail[i].QuoteId, new { htmlAttributes = new { #class = "form-control" } })
....
#Html.EditorFor(model => model.QuoteDetail[i].SKU, new { htmlAttributes = new { #readonly = "readonly", #id = "SKU", #class = "form-control", style = "width: 100px" } })
#Html.EditorFor(model => model.QuoteDetail[i].Amount, new { htmlAttributes = new { #class = "form-control amount", style = "width: 95px" } })
#Html.ValidationMessageFor(model => model.QuoteDetail[i].Amount)
.... // more for controls for properties of QuoteDetail
#Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteDetail[i].QuoteId, quoteDetailId = (Model.QuoteDetail[i].QuoteDetailId) },
new AjaxOptions
{
HttpMethod = "POST",
Confirm = "Are you Sure You Want to Delete " + Model.QuoteDetail[i].ProductName,
}, new { #class = "btn btn-danger glyphicon glyphicon-trash" })
</div>
}
#Html.EditorFor(model => model.Subtotal, new { htmlAttributes = new { #class = "form-control subTotal", style = "width: 100px; float:right; clear:left; text-align:right" } })
#Ajax.ActionLink("Add product", "AddProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetail.Count + 1) },
new AjaxOptions
{
UpdateTargetId = "QuoteDetails",
InsertionMode = InsertionMode.InsertAfter
})
}
Here is the partial view:
#model CMSUsersAndRoles.Models.QuoteDetail
#{
ViewBag.Title = "EditQuoteDetail";
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
</head>
<body>
<div id="row" class="row">
<table>
#using (Html.BeginCollectionItem("quoteDetail"))
{
<tr>
#Html.HiddenFor(model => model.QuoteId, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.SKU, new { htmlAttributes = new { #readonly = "readonly", #id = "SKU", #class = "form-control", style = "width: 100px" } })
#Html.DropDownListFor(model => model.ProductId, new SelectList(ViewBag.ProductData, "ProductId", "Name"), "---Select one---", new { style = "width: 300px !important", htmlAttributes = new { #id = "ProductName", #class = "ProductList" } });
.... // more controls for properties of QuoteDetail
#Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetailId) },
new AjaxOptions
{
HttpMethod = "POST",
Confirm = "Are you Sure You Want to Delete " + Model.ProductName,
}, new { #class = "btn btn-danger glyphicon glyphicon-trash" })
</tr>
}
</table>
</div>
And here is the controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(QuoteViewModel qvm,
[Bind(Include = "CustomerId,SalesRep,FirstName,LastName,Company,Address1,Address2,City,State,PostalCode,WorkPhone,CellPhone,Email,Discount,PaymentTerms")] Customer customer,
[Bind(Include = "QuoteId,QuoteDetailId,ProductId,ProductName,Amount,ListPrice,Discount,Price")] List<QuoteDetail> quoteDetails,
[Bind(Include = "QuoteId,CustomerId,Subtotal,Tax,Total,QuoteDate,GoodUntil,QuoteSent,DateApproved,DateOrdered")] Quote quote)
{
....
}
Can anyone help with this? Any help will be much appreciated.
Your using 2 different techniques here to generate your collection which is causing the issue.
In the main view you have a for loop to generate controls for existing items which is generating the zero-based consecutive indexers, which is what the DefaultModelBinder uses by default. Your html will include name attributes that are for example
<input name="QuoteDetail[0].QuoteId"..../>
<input name="QuoteDetail[1].QuoteId"..../>
<input name="QuoteDetail[2].QuoteId"..../>
But then you add new items using the BeginCollectionItem helper method which generates the collection indexer as a Guid so that new inputs will be (where xxx is a Guid)
<input name="QuoteDetail[xxxx].QuoteId"..../>
and also includes a
<input name="QuoteDetail.Index" value="xxxx" ... />
which is used by the DefaultModelBinder to match non zero-based non consecutive indexers. You cannot use both techniques.
To solve this, you can either add an input for the indexer in the for loop
#for (int i = 0; i < Model.QuoteDetail.Count; i++)
{
....
<input type="hidden" name="QuoteDetail.Index" value="#i" />
}
or change the loop to use the partial view containing the BeginCollectionItem method in each iteration
#foreach(var item in Model.QuoteDetail)
{
#Html.Partial("xxxx", item) // replace xxxx with the name of your partial
}
I am using ajax to send values to my controller from the view:
View where information is collected to send to a different controller:
#using (Html.BeginForm(null, null, FormMethod.Get, htmlAttributes: new { id = "GenerateForm" }))
{
<div class="form-horizontal">
<div class="form-group">
#Html.Label("Choose AC:", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AC", null, "-- Select AC --", htmlAttributes: new { id = "AC", #class = "form-control" })
</div>
</div>
</div>
<div class="form-horizontal">
<div class="form-group">
#Html.Label("Choose Month:", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Months", null, "-- Select Month --", htmlAttributes: new { id = "Month", #class = "form-control" })
</div>
</div>
</div>
<div class="form-horizontal">
<div class="form-group">
#Html.Label("Year:", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Years", null, "-- Select Year --", htmlAttributes: new { id = "Year", #class = "form-control" })
</div>
</div>
</div>
<br />
<input type="submit" id="SendToController" class="btn btn-primary" value="Generate" />
}
<script type="text/javascript">
$('#SendToController').on('click', function() {
sendToController();
return false;
});
function sendToController(){
var selectedAC = $('#AC').val();
var selectedMonth = $('#Month').val();
var chosenYear = $('#Year').val();
$.ajax({
url: '/MonthReports/Generate',
data: { 'id' : selectedAC, 'monthValue' : selectedMonth, 'year' : chosenYear },
type: 'GET',
cache: false,
success: function(data){},
});
}
Controller Method:
public ActionResult Generate(int id, int monthValue, string year)
{
List<DailySum> lstDailySum = db.DailySum.Where(x => x.AID == id && x.Day.Month == monthValue + 1 && x.Day.Year.ToString() == year && x.deleted == false).ToList();
List<string> lstInc = lstDailySum.Select(x => x.codeAC.text).Distinct().ToList();
List<MReport> lstMReport = new List<MReport>();
foreach (var inc in lstInc)
{
MReport mReport = new MReport();
mReport.Inc = inc;
mReport.Count = lstDailySum.Where(x => x.codeAC.text == incident).Count();
lstMReport.Add(mReport);
}
return View(lstMReport);
}
Now the values are being passed through when the button is clicked, and the whole method works, except the View doesn't show up.. it just stays on the View where the button was originally clicked... no Generate View appears.
I have placed a breakpoint on the Generate view and it does get hit but the view doesn't show?
I don't know how else to explain it.. the cshtml code is hit with a breakpoint, but the page doesn't show.. it just stays on the page where the button was clicked.
Any help is appreciated.
Because you are using ajax, you should handle the data in the success callback of your ajax call. The way your code is written, you do nothing with the view. The View is rendered to the data variable.
Try something like this:
$.ajax({
url: '/MonthReports/Generate',
data: { 'id' : selectedAC, 'monthValue' : selectedMonth, 'year' : chosenYear },
type: 'GET',
cache: false,
success: function(data){
$("body").html(data);
}
});
I have a parent View & a child View.
When posing with the Ajax.BeginForm, I'm expecting back the entire parent view plus the results of the partial view updated. Only the results of the partial view is displayed.
In addition, the "OnSuccess" method doesn't seem to be getting hit as I'm debugging.
Can someone please tell me what I'm doing incorrecty?
Controller:
public class HomeController : Controller
{
private DAL db = new DAL();
public ActionResult Index()
{
ViewBag.Message = "Welcome to YeagerTech!";
return View();
}
// GET: Categories/Create
public ActionResult Create()
{
return View();
}
// POST: Categories/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(Category category)
{
if (ModelState.IsValid)
{
try
{
await db.AddCategoryAsync(category);
}
catch (System.Exception ex)
{
throw ex;
}
}
return View(category);
}
public PartialViewResult ShowDetails()
{
//string code = Request.Form["txtCode"];
Category cat = new Category();
//foreach (Product p in prodList)
//{
// if (p.ProdCode == code)
// {
// prod = p;
// break;
// }
//}
cat.CategoryID = 1;
cat.Description = "Financial";
return PartialView("_ShowDetails", cat);
}
}
Parent View
#model Models.Models.Category
#{
ViewBag.Title = "Create Category";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Category</h2>
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
#*<div>
#Html.ActionLink("Back to List", "Index")
#Html.Hidden("categoryCreateUrl", Url.Action("Create", "Home"))
</div>*#
#section Scripts {
<script>
$(document).ready(function ()
{
function OnSuccess(response)
{
$('#form1').trigger("reset");
}
//if (typeof contentCreateCategory == "function")
// contentCreateCategory();
});
</script>
}
Partial View
#model Models.Models.Category
<h1>Cat Details</h1>
<h2>
Cat Code: #Model.CategoryID<br />
Cat Name: #Model.Description<br />
</h2>
EDIT # 1
Parent & child view displayed (due to a missing JS file, thanks to the mention of Stephen), plus was able to programmatically clear out the form with the OnSuccess method.
I had thought since that was JS, it needed to go in the Scripts section, but did not recognize it there.
Here is the finished code.
#using (Ajax.BeginForm("ShowDetails", "Home", new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "div1",
InsertionMode = InsertionMode.Replace,
OnSuccess = "OnSuccess",
OnFailure = "OnFailure"
}, new { #id = "frm1", #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="body-content">
<h4>Category</h4>
<hr />
<div class="form-group">
<div class="col-offset-1 col-lg-11 col-md-11 col-sm-11 col-xs-11">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control", #placeholder = "Description" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-11 col-md-11 col-sm-11 col-xs-11">
<button type="submit" id="btnCategoryCreate" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Create</button>
<button type="reset" id="btnClear" class="btn btn-default"><span class="glyphicon glyphicon-eye-close"></span>Clear</button>
</div>
</div>
</div>
}
<div id="div1">
</div>
<script type="text/javascript">
function OnSuccess(response)
{
$('#frm1').trigger("reset");
}
function OnFailure(response)
{
alert("Whoops! That didn't go so well did it?");
}
</script>
A bit late for answer, but yet it might help someone else.
The issue might be that the javascript files required for Ajax.BeginForm are not loaded in your page.
Microsoft.jQuery.Unobtrusive.Ajax
Nuget package to search for
<package id="Microsoft.jQuery.Unobtrusive.Ajax" version="3.2.3" targetFramework="net45" />
of course that the version might differ.
Add reference to the page after your jQuery and it should work.
I’m using BeginForm inside of a child view. When I submit the form I’m still on the parent view but the child view goes away. I would like for the information to be submitted but the form to remain until the user wants to load another partial view or move away from the page entirely. Is there a way to prevent the page from disappearing upon submit? Here’s what my BeginForm looks like:
using (Html.BeginForm("Action", "Controller", new { fromTeacherPage = true, searchTeacher = instructorName, selectedDepartment = Model.Assignments.FirstOrDefault().departmentNumber, id = Model.Assignments.FirstOrDefault().InstructorId, strCategoryName = #ViewBag.categoryname }, FormMethod.Post, new { #name = "formName", #class = "nameOfClass" }))
{
//code for form here
<button id="submitButton" class="submitButton">Submit</button><br />
}
UPDATE:
<script type="text/javascript">
$(document).ready(function () {
$("#datep").datepicker({ showOn: "both", buttonText: "Select Date", changeMonth: true, changeYear: true, yearRange: "-2:+2", showOtherMonths: true, onSelect: function (date, datepickder) {
var sltdDate = { selectedDate: date };
$.ajax({
type: "GET",
url: "/Schedule/GetSchedule",
data: sltdDate,
datatype: "html",
success: function (data) {
$("#returnedData").html(data);
$("#returnedData #dateContainer").remove();
$("<button>Hide</button>").appendTo("#homeworkUpdateId-0")
}
});
}
});
});
</script>
#{
int? intTeacherID = Convert.ToInt32(HttpContext.Current.Session["intTeacherId"]);
string instructorName = (from x in Model.Enrollments where x.InstructorId == intTeacherID select x.InstructorFullName).FirstOrDefault();
}
<div id="dateContainer">
<label for ="datep">Date: </label><input id="datep" />
</div>
<div id="returnedData">
#if (Model.Assignments != null)
{
using (Html.BeginForm("Action", "Controller", new { fromTeacherPage = true, searchTeacher = instructorName, selectedDepartment = Model.Assignments.FirstOrDefault().departmentNumber, id = Model.Assignments.FirstOrDefault().teacherId, strCategoryName = #ViewBag.categoryname }, FormMethod.Post, new { #name = "formName", #class = "submitAttendance" }))
{
<table>
<tr>
<th>
Grade
</th>
<th>
Attendance
</th>
<th>
Clas Day
</th>
<th>
Assignment Type
</th>
<th>
Overall Grade
</th>
</tr>
#foreach (var assignment in Model.Assignments.Select((x, i) => new { Data = x, Index = i }))
{
int asgnIndex = assignment.Index;
<tr id="rowId+#asgnIndex">
<td>
<div id="homeworkUpdateId-#asgnIndex">
#Html.TextBox("HomeworkGrade", assignment.Data.HomeworkGrade.ToString(), new { style = "width:55px; text-align: center" })
</div>
</td>
</table>
<button id="submitButton" class="submitButton">Submit </button><br />
}
}
</div>
You are using
using(Html.BeginForm()){}
this will refresh the whole page if you only want to reload some section inside your view you have to use
using (Ajax.BeginForm("Action", "Controller", null, new AjaxOptions {UpdateTargetId = "divToUpdate", InsertionMode = InsertionMode.Replace, HttpMethod = "GET"}, new {id = "someIdFOrm"}))
I'm having some trouble setting up the routing to a custom controller in Orchard.
I've created a View:
#model dynamic
#{
Script.Require("jQuery");
}
#using (Html.BeginForm("Send", "Email", FormMethod.Post, new { id = "contactUsForm" }))
{
<fieldset>
<legend>Contact Us</legend>
<div class="editor-label">Name:</div>
<div class="editor-field">
#Html.TextBox("Name", "", new {style = "width: 200px"})
</div>
<div class="editor-label">Email Address:</div>
<div class="editor-field">
#Html.TextBox("Email", "", new {style = "width: 200px"})
</div>
<div class="editor-label">Telephone Number:</div>
<div class="editor-field">
#Html.TextBox("Telephone", "", new {style = "width: 200px"})
</div>
<div class="editor-label">Message:</div>
<div class="editor-field">
#Html.TextArea("Message", "", new {style = "width: 200px"})
</div>
<br/>
<input id="ContactUsSend" type="button" value="Submit" />
</fieldset>
}
#using (Script.Foot()) {
<script>
$(function() {
$('#ContactUsSend').click(function () {
alert('#Url.Action("Send", "Email")');
var formData = $("#contactUsForm").serializeArray();
$.ajax({
type: "POST",
url: '#Url.Action("Send", "Email")',
data: formData,
dataType: "json",
success: function (data) {
alert(data);
}
});
});
});
</script>
}
With a Controller:
public class EmailController : Controller
{
[HttpPost]
public ActionResult Send()
{
var orchardServices = DependencyResolver.Current.GetService<IOrchardServices>();
var messageHandler = DependencyResolver.Current.GetService<IMessageManager>();
var svc = new ContactUsService(orchardServices, messageHandler);
svc.DoSomething();
return new EmptyResult();
}
}
And setup the route:
public class Routes : IRouteProvider {
public void GetRoutes(ICollection<RouteDescriptor> routes) {
foreach (var routeDescriptor in GetRoutes()) {
routes.Add(routeDescriptor);
}
}
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor {
Priority = 15,
Route = new Route(
"ContactUsWidget",
new RouteValueDictionary {
{"area", "ContactUsWidget"},
{"controller", "Email"},
{"action", "Send"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "ContactUsWidget"}
},
new MvcRouteHandler())
}
};
}
}
But when I click the submit button, it tries to post to
OrchardLocal/Contents/Email/Send
and obviously fails. Can anyone point me in the direction of what I'm doing wrong?
Try this:
#using (Html.BeginForm("Send", "Email", new { area = "Your.Module" }, FormMethod.Post, new { id = "contactUsForm" }))
Adding the area is like an extra clause that ensures only your module is searched for a matching controller/action method pair.