How to reload a route using hasher.js and crossroads.js? - javascript

I currently have this code:
crossroads.addRoute('/login', function(){
$.ajax({
url: '/login',
type: 'GET',
data: {
},
success: function(msg) {
$('#' + main_window).html(msg);
}
})
});
Along with this hasher.js for maintaining history:
function parseHash(newHash, oldHash){
crossroads.parse(newHash);
}
hasher.initialized.add(parseHash); //parse initial hash
hasher.changed.add(parseHash); //parse hash changes
hasher.init(); //start listening for history change
$('a').on('click', function(e) {
e.preventDefault();
hasher.setHash($(this).attr('href'));
});
Now when I have a link that says #/login and click it, the login page is loaded fine. However, if the user clicks the login link again, the page doesn't reload. I know the click is registering, but the routing/hashing isn't firing. How do I manage to do this?
Any help would be greatly appreciated. Thanks!

You are not changing anything with hasher.setHash("#/currentpage") because you are already on that page. So the change event never gets fired. You could probably just go:
$('a').on('click', function(e) {
e.preventDefault();
hasher.setHash('');
hasher.setHash($(this).attr('href'));
});
But that would clutter the history, so you could force a reload on the click, if you make the loading method available:
var loadLogin = function(){
$.ajax({
.... your ajax settings ....
});
};
crossroads.addRoute('/login', loadLogin);
$('a').on('click', function(e) {
e.preventDefault();
var newHash = $(this).attr('href');
if(newHash === '/login' && hasher.getHash() === '/login') {
loadLogin()
} else {
hasher.setHash(newHash);
}
});
EDIT
As #Patchesoft pointed out, crossroads has a property called ignoreState, which will force any request to go though the router function, even if on the current page - https://millermedeiros.github.io/crossroads.js/#crossroads-ignoreState

