MVC 3 unobtrusive javascript links in refreshed partial view results - asp.net-mvc

I have noticed that if you add a unobtrusive link in a refreshable partial view such as
<a href="/" data-delete-chapter="#Model.Chapter.ChapterId" class="delete-chapter" >
Delete
</a>
wired up in javascript file in order to do a (less ugly) Jquery popup delete confirmation):
$("[data-delete-chapter]").each(function () {
$(this).click( ...
then upon refresh of the partial view via ajax these javascript handlers dispappear so you end up having to refresh them each ajax call. You can do this with OnSuccess AjaxOption:
or directly in html as data-ajax-success such as my page previous button:
<a data-ajax="true" data-ajax-loading="#chapterAjaxImage" data-ajax-method="GET"
data-ajax-mode="replace" data-ajax-update="#chapterContainer"
href="ScrollChapterPrev/#(Model.VideoId)?pageNumber=#(Model.PageNumber)"
data-ajax-success="afterChapterRefresh()" class="gallery-prev">
<img src="#Url.Content("~/Content/Images/play/chapter-slider-prev.png")" alt="prev" />
</a>
however I feel that having
data-ajax-success="afterChapterRefresh()"
in the multiple places that refreshes my chapter list is not a lot less unobtrusive than making my original delete link use javascript thereby avoid the need to reference a refresh JS function:
<a href="javascript:deleteChapter(10)" class="delete-chapter" >
Delete
</a>
is there a better way or am I as close as unobtrusive that I can get in this special case?

Loading the content via ajax means , you are injecting something to the DOM , after the events ( click or whatever) bounded. So the newly injected elements wont have that event binding.
jQuery on would help. on will work on current elements and future elements
Change this
$("[data-delete-chapter]").each(function () {
$(this).click(function(){
//do something
});
});
to this
$(document).on("click","[data-delete-chapter]"(function(){
//do something
});

Related

How do I trigger 'ajaxStart' and 'ajaxStop' from ASP.NET MVC 5 Html.BeginForm?

I am working on a corporate ASP.NET MVC 5 website that has 159 *.cshtml View pages.
I am trying to incorporate a Wait Cursor to display whenever a query is run.
Some pages have queries that are called with jQuery/Ajax calls:
$.get("#Url.Action("SomeQuery", "ControllerN"));`
By adding the below div tag and jQuery to the Shared_ConsoleLayout.cshtml file, the Wait Cursor will show and hide anytime jQuery makes one of the Ajax calls:
<div id="waitDiv" style="position:fixed; top: 50%; left: 50%">
<p style="text-align: center">
Loading...<br />
<img src="~/Images/wait.gif" /><br />
Please Wait.
</p>
</div>
...
$(document).ready(function () {
$('#waitDiv').hide();
$(document).ajaxStart(function () {
$('#waitDiv').show();
})
$(document).ajaxStop(function () {
$('#waitDiv').hide();
});
});
But, I don't know how to show and hide the div using the ASP.NET MVC 5 Html.BeginForm syntax, and they account for over 90% of the calls on the corporate website.
#using (Html.BeginForm("SomeQuery", "ControllerN", FormMethod.Post))`
What can I do to allow the Wait Cursor to show and hide with the ASP.NET MVC 5 Html.BeginForm techniques?
The Html.BeginForm is just a syntax wrapper on the "form" tag. Submitting a form operates synchronously, and there are no such events. You can handle the submit event to show some indicator, which will be automatically hidden (overridden) within a new page HTML.
If you need to implement asynchronous data posting, you can modify your existing jQuery code - for example, use the jQuery ajax plugin instead and specify the request data manually (I guess this is why you are looking for a form usage). Otherwise, use the Ajax.BeginForm instead, which have the necessary events (similar to the jQuery get/ajax plugin): How to use Simple Ajax Beginform in Asp.net MVC 4?

jQuery Mobile ajax links not working properly in ASP.NET MVC application

I have a master layout looking somehow like this
<html>
<head>
...
</head>
<body>
<section data-role="page" data-theme="d">
<section data-role="content">
#RenderBody()
</section>
...
#Scripts.Render("~/bundles/jquery.mobile")
</section>
</body>
</html>
And I have a couple of controllers and templates pushing some code into content section of master layout.
When i click on a link:
Some order
the ajax loader is shown, the url changes, but all I see is the broken markup of the original page. If I inspect the code I will see that there are two page sections in the DOM (which means that the target page is successfully injected but isn't shown).
If I refresh page I will see the target page.
So what's wrong? Why can't I get the fancy JQM page transitions? thank you.
Add the data-ajax='false' on the links who were responsible for redirect you to another page.
JQuery Mobile uses AJAX navigation unless you tell it otherwise. The ASP.NET redirects have no problem to normal requests, but cause issues with AJAX. So my answer in this case is to turn off AJAX. For that, all you have to do is add the attribute data-ajax='false' to the tag.
In my case, an easy solution could be (as changing each Html.ActionLink/Url.Action is time consuming), adding this at the end of the _Layout.cshtml (just before closing of the 'body' tag).
$(document).on('pageinit', function() {
$('a').each(function() {
$(this).attr("data-ajax", "false");
});
});
That would be fine assuming you don't want jQuery mobile to use any Ajax with links too.
You can go ahead and use Ajax in most circumstances and only turn it off selectively where it can cause a problem.

Jquery Mobile Javascript not working on ajax load

Have the following markup in my html page to toggle a search bar based on if a search icon is clicked:
<a id="searchIcon" href="/"></a>
<div id="searchWrapOuter" style="display:none;">
<div id="searchWrapInner">
<div id="formContainer">
<form id="searchForm" action="#">
<div>
<input type="search" name="search-mini" id="search-mini" value="" data-mini="true" />
</div>
</form>
</div>
</div>
</div>
Width the following javascipt/jquery:
$(function() {
$(document).on("click", "#searchIcon", function () {
var searchWrapper = $("#searchWrapOuter");
$(searchWrapper).slideToggle();
return false;
});
});
This code works as expected on a page load direct off a Url. When coming into the page off a link which is Ajax loaded, loads the contents of the page into the DOM, and the DOM ready handler only executes for the first page.
I have read about using the
$(document).on('pageinit'), not $(document).ready()/$(function()
I still haven't been able to get this to work when coming in off an ajax link however. What would be the correct way to implement these events to get the code to work coming in from an Ajax link?
Thanks,
Most likely it is because you are using IDs instead of classes. jQuery Mobile doesn't work well with IDs because it caches pages into the DOM so if you open a page, close it, then go back to the page, you might have your page twice inside the DOM (one visible, one hidden/cached). So when you do $("#searchWrapOuter") you don't know which element you are actually dealing with (in your case, probably the hidden one).
The solution is to change your IDs to classes. This is not very intuitive but I found that is the best way to work with jQuery Mobile.
Also note this comment in the doc which might also be relevant to you:
The id attribute of all your elements must be not only unique on a given page, but also unique across the pages in a site. This is because jQuery Mobile's single-page navigation model allows many different "pages" to be present in the DOM at the same time. This also applies when using a multi-page template, since all "pages" on the template are loaded at once.
http://jquerymobile.com/demos/1.2.0/docs/pages/page-anatomy.html
You can manually adjust delay time to 500ms and 1s.
$(searchWrapper).delay(1000).slideToggle();
My issue is that the page id was below the pages tags. So once I moved the page div above it, the javascript was included in the ajax page load. Previous to this

jquery mobile save button click event not firing

I am working on jQuery mobile controls. I have a jqgrid in a master page and on click of selected row id I am redirecting to another page with the id. In that page I have drop down which is bound with json. And after binding I want to save data to database on click of a button.
The problem is that the button click event is not firing, however, when I run that particular aspx on its own the button click event does fire.
<a href="#" data-role="button" data-inline="true" id="btnsave" data-transition="fade"
data-theme="b" data-icon="check" data-iconpos="right" runat="server"
onserverclick="btnsave_click" >Save</a>
Try adding the data-ajax="false" to your link.
For example
<a href="#" data-role="button" data-ajax="false" data-inline="true" id="btnsave"
data-transition="fade" data-theme="b" data-icon="check" data-iconpos="right"
runat="server" onserverclick="btnsave_click" >Save</a>
Now for the explanation, by default when you link to a page in jQuery Mobile, JQM pulls in that page via ajax and attaches it to the current page's DOM. When JQM pulls in a page via ajax it only pulls in that data-role="page" and does not load any of resources that may be in the <head> or elsewhere on the page.
From your question and markup it sounds like you are using asp.net, without seeing any more of your markup/code I'm pretty certain that your issue is that you don't have the necessary code for the linked page on your master page.
Normally to resolve this you can either place the unique content within the linked page's data=role="page" div, or when you call the page make sure the page is completely loaded, that is without ajax. In your case since it looks like you are trying to call a server method the code to create the postback is probably outside of your second page's div in which case you will need to load the page without ajax, the easiest way you can do that is by adding the data-ajax="false" to your link.

jQuery Mobile proper way to initialize a listview

Here is my problem. I have a few hard coded pseudo pages in my index. Some filled with content, some empty which will be filled on user interaction only by ajax. This ajax content contains html lists. When they load they don't have the nice jquery mobile look so I have to call a .listview() method so the jqm framework parse it on my ajax callback. That is where I often get this JS error:
Uncaught TypeError: Cannot read property 'jQuery162027575719612650573' of undefined
The number is never the same...
I wonder if I use the proper way to parse a listview after the page loads the ajax content. the error seems to be triggered when there is slight lag for the loading and the complete event is triggered too soon and my listview is not yet in the DOM at that moment, just a guess. what is the recommended way to initialize a listview after an ajax call?
It is very unfortunate because when the js error occurs it seems to freeze any further js execution...
so here is my empty pseudo page:
<div data-role="page" id="playlist" data-add-back-btn="true">
<div data-role="header">
<h1><g:message code="pd.playlist" /></h1>
</div>
<div data-role="content"></div>
</div>
right under it there is a script tag with the bind an ajax call on pageshow to activate the listview
<script type="text/javascript">
$('#playlist').bind('pageshow', function () {
$.ajax({
url: "updatePlaylistTemplate.gsp",
error:function(x,e){handleAjaxError(x,e);},
beforeSend:function(){$.mobile.showPageLoadingMsg();},
complete:function(){
$.mobile.hidePageLoadingMsg();
$('[data-role="listview"]').listview(); //re-active all listview
},
success:function(data, textStatus, jqXHR){
$('#playlist').find('[data-role="content"]').html(data);
}
});
});
</script>
The updatePlaylistTemplate return this (extract):
<ul data-role="listview" data-split-theme="d">
<li data-icon="delete">
Provider: Bell
</li>
<li data-icon="delete">
Rock - Classic Rock
</li>
<li data-icon="refresh" data-theme="e">Refresh list</li>
<li data-role="list-divider">Next song</li>
<li>
<a href="urlToViewSongInfo">
<img src="images/song.gif" />
<h3>Albert Flasher</h3>
<p>The Guess Who</p>
<p class="ui-li-aside">Next</p>
</a>
</li>
<li data-role="list-divider">Now playing</li>
<li>
<a href="urlToviewSongInfo">
<img src="images/song.gif" />
<h3>Crime of the Century</h3>
<p>Supertramp</p>
<p class="ui-li-aside">14h49</p>
</a>
</li>
<li data-role="list-divider">Previous songs</li>
<li>
<a href="urlToViewSongInfo">
<img src="images/song.gif"" />
<h3>Desperado</h3>
<p>Alice Cooper</p>
<p class="ui-li-aside">14h45</p>
</a>
</li>
[...]
</ul>
What version of jQuery Mobile are you using? In the latest beta (1.0b2) you can trigger the create event on a dom element to have the framework initialize it:
New “create” event: Easily enhance all widgets at once
While the page plugin no longer calls each plugin specifically, it
does dispatch a “pagecreate” event, which most widgets use to
auto-initialize themselves. As long as a widget plugin script is
referenced, it will automatically enhance any instances of the widgets
it finds on the page, just like before. For example, if the selectmenu
plugin is loaded, it will enhance any selects it finds within a newly
created page.
This structure now allows us to add a new create event that can be
triggered on any element, saving you the task of manually initializing
each plugin contained in that element. Until now, if a developer
loaded in content via Ajax or dynamically generated markup, they
needed to manually initialize all contained plugins (listview button,
select, etc.) to enhance the widgets in the markup.
Now, our handy create event will initialize all the necessary plugins
within that markup, just like how the page creation enhancement
process works. If you were to use Ajax to load in a block of HTML
markup (say a login form), you can trigger create to automatically
transform all the widgets it contains (inputs and buttons in this
case) into the enhanced versions. The code for this scenario would be:
$( ...new markup that contains widgets... ).appendTo( ".ui-page"
).trigger( "create" );
Create vs. refresh: An important distinction
Note that there is an important difference between the create event
and refresh method that some widgets have. The create event is suited
for enhancing raw markup that contains one or more widgets. The
refresh method that some widgets have should be used on existing
(already enhanced) widgets that have been manipulated programmatically
and need the UI be updated to match.
For example, if you had a page where you dynamically appended a new
unordered list with data-role=listview attribute after page creation,
triggering create on a parent element of that list would transform it
into a listview styled widget. If more list items were then
programmatically added, calling the listview’s refresh method would
update just those new list items to the enhanced state and leave the
existing list items untouched.
Link: http://jquerymobile.com/blog/
You can also copy the output that jQuery Mobile creates and use that structure rather than using <li> tags and depending on jQM to inititialize it:
<li data-role="list-divider" class="ui-li ui-li-divider ui-btn ui-bar-a ui-btn-up-undefined" role="heading"><span>List Divider</span></li>
<li data-theme="b" class="ui-li ui-li-static ui-body-b">Regular LI</li>

Resources