SAPUI5 routing reset after browser refresh - javascript

I have developed a SAPUI5 master detail app. When I'm clicking on a master item, the corresponding detail page is shown (so far so good). The problem now is, that if I hit refresh (F5) in the browser, the last selected item is loaded (because of the URL parameter).
What I want to achieve is, to display the master list, but no item is selected. Instead a "select an item" page should be shown instead of the item detail page.
I've tried many things such as manipulate the routing, but none of this works. Any ideas on how to achieve this?

By doing that you give up the whole idea of deep-linking which is imho one of the major benefits of the routing. So think twice before doing so.
Anyways you could just reset the hash before initializing the router in your Component like this:
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/core/routing/HashChanger"
], function (UIComponent, HashChanger) {
"use strict";
return UIComponent.extend("sap.ui.demo.nav.Component", {
metadata: {
manifest: "json"
},
init: function () {
// reset the routing hash
HashChanger.getInstance().replaceHash("");
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// create the views based on the url/hash
this.getRouter().initialize();
}
});
});
BR
Chris

Related

Event Listener Duplication on Page Navigation SammyJS

I am using SammyJS framework for my routing.
As my title reads, I am having a problem with event listeners on my web page. In my code I have 4 main pages, clients/sensors/dashboard/status. On the sensors page it loads many different sensors. As well as being able to edit the sensor metadata, there is a button to add an owner to the sensor. The HTML for the page and button is loaded only when going to the edit page. It looks like this Here is a shaved down version of the Javascript...
(function() {
var app = Sammy.apps.body;
app.get('#/sensors', function(context) {
//context.render the index page...
});
app.get('#/sensors/edit/:sensor', function(context) {
context.render('/views/sensors/edit.html', function(view) {
$('#result').html(view);
$(document).on('click', '#addCustomer', function() {
//Add another dropdown customer list...
});
//More event listeners...
})
})
})();
What happens is that if I navigate away from that page, then back to it (doesn't have to be the same sensor edit page) and then click ADD CUSTOMER, it adds two. Each time I navigate away then back to, it adds 1 to the amount of dropdowns it adds. My first thought was that the event listeners are somehow being duplicated but I have no idea how to test for that. Any help is greatly appreciated.
Take a look at the jQuery "one" method which will ensure the event handler is only bound once: http://api.jquery.com/one/
$("#addCustomer").one("click", function(event) {
//Add another dropdown customer list...
});

Prevent user to navigate away with unsaved changes

I'm currently using Backbone.Marionette to create a SPA and in one of the views it is possible for the user to navigate away with unsaved changes. I can control some of these events, like buttons and menu options within the view that would take the user away, but some others would require to manipulate either Backbone.Router or work with the DOM events directly.
I already tried listening to beforeunload (doesn't work as the application is still loaded) and hashchange (doesn't work as you cannot stop the browser from navigating away). These solutions (1, 2, 3) don't work in this case, the Javascript is never unloaded.
Changing the Backbone.Router seems to be the best option, but because of how it is initialized I don't think it is possible to introduce this feature or at least I cannot find a way of doing it. This solution, for example, doesn't work because hashchange is not cancelable (you cannot call stopPropagation on it), and this other solution doesn't work because navigate is not defined on the Backbone.Router object.
Any suggestions?
I've managed to find a solution to this, although some more work is required. For this solution, I am assuming that you keep track when a view is dirty.
There are 4 main ways of moving out of a view;
Click on a link on the view
Click on link outside the view
Click on refresh or external link
Click on back/forward on the browser
1. Application link
This is the easiest case. When you click on your own link, you have to check if your view is dirty. For example, I have an in-app back button that is handled by a historyBack function. On the view:
historyBack: function() {
if (this.isDirty) {
answer = confirm("There are unsaved changes.\n\nDo you wish to continue?")
if (answer) {
this.isDirty = false
window.history.back()
}
}
else {
window.history.back()
}
}
2. Links outside your view
This type of interaction can be handled by extending the Router prototype's execute method, not the navigate method as proposed in other places.
There should be a variable somewhere accessible by the Router that stores the state of the view. In my case, I'm using the Router itself and I update this variable every time I change the dirty flag on the view.
The code should look something like this:
_.extend(Backbone.Router.prototype, {
execute: function (callback, args, name) {
if (Backbone.Router.isDirty) {
answer = confirm "There are unsaved changes.\n\nDo you wish to continue?";
if (!answer) {
return false;
}
}
Backbone.Router.isDirty = false
if (callback) callback.apply(this, args)
}
}
3. Refresh or external link
Refresh and external links actually unload your Javascript so here the solutions based on beforeunload (see question) actually work. Wherever you manage your view, I use a controller but let's assume it's on the same view, you add a listener on show and remove it on destroy:
onShow: function() {
$(window).bind("beforeunload", function (e) {
if (this.isDirty) {
return "There are unsaved changes.";
}
}
}
onDestroy: function() {
$(window).unbind("beforeunload");
}
4. Back/Forward on the browser
This is the trickiest case and the one I haven't figured out completely yet. When hitting back/forward, the user can navigate out of the app or within the app, both cases are covered by the code on 1 and 3, but there is an issue I can't figure out and I will create another question for it.
When hitting back/forward, the browser changes the address bar before calling the router so you end up with an inconsistent state: The address bar shows a different route to the application state. This is a big issue, if the user clicks again on the back button, after saving or discarding the changes, she will be taken to another route, not the previous one.
Everything else works fine, it shows a pop up asking the user if she wants to leave or continue and doesn't reload the view if the user chooses to stay.

Load external page on div and getting a specific url at the same time

I am developing a new website and while I want to get it done as easy to navigate as possible, I also wanted to use some kind of navegation with overlapping pages.
My idea was to have articles on the current page that will open on a floating div over the rest when clicked. That´s not really the problem because using jquery .load() it gets quite easy to do, but my problem is that it doesn't modify the current url, so it remains as www.myweb.com for example and I would like to have it like www.myweb.com/current-article when the article is opened. Once you have that specific url to the article, if it is shared, whoever open that link will get to the website with the article opened over the it.
I hope it all makes sense, but a good example can be found in USA Today or Play.Spotify
I am using umbraco 7 and javascript for the site. Any idea of how it could be done?
its called hash base navigation
location.hash = "#myHash"; //sets the url plus hash
Below is fired if user manually changes the URL or by using the back button
window.onhashchange = function()
{
if (location.hash === "#myHash")
{
doSomething();
}
}
This is actually a big and complex task to implement correctly.
I would advise you to use Backbone.Router http://backbonejs.org/#Router. It use history api in "new" browsers, with a fallback to hashtags in older browsers.
Some pseudo code:
First define your route. It will catch all pages under www.myweb.com/articles/*
var MyRouter = Backbone.Router.extend({
routes: {
"articles/:page": "loadPage"
},
loadPage: function() {
var div = $("#overlay");
div.html($.load("your page"))
div.show()
}
});
You would need to implement some logic to test if the loaded page is not under articles/.*
Init MyRouter when the page is loaded:
var router = new MyRouter();
router.start()
The overlay page will now open when you hit www.myweb.com/articles/cool-article
If you want to open the page from a parent page, simply call
$("button").click(function(){
router.navigate("articles/cool-article", {trigger: true});
});

How to properly load a modal (bootstrap), which content changes based on the url, in a Meteor template without blinking the background page

I want to load a modal where its content is based on an ID gotten from the current URL. The idea is that if that ID is not specified in the URL, and the rest of the URL remains the same, then no modal will appear (so no blinking) but if it was specified, then a modal will appear. In other words, open or close a modal based on the URL.
In my project, the URL without loading a modal is the next one:
/:projectId/kanbas
But if the URL is:
/:projectId/kanbas/:cardId
Then the modal will appear using bootstrap javascript. Moreover, if we close the modal or change the URL to the previous one (keeping the same projectId value), then the page should remain intact without any blinking.
In order to make it working, I have decided to create two controllers in the Iron Router. I am not sure if this is correct, but it I think it can be achieved with only one controller.
KanbanController = RouteController.extend({
layoutTemplate: 'normalLayout',
template: 'kanban',
waitOn: function () {
return Meteor.subscribe('Desktop', this.params.desktopId);
},
data: function () {
return {
desktop: Desktops.findOne({
_id: this.params.desktopId
})
};
}
});
And the other:
KanbanCardDetailController = KanbanController.extend({
yieldTemplates: {
KanbanCardDetail: {to: 'cardDetail'}
},
waitOn: function () {
return Meteor.subscribe('Card', this.params.cardId);
},
data: function() {
return {
card: Cards.findOne({
_id: this.params.cardId
})
,desktop: Desktops.findOne({
_id: this.params.desktopId
})
};
}
});
Notice that KanbanCardDetailController extends from KanbanController. Notice that the modal place is a template yield named cardDetail.
The first controller would be loaded with the first URL, and the second one when there is a modal.
As far as I tested, I think this approach is not correct because I am noticing a blinking when going from the first url to the second url (when loading a modal). I think the reason is that the waitOn callback from the second controller expects the Card document to be loaded before template rendering, but I was expecting that since the background template is not going to change it will not re-render it completely (because we already have it thanks to the previous url).
What am I doing wrong here? What would be the best meteorjs modal approach to open and close modals based on the URL?
My apologies if my English is not good enough but it is not my mother language. Thank you very much for your help and consideration.

How do I change this JavaScript?

I want to change the way that content is displayed on my website:
var FNav = {
init: function() {
$("a[href*=#]").click(function(e) {
e.preventDefault();
if($(this).attr("href").split("#")[1]) {
FluidNav.goTo($(this).attr("href").split("#")[1]);
}
});
this.goTo("home");
},
goTo: function(page) {
var next_page = $("#"+page);
var nav_item = $('nav ul li a[href=#'+page+']');
$(".page").fadeOut(500);
next_page.fadeIn(500);
How do I change this JavaScript, so I can have a proper back button functionality?
What I have tried (Unsuccessfuly). These are the solutions that I tried but without changing the javascript above. That is why I think none of them seem to work.
Using the History.js method described here:
https://github.com/browserstate/history.js/ I fill out all the steps and
enter the scripts to the header, however only the URL in the URL bar
changes when I click on a link. When I click the Back button, the URl
changes accordingly, but content doesn't load. When I enter a URL in
the URL bar, I get sent to the home page.
Ajaxify and Gist method
described here: https://github.com/browserstate/ajaxify Achieves the
same as above, same issues as well
Davis.js method described here:
https://github.com/olivernn/davis.js Achieves nothing upon completion
of the installation instructions. No change.
jQuery BBQ Plugin method
described here: http://benalman.com/projects/jquery-bbq-plugin/
Achieves nothing, no change upon loading the .js file in the header
of the website.
I read this article and understood it:
http://diveintohtml5.info/history.html
I'm not sure why you couldn't get Davis.js to work for you? Perhaps open an issue on the GitHub page.
If you want to use hash based routing with davis you need to include the hash routing extension. You then just need to include it in your page after davis.
The following setup should then allow you to handle routes
Davis.extend(Davis.hash)
Davis(function () {
this.get('/:page', function (req) {
FluidNav.goTo(req.params.page);
})
})
Assuming you have links in your page with the following
Page1
Page2
Davis will take care of handling the back button for you, so that if you click on the link for Page1 and then Page2, clicking on the back button will navigate to Page1 again.
If you have any problems please open an issue on the GitHub page detailing what you have and what isn't working and I can take a look at it.
The back button does not magically work. You need to code and listen for the event change!
In history.js, it shows you right on the front page:
// Bind to StateChange Event
History.Adapter.bind(window,'statechange',function(){ // Note: We are using statechange instead of popstate
var State = History.getState(); // Note: We are using History.getState() instead of event.state
History.log(State.data, State.title, State.url);
});

Categories

Resources