Your can force the call of your route by this:
// add this dummy route first
crossroads.addRoute('noop', function() { } );
$('a').on('click', function(e) {
// route to dummy route
hasher.setHash('noop');
// and then replace by your login route
hasher.replaceHash('login');
}

Related

How to prevent all links from redirecting before completing an AJAX process

I have an application that uses tinyMCE. In this page, the user will compose a message then previews it on another page (not using tinyMCE's preview). I currently have an AJAX function that saves the content when the user clicks away from the tinyMCE UI. The problem is, I have different kinds of links on this same page, sometimes when a user clicks on the link, the AJAX function fails to save the content before redirecting, thus resulting to a blank preview or unsaved message.
<div>
<textarea id='msg'></textarea>
<a id='preview'>Preview</a>
<a id='other_page'>Other Page</a>
<a id='another_page'>Another Page</a>
</div>
Here are the JavaScript handlers and functions.
<script>
// INITIALIZE TINYMCE
tinyMCE.init({
// SAVE CONTENT ON BLUR
editor.on('blur', function() { save_message(this); });
})
function save_message(){
var msg= tinyMCE.get('msg ').getContent();
$.ajax({
statusCode : { 404: function(){alert('Not Found');} },
type : 'post',
data : {msg:msg},
url : '<script_that_handles_save>',
success : function(res){ // DO SOMETHING }
});
}
// WHEN PREVIEW IS CLICKED
$('a.preview).click(function(){
save_message();
});
$('a.other_page).click(function(){
save_message();
});
$('a.another_page).click(function(){
save_message();
});
</script>
A few things, your save_message() function is an AJAX call that needs to complete and send back a response for it to work. When you click an anchor tag, the function is called (a per your code above) but the page redirects before the function returns a response.
Also it seems like you are calling the function redundantly for each anchor tag, why not call it once for ALL anchor tags, like so a.click(function(){ // your function here });
Your code logic is good, you only need to re-structure and simplify it, like so:
tinyMCE.init({
// SAVE CONTENT ON BLUR
editor.on('blur', function() { save_message(this); });
})
$('a').click(function(e){
// prevent the redirect
e.preventDefault();
var msg= tinyMCE.get('msg ').getContent();
var location = $(this).attr('href');
// execute ajax
$.ajax({
statusCode : { 404: function(){alert('Not Found');} },
type : 'post',
data : {msg:msg},
url : '<script_that_handles_save>',
success : function(res){
// redirect on success
window.location.href = location
}
});
});
You can create some kind of flag variable, which will tell if links should be prevented from redirecting or not. Before sending AJAX request to save message, set this variable to true, and after successful request set it back to false. Also set an event listener on all links, which will check this flag variable, and if it equals true, prevent redirecting.
Example code:
var linksDisabled = false;
function save_message() {
linksDisabled = true;
$.ajax({
success: function() {
linksDisabled = false;
}
// some other options
})
}
$("a").click(function(event) {
if (linksDisabled) {
event.preventDefault();
}
});

JQuery conditional preventDefault() firing when not called

I have the following script I've written.
<script>
$(document).ready(function(){
$('a').data('loop',true);
$('body').on('click', 'a', function(event){
console.log($(this).data('loop'));
if ($(this).data('loop') == 'true') {
console.log('hit');
event.preventDefault();
caller = $(this);
$(this).data('loop',false);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({linkref: linkref, linkpos: linkpos, screenwidth: screenwidth});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
data: "json=" + json_data,
complete: function (jqXHR, status) {
console.log(status);
console.log(caller);
$(caller).click();
}
});
} else {
console.log(event.isDefaultPrevented());
console.log('miss');
$(this).data('loop',true);
}
});
});
</script>
It works, sends me the details I want etc etc. BUT!!!
When I click a link, It fires off the details to me via Ajax, then it's meant to "click" the event again, which it does! but the event does not fire it's normal action. So When clicking a link to another page, I would go to that other page... that's not happening.
If I comment out the line event.preventDefault(); Then the event fires as I would expect...
So to me it looks like the event.preventDefault is executing even though it's not meant to be during the second call...
Sorry if this is a bit complicated to understand. I don't quite understand what's happening myself.
Is it possibly a bug, or is there something that I've done that has caused this?
I didn't think I could, but I have successfully made a jsfiddle for this.
https://jsfiddle.net/atg5m6ym/2001/
You can try this and not worry about the "loop" anymore:
$(document).ready(function () {
$('body').on('click', 'a', function (event) {
event.preventDefault();
var caller = $(this);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({linkref: linkref, linkpos: linkpos, screenwidth: screenwidth});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
data: "json=" + json_data,
complete: function (jqXHR, status) {
console.log(status);
console.log(caller);
window.location.href = linkref; // Redirect happens here
}
});
});
});
UPDATE
There's a few issues to note here:
1) Some links don't require a redirect (as noted, bootstrap model links that control showing/hiding or within document anchors
To correct this it really depends on the case. Usually bootstrap adds specific classes or data attributes to the links so you can do something like.
$('body').on('click', 'a:not(list of things to exclude)'..
Personally I'd instead define the links I wanted to track as :
<a href=<link> data-tracked='true'...
<script>
$('body').on("click","a[data-tracked='true']"...
Or if you want to track most links with a few exceptions you can:
<a href=<link> data-tracked='false'...
<script>
$('body').on("click","a:not([data-tracked='false'])"...
Or more generally:
<script>
$('body').on("click","a", function () {
if ($(this).attr("data-tracked") == "false" || <you can check more things here>){
return true; //Click passes through
}
//Rest of the tracking code here
});
The following if statement will return true whenever the data-loop attribute exists against an element, regardless of it's value:
if ($(this).data('loop')) {
It needs to be changed to check for the value:
if ($(this).data('loop') == 'true') {
When you assign anything to be the value of an element attribute it becomes a string and, as such, requires a string comparison.
Event.preventDefault() is not being executed second time.
Link redirection happens when the method is completed.
So in your case redirection will happen when complete method of ajax call is completed.
lets say, we have event1 and event2 object in the code. event1 is the object in the ajax call method and event2 is the event object in recursive call (second call) method.
so when link is clicked second time , we still have complete method to be executed. as soon as it returns to the complete method of ajax call, it finds the event1 is having preventDefault property true and it does not redirect.
Try this ;)
$(document).ready(function(){
$('body').on('click', 'a', function(event){
event.preventDefault();
var caller = $(this);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({
linkref: linkref,
linkpos: linkpos,
screenwidth: screenwidth
});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
/* To temprary block browser; */
async: false,
data: "json=" + json_data,
complete: function(jqXHR, status){
/* add class **ignore** to a element you don't want to redirect anywhere(tabs, modals, dropdowns, etc); */
if(!caller.hasClass('ignore')){
/* Redirect happens here */
window.location.href = linkref;
}
}
});
});
});

Using history.pushstate on jquery ajax

I have a heavily ajax based application wherein i only have a login page and the main page.
Most of my links are "ajaxed" and i have them done like this:
//get the href of the link that has been clicked, ajaxify ANY links
$(document).on('click', '.tree a', function () {
var link = $(this).attr('href'); //get the href off the list
$.ajax({ //ajax request to post the partial View
url: link,
type: 'POST',
cache: false,
success: function (result) {
$('#target').html(result);
$.validator.unobtrusive.parse($("form#ValidateForm"));
}
});
return false; //intercept the link
});
I want to implement "pushState" on my application and the first step that i have done so far is to add this code:
$(document).on('click', 'a', function () {
history.pushState({}, '', $(this).attr("href"));
});
Now it updates my address bar whenever i click on any of my links and the ajax content gets successfully loaded.
I am kinda new to this API so i don't know what am i missing but here are my issues so far:
when i press the "back" button, nothing happens. I read about "popstate" and browsed through SO to look for solutions but i can't
seem to make them work.
When i click the link from the history, i get the "raw" view of the child html w/o the layout from the master html. What do i need to do if i want it to be displayed like
it was clicked from my main application?
Most of my child views are either forms or list.
This code should help you :
function openURL(href){
var link = href; //$(this).attr('href');
$.ajax({
url: link,
type: 'POST',
cache: false,
success: function (result) {
$('#target').html(result);
$.validator.unobtrusive.parse($("form#ValidateForm"));
}
});
window.history.pushState({href: href}, '', href);
}
$(document).ready(function() {
$(document).on('click', 'a', function () {
openURL($(this).attr("href"));
return false; //intercept the link
});
window.addEventListener('popstate', function(e){
if(e.state)
openURL(e.state.href);
});
});

jQuery .on not working after ajax delete and div refresh

On a page with a tab control, each tab contains a table, each tr contains a td with a button which has a value assigned to it.
<td>
<button type="button" class="btn" name="deleteEventBtn" value="1">Delete</button>
</td>
This code below works for the first delete. After the AJAX call & the refresh of the div, no further delete buttons can be clicked. The .on is attached to the document. The same happens if I attach it to the body or anything closer to the buttons.
function deleteRecord(url, id, container) {
$.ajax({
type: "POST",
url: url,
data: { id: id },
success: function (data) {
$('#delete-popup').hide();
$(container).trigger('refresh');
}
});
}
$(document).ready(function () {
$(document).on('click', '[name^="delete"]', function (e) {
e.preventDefault();
var id = $(this).val();
$('#current-record-id').val(id);
$('#delete-popup').modal('show');
});
$('#delete-btn-yes').on('click', function (e) {
e.preventDefault();
var recordId = $('#current-record-id').val();
var recordType = location.hash;
switch (recordType) {
case "#personList":
deleteRecord(url, recordId, recordType);
break;
}
});
});
Any ideas? Could it be related to the wildcard for starts with [name^="delete"]? There are no other elements where the name starts with 'delete'.
EDIT
When replacing
$(container).trigger('refresh');
with
location.reload();
it "works", however that refreshes the whole page, loses the users position and defeats the point of using AJAX.
As the button click is firing at first attempt, there is no issue in that code. All you have to do is, put the button click event in a method and call it after the refresh. This way, the events will be attached to the element again. See the code below,
function deleteRecord(url, id, container) {
$.ajax({
type: "POST",
url: url,
data: { id: id },
success: function (data) {
$('#delete-popup').hide();
$(container).trigger('refresh');
BindEvents();
}
});
}
$(document).ready(function () {
BindEvents();
});
function BindEvents()
{
$(document).on('click', '[name^="delete"]', function (e) {
e.preventDefault();
var id = $(this).val();
$('#current-record-id').val(id);
$('#delete-popup').modal('show');
});
$('#delete-btn-yes').on('click', function (e) {
e.preventDefault();
var recordId = $('#current-record-id').val();
var recordType = location.hash;
switch (recordType) {
case "#personList":
deleteRecord(url, recordId, recordType);
break;
});
}
Apologies to all and thanks for your answers. The problem was due to the way the popup was being shown & hidden.
$('#delete-popup').modal('show');
and
$('#delete-popup').hide();
When I changed this line to:
$('#delete-popup').modal('hide');
it worked. Thanks to LShetty, the alert (in the right place) did help!
If you are using Bootstrap Modal
After Ajax Request before Refreshing page add
$('.modal').modal('hide');
This Line will Close your Modal and reload your page. Before that it will complete all Ajax Request things.
But for google chrome there is no issues :) hope this help someone.

How can I load the first page of an MVC 4 application via AJAX?

How can I load the first page of an MVC 4 application via AJAX?
I already have the structure set-up to load all the subsequent pages, but I've just hit a brick wall.
After adding the history management through history.js, I've changed my partial views to have a back button - which works as expected.
My problem is that after navigating to a couple of pages and then pressing the Back button, when I get to the first item on the windows history - i.e.: the index.chtml page - this page loads with the navigation bar appearing twice. After pressing F5, the page loads correctly.
What I'm doing wrong and what is the best way to fix this?
Thanks in advance for your help
By the way, this is the JavaScript I'm loading on the Layout View:
$(function () {
var contentShell = $('#bodyContent');
var History = window.History, State = History.getState();
$(".ajaxLink").on('click', function (e) {
e.preventDefault();
var url = $(this).data('href');
var title = $(this).data('title');
History.pushState(null, title, url);
});
History.Adapter.bind(window, 'statechange', function () {
State = History.getState();
navigateToURL(State.url);
});
function navigateToURL(url) {
$.ajax({
type: "GET",
url: url,
dataType: "html",
success: function (data, status, xhr) {
contentShell.html(data);
},
error: function (xhr, status, error) {
alert("Error loading Page.");
}
});
}
$('.history-back').on('click', function (e) {
e.preventDefault();
History.back();
return false;
})
});
This looks for me like a known problem, bug related to the click events which are fired twice in some browsers. This behavior is causing the History.pushState(...) in your code to execute twice.
You can try to add sth. like e.stopPropagation() to your code after. More on that problem you can find here: https://stackoverflow.com/a/21511818/2270888
Try something like this in your "first item" controller action:
public ActionResult Index()
{
if (Request.IsAjaxRequest())
{
return PartialView();
}
return View();
}

Categories

Resources