I'm trying to write CRUD operations using ajax. Here some code:
These are my View classes:
//PhotoSummary
#model PhotoAlbum.WEB.Models.PhotoViewModel
<div class="well">
<h3>
<strong>#Model.Name</strong>
<span class="pull-right label label-primary">#Model.AverageRaiting.ToString("# stars")</span>
</h3>
<span class="lead">#Model.Description</span>
#Html.DialogFormLink("Update", Url.Action("UpdatePhoto", new {photoId = #Model.PhotoId}), "Update Photo", #Model.PhotoId.ToString(), Url.Action("Photo"))
</div>
//Main View
#model PhotoAlbum.WEB.Models.PhotoListViewModel
#{
ViewBag.Title = "My Photos";
}
#foreach (var p in #Model.Photos)
{
<div id=#p.PhotoId>
#Html.Action("Photo", new {photo = p})
</div>
}
The sript:
$('.dialogLink').on('click', function () {
var element = $(this);
var dialogTitle = element.attr('data-dialog-title');
var updateTargetId = '#' + element.attr('data-update-target-id');
var updateUrl = element.attr('data-update-url');
var dialogId = 'uniqueName-' + Math.floor(Math.random() * 1000)
var dialogDiv = "<div id='" + dialogId + "'></div>";
$(dialogDiv).load(this.href, function () {
$(this).dialog({
modal: true,
resizable: false,
title: dialogTitle,
close: function () { $(this).empty(); },
buttons: {
"Save": function () {
// Manually submit the form
var form = $('form', this);
$(form).submit();
},
"Cancel": function () { $(this).dialog('close'); }
}
});
$.validator.unobtrusive.parse(this);
wireUpForm(this, updateTargetId, updateUrl);
});
return false;
});});
function wireUpForm(dialog, updateTargetId, updateUrl) {
$('form', dialog).submit(function () {
if (!$(this).valid())
return false;
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$(dialog).dialog('close');
$(updateTargetId).load(updateUrl);
} else {
$(dialog).html(result);
$.validator.unobtrusive.parse(dialog);
wireUpForm(dialog, updateTargetId, updateUrl);
}
}
});
return false;
});
}
And here my Tag builder:
public static MvcHtmlString DialogFormLink(this HtmlHelper htmlHelper, string linkText, string dialogContentUrl,
string dialogTitle, string updateTargetId, string updateUrl)
{
TagBuilder builder = new TagBuilder("a");
builder.SetInnerText(linkText);
builder.Attributes.Add("href", dialogContentUrl);
builder.Attributes.Add("data-dialog-title", dialogTitle);
builder.Attributes.Add("data-update-target-id", updateTargetId);
builder.Attributes.Add("data-update-url", updateUrl);
builder.AddCssClass("dialogLink");
return new MvcHtmlString(builder.ToString());
}
So, I have major problem if the dialog was called twice without the calling page being refreshed:
it just redirects me to the action page.
The question is how to update #Html.Action without reloading the page?
Could anyone help me?
Your #foreach loop in the main view is generating a partial view for each Photo which in turn is creating a link with class="dialogLink".
Your script handles the click event of these links and replaces it with a new link with class="dialogLink". But the new link does not have a .click() handler so clicking on the new (replacement) link does not activate your script.
Instead you need to use event delegation to handle events for dynamically generated content using the .on() method (refer also here for more information on event delegation). Note also that your current use of $('.dialogLink').on('click', function () { is the equivalent of $('.dialogLink').click(function () { and is not using event delegation. It attaches a handler to elements that exist in the DOM at the time the page is loaded, not to elements that might be added in the future.
Change your html to
<div id="photos">
#foreach (var p in #Model.Photos)
{
<div class="photo">#Html.Action("Photo", new { photo = p })</div>
}
</div>
and then modify the script to
$('#photos').on('click', '.dialogLink', function() {
....
});
Side note: There is no real need to add an id=#p.PhotoId to the containing div element and you could use <div class="photo"> as per above, and then reference it by using var updateTargetId = $(this).closest('.photo'); and delete the builder.Attributes.Add("data-update-target-id", updateTargetId); line of code from your DialogFormLink() method
Related
I have the following code:
#Ajax.ActionLink("Settings", "SettingsPopup", "Settings",
new { area = "Customer" },
new AjaxOptions()
{
HttpMethod = "Get",
UpdateTargetId = "settings-content",
InsertionMode = InsertionMode.Replace,
OnSuccess = "settingsPopupLoaded",
AllowCache = true
},
new { #class = "profile-right__a icon-help" })
I need to add <i class="sub"></i> element inside this liks as:
<i class="sub"></i>
How to do this?
When you want to have customized markup but still want the ajaxy behavior. You can simply use jQuery to wire up that (That is what the ajax helpers also does)
<a class="ajaxy" targetid="settings-content"
href="#Url.Action("settingsPopup","Settings",new { area="Customer"})">
<span class="glyphicon glyphicon-user text-#userLevel"></span>
</a>
The javascript will be quite simply, simply look for the elements with the css class "ajaxy", make an ajax call using jQuery $.get method and update the DOM element with the result coming back.
function settingsPopupLoaded(e) {
console.log('settingsPopupLoaded', e);
}
$(function () {
$(".ajaxy").click(function (e) {
e.preventDefault();
var _this = $(this);
$.get(_this.attr("href"), function (res) {
var tId = _this.attr("targetid");
$("#" + tId).html(res);
settingsPopupLoaded(res);
});
});
});
You can also use $.load method if it is simply updating the DOM element with the response from the ajax call.
I am using pagedList it is working but i need to have an ajax call to bring a new page i don't know how to do.
public ActionResult ApplicantsRecord(int page =1)
{
List<ApplicantsRecord> ar = new List<ApplicantsRecord>();
ApplicantsRecord a = new ApplicantsRecord();
List<ApplicantsRecordDetailViewModel> apvmlist = new List<ApplicantsRecordDetailViewModel>();
ApplicantsRecordDetailViewModel apvm = new ApplicantsRecordDetailViewModel();
//ar = db.ApplicantsRecords.ToList();
var groupedAR = db.ApplicantsRecords.GroupBy(x => x.SessionId)
.Select(y => new
{
SessionId = y.Key,
ApplicationsRecords = y.FirstOrDefault(),
}).ToList().OrderByDescending(x => x.ApplicationsRecords.LoginDate);
foreach (var i in groupedAR)
{
ar.Add(i.ApplicationsRecords);
}
if(Request.IsAjaxRequest())
{
return PartialView("_ApplicantsRecord", ar.ToPagedList(page, 10));
}
return View(ar.ToPagedList(page, 10));
}
and here is the view code
<div id="pagerecord">
#Html.Partial("_ApplicantsRecord");
</div>
<script src="~/Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var getpage = function () {
var $a = $(this);
var options = {
url: $a.attr("href"),
type: "get"
};
$.ajax(options).done(function (data) {
var target = $a.parents("div.pagedList").attr("next");
$(target).replaceWith(data);
});
return false;
};
$('main-conten').on('click', '.pagedList', getpage)
});
</script>
and here is the partial view
<table>
<thead>
<tr>
<th width="220" align="center">
<div class="pagedList" data-back-btn-text="next">
#Html.PagedListPager(Model, page => Url.Action("ApplicantsRecord", new { page }),
PagedListRenderOptions.MinimalWithItemCountText)
</div>
</th>
#*<th width="220" align="center">#Html.DisplayNameFor(model => model.UserName)</th>
<th width="220" align="center">#Html.DisplayNameFor(model => model.LoginDate)</th>
<th width="220" align="center">Details</th>*#
</tr>
</thead>
Did you try this already?
var getpage = function () {
var $a = $(this);
$.ajax({
url: $a.attr("href"),
type: "get",
success: function(data) {
var target = $a.parents("div.pagedList").attr("next");
$(target).replaceWith(data);
},
error: function() {
return false;
}
});
};
Next what you need tot do is:
Try to figure out if the data you are receiving is really the data you are expecting.
See if you do not enter the error function.
The first thing wrong is your selector is not a valid selector. $('main-conten') should probably be $('#main-conten') (assuming it is an element with an id of main-conten).
Secondly, you are using a delegated event handler, but listening for clicks on the .pagedList element and not the anchors within it:
$('main-conten').on('click', '.pagedList', getpage)
but inside the handler you are expecting the anchor to have been clicked as you are using:
var $a = $(this);
and
$a.attr("href")
Try something like this:
$(function () {
// Listen for clicks on anchors inside the paging panel
$('#main-conten').on('click', '.pagedList a', function () {
var $a = $(this);
var options = {
url: $a.attr("href"),
type: "get"
};
$.ajax(options).done(function (data) {
// Do something with the returned data
var target = $a.closest("div.pagedList").attr("next");
$(target).replaceWith(data);
});
return false;
});
});
Notes:
$(function () {YOUR CODE HERE}); is just a handy shortcut for $(document).ready(function(){});
Use closest() in preference to parents() where you only expect a single matching ancestor.
If all this still does not work, please post your HTML output (not source) as saved from the browser.
I need to be able to populate data into a <div> or some other sort of section from an object after the corresponding string has been selected from a drop down list (lazy loading).
When a chnage is made in the dropdownlist, I want the method in my controller to be called which will fill in <div id=result></div> with the output from the method.
Perhaps I am approaching this problem wrong.
I suspect the problem is in my JavaScript.
Here is my approach:
View:
<div>#Html.DropDownList("MyDDL") </div>
<br>
<div id="result"></div>
JavaScript:
<script type ="text/javascript">
$(document).ready(function () {
$("#MyDDL").change(function () {
var strSelected = "";
$("#MyDDL option:selected").each(function () {
strSelected += $(this)[0].value;
});
var url = "HomeController/showInfo";
//I suspect this is not completely correct:
$.post(url, {str: strSelected},function (result) {
$("result").html(result);
});
});
});
</script>
Controller (Perhaps I shouldn't be using PartialViewResult):
public ActionResult Index()
{
List<string> myList = new List<string>();
List<SelectListItem> MyDDL = new List<SelectListItem>();
myList.Add("Tim");
myList.Add("Joe");
myList.Add("Jim");
//fill MyDDL with items from myList
MyDDL = myList
.Select(x => new SelectListItem { Text = x, Value = x })
.ToList();
ViewData["MyDDL"] = MyDDL;
return View();
}
[HttpPost]
public PartialViewResult showInfo(string str)
{
Person p = new Person(str); //name is passed to constructor
p.LoadInfo(); //database access in Person Model
ViewBag.Info = p.Info;
return PartialView("_result");
}
_result.cshtml:
<p>
#ViewBag.Info
</p>
Thanks You.
Change your script a little bit. Missing a # in the jQuery selecter for result div . Use the code given below
$.post(url, {str: strSelected},function (result) {
$("#result").html(result);
});
In my opinion if the javascript are in local don't need put $.post(url, {str: strSelected},function (result) {
You can use
//I suspect this is not completely correct:
$("#result").html(result);
try it
Did you try debugging p.LoadInfo() if it has any value? I also have some suggestions for your script:
Try adding keyup in your event so you can get the value in cases when the arrow keypad is used insted of clicking:
$("#MyDDL").on("change keyup", function () {
// you can get the dropdown value with this
var strSelected = $(this).val();
So I made the following changes and it worked:
View:
<div><%= Html.DropDownList("MyDDL") %> </div>
<br>
<span></span>
JavaScript:
<script type ="text/javascript">
$(document).ready(function () {
$("#MyDDL").change(function () {
var strSelected = $("#MyDDL option:selected").text();
var url = "/Home/showInfo";
$.post(url, {str: strSelected},function (result) {
$("span").html(result);
});
});
});
_result.cshtml:
#ViewBag.Info
The Controller was left unchanged.
In the code below, I have a form with some informations about an employee. I'd like feel in the password and post. If the password is correct the user is redirected to /Dashboard that it's ok.
When the password is wrong, I'd like show the error (_ErrorPartial) in MyError div and still see the form. I'd like see the form and the message in the div (MyError). With the code below, when the password is wrong, I see the error message but on a blank page and not a page with the layout + the form + the error message
Any idea ?
Thanks,
My page :
#model MyApp.EmployeeModel
<script type="text/javascript">
$(document).ready(function () {
$(".").click(function () {
$.ajax({
url: '/Account/Login',
type: "Post",
data: $('#MyForm').serialize(),
success: function (result) {
alert('Im here');
$("#MyError").html(result);
//Here is code to detect error or not but removed
}
});
});
});
</script>
<div id="MyError"></div>
#using (Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "MyForm" }))
{
#Html.AntiForgeryToken()
<div>
#Html.LabelFor( m => m.FirstName)
<div>
#Html.HiddenFor(model => model.Username)
#Html.LabelFor( m => m.Password)
#Html.PasswordFor(m => m.Password)
<input type="submit" class="jqClick" />
</div>
</div>
}
Controller :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(EmployeeModel employeeModel)
{
var Errors = new List<string>();
var employee = .... //Get employee from DB
if (employee == null)
{
Errors.Add("Employee trouble");
return PartialView("_ErrorPartial", Errors.AsEnumerable());
}
return RedirectToAction("Index", "Dashboard");
}
My _ErrorPartial view :
#model IEnumerable<string>
<div id="DisplayError">
#{ var error = string.Empty; }
#foreach (var err in Model){
error += err + "<br>";
}
#Html.Raw(error)
</div>
You have to make some changes to your javascript code. First of all, replace jQuery selector $(".") with specific one, like $(".jqClick"). Second of all, never hardcode urls like /Account/Login in your code, use UrlHelper for that. And the third thing is, you have to prevent a default behavior of the submit button. See the changes:
$(document).ready(function () {
$(".jqClick").click(function (e) {
e.preventDefault();
$.ajax({
url: '#Url.Action("Login", "Account")',
type: "Post",
data: $('#MyForm').serialize(),
success: function (result) {
alert('Im here');
if (result.redirectUrl)
window.location.href = result.redirectUrl;
else
$("#MyError").html(result);
//Here is code to detect error or not but removed
}
});
});
});
Also you need to change the Login action a little in order to make a redirect if a login is success:
public ActionResult Login(EmployeeModel employeeModel)
{
var Errors = new List<string>();
var employee = .... //Get employee from DB
if (employee == null)
{
Errors.Add("Employee trouble");
return PartialView("_ErrorPartial", Errors.AsEnumerable());
}
return Json(new { redirectUrl = Url.Action("Index", "Dashboard") });
}
The probable error that you throw from the server side, won't appear in success function. Instead you should have below
error:function(response)
{
//your code here
}
Simply throw exception from the controller.
Having a jQuery dialog issue. I want to leverage ajax to render calendar content in a dialog window when a person clicks the the calDayContentEntry div. The following code works on the first click, but after closing the dialog I can no longer get the dialog to show again for that entry. Other entries work the first time as well, but secondary clicks will not open the dialog again.
Here is relevant code that I am having the issue with (all within the same asp.net mvc 3 razor view). Does anyone have some tweaks that could fix this issue?
...
<div class="calDayContent">
#foreach (var content in day.Contents)
{
<div class="calDayContentEntry">
<input type="hidden" value="#content.Entry.Id" class="hiddenId" />
<div class="#content.DisplayClass">#content.Entry.Hours.ToString() hrs</div>
</div>
<div class="leaveRequestPopup"></div>
}
</div>
...
<script type="text/javascript">
$().ready(function () {
$('.calDayContentEntry').click(function () {
getAndShowDialogContents(this);
});
// Register close event for dialog if overlay is clicked
$('.ui-widget-overlay').live("click", function () {
//Close the dialog
$currentDialog.dialog("close");
});
});
function getAndShowDialogContents(entryDiv) {
var entryId = $(entryDiv).find('input[type="hidden"]').val();
var contentdiv = $(entryDiv).next('.leaveRequestPopup');
var x = $(entryDiv).position().left + jQuery(entryDiv).outerWidth();
var y = $(entryDiv).position().top - jQuery(document).scrollTop();
$.ajax(
{
type: 'POST',
url: 'Request/GetCalendarDetails',
data: { id: entryId },
success: function (result) {
$(contentdiv).html(result);
$(contentdiv).dialog({
autoOpen: false,
modal: true,
title: 'Details',
width: 400,
height: 300,
draggable: false
});
$(contentdiv).dialog("option", "position", [x, y]);
$currentDialog = $(contentdiv).dialog('open');
}
});
}
</script>
Is this a valid statement?
$currentDialog.dialog("close");
I think that at this point it is out of scope.
Maybe if you define it outside before the $().ready
var $currentDialog;
$().ready(function () {
...
You need to reset the dialog.
contentdiv.dialog("destroy").dialog(....
Note: if you do
var contentdiv = $(entryDiv).next('.leaveRequestPopup');
then contentdiv is already a jQuery object so you say something like:
contentdiv.click(function(){
//code here
});
You don't need to wrap it in $(contentdiv) again.
when you call this function:
getAndShowDialogContents(this);
It will pass as argument the raw document object, not a jQuery one, use:
getAndShowDialogContents($(this));
to pass the current jQuery object
I think the weekend added a fresh perspective on the issue. The code that works is below. Basically, instead of using a popup div for every entry, I just used one div at the end of my page. That div is reused for every dialog. I use the global variable so I can refer to it when someone clicks outside the dialog to close it. Hope this helps someone else out.
...
<div class="calDayContent">
#foreach (var content in day.Contents)
{
<div class="calDayContentEntry">
<input type="hidden" value="#content.Entry.Id" class="hiddenId" />
<div class="#content.DisplayClass">#content.Entry.Hours.ToString() hrs</div>
</div>
}
</div>
...
<div class="leaveRequestPopup"></div>
...
<script type="text/javascript">
$().ready(function () {
$('.calDayContentEntry').click(function () {
getAndShowDialogContents(this);
});
// Register close event for dialog if overlay is clicked
$('.ui-widget-overlay').live("click", function () {
//Close the dialog
$currentDialog.dialog("close");
});
$currentDialog = $('.leaveRequestPopup').dialog({
autoOpen: false,
modal: true,
title: 'Details',
width: 400,
height: 300,
draggable: false
});
});
function getAndShowDialogContents(entryDiv) {
var entryId = $(entryDiv).find('input[type="hidden"]').val();
var x = $(entryDiv).position().left + jQuery(entryDiv).outerWidth();
var y = $(entryDiv).position().top - jQuery(document).scrollTop();
$.ajax(
{
type: 'POST',
url: 'Request/GetCalendarDetails',
data: { id: entryId },
success: function (result) {
$currentDialog.html(result);
$currentDialog.dialog("option", "position", [x, y]);
$currentDialog.dialog('open');
}
});
}
</script>