I have 3 links, 2 with a pushstate data 1 without. Users + Tags link has data, topics doesnt. If i click users then topics then back or tags then topics then back it works perfect. if I click users then tags then click back it will only load the last pushstate (tags). if i click tags then users then back it just reuses the users pushstate. if i go tags -> users -> topics, back will goto users, back again will also be users ??
$('#changetousers').click(function () {
$('#loadingAjaxs').show(); $('#flubestext').hide();
$('#contentwrap').load('#Url.Action("FollowingUsersDetail", "Following", new {#ajax = "yes"})', function () { $('#loadingAjaxs').hide(); $('#flubestext').show(); window.history.pushState({ "page": "followingusers" }, 'title1', '/users/'); window.onpopstate = function (e) { document.getElementById('changetousers').click(); };
})
});
$('#changetotags').click(function () {
$('#loadingAjaxs').show(); $('#flubestext').hide();
$('#contentwrap').load('#Url.Action("FollowingTagsDetail", "Following", new {#ajax = "yes"})', function () { $('#loadingAjaxs').hide(); $('#flubestext').show(); window.history.pushState({ "page": "followingtags" }, 'title2', '/tags/'); window.onpopstate = function (e) { document.getElementById('changetotags').click(); }; })
});
$('#changetofavorites').click(function () {
$('#loadingAjaxs').show(); $('#flubestext').hide();
$('#contentwrap').load('#Url.Action("FollowingTopicsDetail", "Following", new {#ajax = "yes"})', function () { $('#loadingAjaxs').hide(); $('#flubestext').show(); window.history.pushState(null, 'title', '/favorites/'); })
});
I think you calling the pushState even user clicks to back, this is why you cannot go to previous state. This should work:
function loadUserDetails() {
$('#loadingAjaxs').show();
$('#flubestext').hide();
$('#contentwrap').load(
'#Url.Action("FollowingUsersDetail", "Following", new {#ajax = "yes"})',
function () {
$('#loadingAjaxs').hide();
$('#flubestext').show();
});
}
$('#changetousers').click(function () {
loadUserDetails();
window.history.pushState({ "page": "followingusers" }, 'title1', '/users/');
window.onpopstate = function (e) { loadUserDetails(); };
});
Related
I have SPA multi view application in AngularJS, I have defined $interval which is started from another view Controller. When i click a btn with function called and line $interval.cancel(); in it, it does not stop.
Here are examples of my code:
MainController:
$scope.$on("startInterval", function () {
$interval(function warningsControl() {
console.log("Timer stamp!");
$.ajax({
// some web api call which works fine
})
}, 10000);
});
$scope.stop = function () {
$interval.cancel();
}
$scope.logoutButton = {
text: "Logout",
type: "normal",
visible: false,
onClick: function () {
// some working code
$scope.stop();
var logoutBtn = $("#logout-btn").dxButton("instance");
logoutBtn.option({
visible: false
});
}
}
And SecondController:
$scope.authenticateButton = {
type: "default",
text: "Log In",
onClick: function () {
$.ajax({
// some web api calling
success: (data) => {
// some working code
$rootScope.$broadcast("startInterval");
}
})
}
}
This code start interval and everithing is running OK, until the point i click Logout btn - it made everithing except stoping the interval.
Any ideas how to fix it? I would be grateful for advice.
The $interval function should return some sort of ID which you can pass into $interval.cancel(). For example:
var int_id = $interval( func, time )
//...later...
$interval.cancel(int_id)
I've struggled with this problem for two days. I googled it and see some similar cases.
After sending POST request to the server, I wanna redirect the page to another one. The POST function works properly but window.location.assign() method doesn't work (although it works in another part of my project).
Here is my code:
const $btnDel = $("<button>", {
appendTo: $('td:eq(0)', $tr),
"class": "revertButton",
html: "<i class='material-icons'>restore</i>",
on: {
click: function () {
console.log(`Reverting: ${book.name}`)
$.post(`/revert/${book.name}`, {
bookName: `${book.name}`,
pageCount: `${book.pageCount}`,
action: "REVERT"
}).done(() => {
window.location.assign('/bookPage');
})
}
}
});
The first console.log works (I mean console.log("Reverting: ${book.name}"), but done function doesn't work.
Can you please try the below code.
const $btnDel = $("<button>", {
appendTo: $('td:eq(0)', $tr),
"class": "revertButton",
html: "<i class='material-icons'>restore</i>",
on: {
click: function () {
console.log(`Reverting: ${book.name}`)
$.post(`/revert/${book.name}`, () => {
bookName: `${book.name}`,
pageCount: `${book.pageCount}`,
action: "REVERT"
}).done(() => {
window.location.assign('/bookPage');
}).fail(function(error){
console.log(error);
})
}
}
});
Hope this will give you some help/clue.
I hope someone can help me with this.
I have a Backbone based SPA for a responsive website with a .net WebAPI providing all of the data.
I've recently found a weird problem. I've added a search box, which searches one of the catalogues on the system. This works fine on desktop browsers and on Android. On iOS, executing a search seems to take you back to the sign in page.
You can execute a search in various ways, you can either hit enter or you can click the search icon. Both of these then trigger a method that navigates the router to the URL for the search result.
My first thought was that it was some button weirdness, but I don't think that's the problem as both methods of search execution are causing the same problem.
The search results are displayed in a view that is secured (It requires a username to be present - this is stored in a hidden field on the page). There are two search boxes on the site - one on the home page and one on the search results page itself (it shows a default set when you load it first time - which it does load first time fine). Both search boxes are exhibiting the same behaviour.
My site is set up in such a way that when Backbone pulls back a model, if it gets a 401 back from the API then it will send you back to the login page, so I can only think it's happening here.
Here's my view code...
function (SiansPlan, ErrorManager, RecipeSearchResult, Header, Components, TemplateSource) {
var recipeSearchView = SiansPlan.SecureView.extend({
name: 'Recipe Search',
sectionName: 'Recipes',
queryText: '',
template: Handlebars.compile(TemplateSource),
headerView: new Header({ text: 'Recipes', swatch: 'e' }),
searchBoxRegion: undefined,
$searchWrapper: undefined,
$queryHeaderMobile: undefined,
$queryHeaderDesktop: undefined,
$searchButton: undefined,
$searchInput: undefined,
$recipeSearch : undefined,
events: {
'click .link-container': 'showRecipe',
'click #searchWrapper': 'showSearch',
'click #searchButton': 'showOrPerformSearch',
'keydown #searchButton': 'performSearchOnEnter',
'keydown #recipeSearch': 'performSearchOnEnter'
},
initialize: function (options) {
this.options = options || {};
SiansPlan.SecureView.prototype.initialize.call(this, options);
this.queryText = Object.exists(this.options.query) ? this.options.query : '';
},
bindData: function () {
this.$el.html(this.template({ results: this.collection.toJSON() }));
},
render: function () {
var that = this;
if (this.isSecured()) {
this.trigger('rendering');
var params = {
success: function () {
that.bindData();
that.trigger('rendered');
},
error: function (model, xhr) {
if (Object.exists(xhr) && xhr.status == 401) {
that.applyTimedOutSecureLoginPrompt();
} else {
that.$el.html('Unable to fetch search results');
ErrorManager.handleXhr('Search failed', xhr);
}
that.trigger('rendered');
}
};
if (!Object.exists(this.collection)) {
this.collection = new RecipeSearchResult.Collection({ username: SiansPlanApp.session.username(), query: this.queryText });
}
this.collection.fetch(params);
} else {
this.applySecureLoginPrompt();
}
return this;
},
postRender: function () {
var that = this;
var queryHeader = "All recipes";
if (Object.hasValue(this.queryText)) {
queryHeader = this.collection.length + " results for '" + this.queryText + "'";
}
this.$searchWrapper = $('#searchWrapper');
this.$queryHeaderMobile = $('#queryHeaderMobile');
this.$queryHeaderDesktop = $('#queryHeaderDesktop');
this.$searchButton = $('#searchWrapper');
this.$searchInput = $('#searchInput');
this.$recipeSearch = $('#recipeSearch');
this.$queryHeaderMobile.html(queryHeader);
this.$queryHeaderDesktop.html(queryHeader);
this.$recipeSearch.val(this.queryText);
SiansPlanApp.session.waitForLoad(30, function () {
that.searchBoxRegion = new SiansPlan.Region({ el: '.recipe-search-box-container' });
that.searchBoxRegion.renderView(new Components.RecipeSearchBox({ username: SiansPlanApp.session.username(), query: that.queryText, title: 'Search' }));
});
},
performSearchOnEnter: function (e) {
if (e.keyCode == 13) {
this.showOrPerformSearch(e);
}
},
showOrPerformSearch: function (e) {
if (!this.$searchInput.is(':visible')) {
this.showSearch(e);
} else {
e.preventDefault();
var url = '/recipes/search/' + this.$recipeSearch.val();
window.SiansPlanApp.router.navigate(url, true);
}
return false;
},
showRecipe: function (e) {
e.preventDefault();
var url = $(e.target).find('a').first().attr('href');
window.SiansPlanApp.router.navigate(url, true);
},
showSearch: function (e) {
e.preventDefault();
if (!this.$searchInput.is(':visible')) {
this.$queryHeaderMobile.hide();
this.$searchInput.show();
this.$recipeSearch.focus();
this.$recipeSearch.select();
}
return false;
}
});
return recipeSearchView;
});
UPDATES
I've set up some alerts as follows in the script to see what's going on and I've discovered the following...
render: function () {
var that = this;
if (this.isSecured()) {
this.trigger('rendering');
var params = {
success: function () {
alert('Bind has succeeded!');
that.bindData();
that.trigger('rendered');
},
error: function (model, xhr) {
alert('Bind has failed!');
if (Object.exists(xhr) && xhr.status == 401) {
that.applyTimedOutSecureLoginPrompt();
} else {
that.$el.html('Unable to fetch search results');
ErrorManager.handleXhr('Search failed', xhr);
}
that.trigger('rendered');
alert(xhr.status + ' ' + xhr.responseText);
}
};
if (!Object.exists(this.collection)) {
alert('Binding new collection: ' + SiansPlanApp.session.username() + ' - ' + this.queryText);
this.collection = new RecipeSearchResult.Collection({ username: SiansPlanApp.session.username(), query: this.queryText });
}
alert('About to fetch using ' + this.collection.url());
this.collection.fetch(params);
} else {
alert('I dont appear to be secured??');
this.applySecureLoginPrompt();
}
return this;
},
When I first load the page (to show all the results) it loads fine and 'Bind Succeeded!' appears. The API call made is /api/recipes/search/{username}/
When I submit search criteria it fails ('Bind failed!') with the API call of /api/recipes/search/{username}/{query} and returns a 401.
This has me even more befuddled than before as this now looks like an API issue, but other devices are working fine and if I submit the same queries into Fiddler everything is, as expected, fine.
I've found the answer in the smallest place...
The issue was that the search criteria had an upper case letter. So, for example, when searching with 'Fish', The API generated a 301 which redirected to /api/recipes/search/{username}/fish. iOS didn't like that and reported it as a 401 (Which truly sucks!)
I'm creating a single-page web app using the Parse JS library, which is an extension of Backbone.
My Router is set up as follows:
var AppRouter = Parse.Router.extend({
routes: {
"": "signup",
"signup": "signup",
"login": "login",
"logout": "logout",
"dashboard": "dashboard",
"history": "history",
"account": "account"
},
initialize: function(options) {
},
login: function() {
this.navigateTo(new LoginView(), false);
},
logout: function() {
Parse.User.logOut();
Parse.history.navigate("signup", true);
},
signup: function() {
this.navigateTo(new SignupView(), false);
},
dashboard: function() {
this.navigateTo(new DashboardView(), true);
},
history: function () {
this.navigateTo(new SentMailView(), true);
},
account: function () {
this.navigateTo(new AccountView(), true);
},
navigateTo: function(view, needsSignedIn) {
if (needsSignedIn && !Parse.User.current()) {
Parse.history.navigate("signup", true);
} else if (!needsSignedIn && Parse.User.current()) {
Parse.history.navigate("dashboard", true);
} else {
this.loadView(view);
}
},
loadView: function(view) {
this.view && (this.view.close ? this.view.close() : delete this);
this.view = view;
}
});
At the end of my main.js, I'm creating a new Router, View, and setting pushState to true:
new AppRouter;
new AppView;
Parse.history.start({pushState: true});
The problem I'm having is that occasionally (often when I first load a page of the app), none of the links work, and all they do is refresh the page. For instance, clicking on a link to '/history' does not load the history page, it just reloads the current page. Similarly, clicking a link to a bootstrap dropdown menu, which is just supposed to trigger a js call, will also reload the current page. Refreshing the browser (via the refresh button) usually fixes this, and then all the links work fine.
Any idea why this is happening?
Fixed it. The issue was being caused by an underlying view that wasn't being properly deleted. This view had a function that was being bound to the "click a" event -- a click on any element. The same function was being bound to click events on all links in the dashboard view, which I did not want.
I'm not sure how to express this in code, as I can't seem to locate the problem, but my issue is that Backbone.history seems to be recording two items when a user clicks on a list item in my app.
This is not consistent.
My app has a 4 item navigation at the bottom that links to 4 main sections (the first one being home - routed to '/'). If I load up the app, go to one of the other navigation pages, then click the 'Home' button again and then click one of the navigation options I get a list of items to choose from. If I then choose one two entries are added - Firstly, for some reason, a reference to the home route with /# at the end and then the route for the item I clicked.
The end result is that 'back' then inexplicably takes me to the home page.
If it helps, my router looks like this...
var siansplanRouter = Backbone.Router.extend({
initialize: function () {
var that = this;
this.routesHit = 0;
//keep count of number of routes handled by your application
Backbone.history.on('route', function() { that.routesHit++; }, this);
window.SiansPlanApp.render();
window.SiansPlanApp.router = this;
},
routes: {
'': 'showHome',
'home': 'showHome',
'hub': 'showHome',
'samples': 'showJqmSamples',
'mealplanner': 'showCurrentMealPlanner',
'mealplanner/:planId': 'showMealPlanner',
'recipes': 'showRecipeSearch',
'recipes/:recipeId': 'showRecipe',
'settings': 'showSettings',
'versioninfo': 'showVersionInfo',
'*other': 'showHome'
},
routesHit: 0,
back: function() {
if(this.routesHit > 1) {
window.history.back();
} else {
//otherwise go to the home page. Use replaceState if available so
//the navigation doesn't create an extra history entry
this.navigate('/', { trigger: true, replace: true });
}
},
showHome: function () {
SiansPlanApp.renderHome();
},
showJqmSamples: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Hub.Samples());
},
showMealPlanner: function (planId) {
SiansPlanApp.renderView(new SiansPlanApp.views.Planner.MealPlanner({ id: planId }));
},
showCurrentMealPlanner: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Planner.MealPlanner({ current: true }));
},
showRecipeSearch: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.Recipes.Search());
},
showRecipe: function (recipeId) {
SiansPlanApp.renderView(new SiansPlanApp.views.Recipes.Recipe({ id: recipeId }));
},
showSettings: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.System.Settings());
},
showVersionInfo: function () {
SiansPlanApp.renderView(new SiansPlanApp.views.About.VersionInfo.ListView());
}
});
I've got some basic elements in a kick off file too here...
define(['router', 'regions/r-app', 'jquery', 'domReady'],
function (SiansPlanRouter, AppRegion) {
var run = function () {
// Global click event handler to pass through links to navigate
$(document).on("click", "a:not([data-bypass])", function (e) {
var href = { prop: $(this).prop("href"), attr: $(this).attr("href") };
var root = location.protocol + "//" + location.host + SiansPlanApp.root;
if (href.prop && href.prop.slice(0, root.length) === root) {
e.preventDefault();
Backbone.history.navigate(href.attr, true);
}
});
$.ajaxPrefilter(function (options, originalOptions, jqXhr) {
//options.url = '/api' + options.url;
});
// Create the global namespace region object.
window.SiansPlanApp = new AppRegion();
// Adds the authorization header to all of the API requests.
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader("Authorization", 'SiansPlan ' + SiansPlanApp.cookies.getSessionData());
});
// Load up session data if any is present yet - this can't happen until the XHR headers are set up.
SiansPlanApp.session.loadSession();
// Instantiate the router.
window.SiansPlanApp.router = new SiansPlanRouter();
// Boot up the app:
Backbone.history.start();
};
return {
run: run
};
});