I am using, Angular.js 1.3, with ui-router.
I have 3 pages, page1.html, page2.html, page3.html.
When user click on page1, page2 will open, but I want to save the scroll state of page 1, where user was before clicking, so that after clicking on back button he lands on same scroll state.
To solve this I opened the page2.html over the page1.html, in iframe, and giving it absolute position to display over the page1.html, and I am using the:
history.pushState({}, '', '/page2.html');
to change the url. This implementation working fine.
Now when user click on link on page2.html, it should open the page3.html, like a normal link, for which I used :
$state.go("page3")
The problem is now the state chages, and page3.html loads, but url is still the /page2.html, url is not changing.
I even tried:
history.pushState({}, '', '/page3.html');
Still url is not changing. Anyone know why it's happening.
Even though the solution you are trying to implement is very creative, I would really choose another direction.
You've got various option, all of which rely on caching the scroll position somewhere. For example:
var scrollTopData = {};
function changeState (toState) {
var currentStateName = $state.current.name;
scrollTopData[currentStateName] = window.scrollY;
$state.go(toState);
}
Then the question remains what to do with the scroll position you have cached. $state.go returns a promise, so a solution would be:
var scrollTopData = {};
function changeState (toState) {
var currentStateName = $state.current.name;
scrollTopData[currentStateName] = window.scrollY;
$state.go(toState).then(function () {
if (toState in scrollTopData) {
window.scrollTo(0, scrollTopData[toState]);
}
});
}
Depending on your implementation, you could also use the answer of asgoth:
I haven't used it before, but angular has a $anchorScroll service. As to reloading the data, you could cache it using $cacheFactory, or store the data on a higher scope.
Another solution would be to use a stateChangeEvent. For example:
$rootScope.$on('$stateChangeSuccess',
function (event, toState, toParams, fromState, fromParams) {
// Do magic
}
);
My advice would be to use the promise returned by $state.go.
You can not have a change in url if you change the default behavior of angular. The part in url after hash changes automatically. You do not have changing urls because you force the browser to /somepage.html
try some thing like /#somepage.html
note the hash(#) added here.
Related
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});
});
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);
});
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);
});
I use routeProvider to define controlers and templates for my urls.
When I click on the link, which has the same url as is the actual location, nothing happens. I would like the reload() method to be called if a user clicks on such a link even if the location hasn't changed. In other words, if I set the location to the same value, I would like it to behave the same as if I would set it to different value.
Is there a way to configure routeProvider or locationProvider to do it automatically? Or what is the right approach to do this? This is stadard behaviour in round trip applications, but how to do it in angularjs?
I've asked it on google groups as well.
UPDATE:
This question is getting lots of views, so I will try to explain how I solved my problem.
I created a custom directive for linking in my app as Renan Tomal Fernandes suggested in comments.
angular.module('core.directives').directive('diHref', ['$location', '$route',
function($location, $route) {
return function(scope, element, attrs) {
scope.$watch('diHref', function() {
if(attrs.diHref) {
element.attr('href', attrs.diHref);
element.bind('click', function(event) {
scope.$apply(function(){
if($location.path() == attrs.diHref) $route.reload();
});
});
}
});
}
}]);
The directive is then used for all links in my app I want to have this functionality.
<a di-href="/home/">Home</a>
What this directive does is that it sets the href attribute for you based on di-href attribute so angular can handle it like always and you can see the url when you hover over the link. Furthermore when user clicks on it and the link's path is the same as the current path it reloads the route.
Add a / (slash) to the defined url in the route configuration
I met a similar problem today, I have a link in my web page and when I click it, I want the ng-view reload each time, so that I can refresh data from server. But if the url location doesn't change, angular doesn't reload the ng-view.
Finally, i found a solution to this problem. In my web page, I set the link href to:
test
But in the route config, I set:
$routeProvider.when('/test/', {
controller: MyController,
templateUrl:'/static/test.html'
});
The different is the last slash in url. When I click href="#/test" for the first time, angular redirect the url to #/test/, and load ng-view. when i click it second time, because the current url is #/test/, it's not equal to the url in the link (href="#/test") I clicked, so Angular triggers the location change method and reloads the ng-view, in addition Angular redirects the url to #/test/ again. next time i click the url, angular does the same thing again. Which is exactly what I wanted.
Hope this was useful for you.
You can add a _target='_self' on the link to forces the page to reload.
e.g.
{{customer.Name}}
Tested with version 1.0.5 and 1.2.15 on IE and Firefox.
Here's more information from AngularJS site :
Html link rewriting
When you use HTML5 history API mode, you will need different links in different browsers, but all you have to do is specify regular URL links, such as:
link
When a user clicks on this link,
In a legacy browser, the URL changes to /index.html#!/some?foo=bar
In a modern browser, the URL changes to /some?foo=bar
In cases like the following, links are not rewritten; instead, the browser will perform a full page reload to the original link.
Links that contain target element
Example: link
Absolute links that go to a different domain
Example: link
Links starting with '/' that lead to a different base path when base is defined
Example: link
you should use $route.reload() to force the reload.
I don't know if is there a 'automatic' way to do this, but you can use ng-click on these links
For people who are using AngularUI Router. You can use something like this:
<a data-ui-sref="some.state" data-ui-sref-opts="{reload: true}">State</a>
Notice the reload option.
Found the answer here: https://stackoverflow.com/a/29384813/426840
From #Renan Tomal Fernandes answer. following is an example
HTML
<a href="#/something" my-refresh></a>
JS
angular.module("myModule",[]).
directive('myRefresh',function($location,$route){
return function(scope, element, attrs) {
element.bind('click',function(){
if(element[0] && element[0].href && element[0].href === $location.absUrl()){
$route.reload();
}
});
}
});
I think it's a simpler approach.
.directive ('a', function ($route, $location) {
var d = {};
d.restrict = 'E';
d.link = function (scope, elem, attrs) {
// has target
if ('target' in attrs) return;
// doesn't have href
if (!('href' in attrs)) return;
// href is not the current path
var href = elem [0].href;
elem.bind ('click', function () {
if (href !== $location.absUrl ()) return;
$route.reload ();
});
};
return d;
});
Assuming You want to make all basic <a> links (without target attribute) reload on click and You use relative links in the href attribute (e.g. /home instead of http://example.com/home) You don't have to add any special markup to your HTML (comes handy when updating a site with HTML already written).
In my case if the url is same, nothing worked including $route.reload(), $location.path(), $state.transitonTo() etc.
So my approach was Using Dummy Page as follows,
if( oldLocation === newLocation ) {
// nothing worked ------------
// window.location.reload(); it refresh the whole page
// $route.reload();
// $state.go($state.$current, null, { reload: true });
// $state.transitionTo($state.current, $stateParams, {reload:true, inherit: false, notify: false } );
// except this one
$location.path('/dummy');
$location.path($location.path());
$scope.$apply();
}
You need to make '/dummy' module somewhere, the module doesn't do anything, it only change url so that next $location.path() can be
applied. Don't miss $scope.$apply()
I ran into this issue a moment ago, except for it was the home page '/'. I wanted a simple solution with less code. I just took advantage of the .otherwise method in the $routProvider
So in the html link looks like:
Home
since there is no '/home' page specified in the routProvider it will redirect to '/' via the 'otherwise' method. page with this set up:
.otherwise({
redirectTo: '/'
});
Hope it helps someone
I tried Wittaya's solution above using directive approach. Somehow the directive keeps throwing error. I end up with this solution
HTML
Devices
Controller
$scope.stateGo = function (stateName) {
if ($state.$current.name === stateName) {
$state.reload();
} else {
$state.go(stateName);
}
}
Just tried adding this
$(window).on('popstate', function(event) {
//refresh server data
});
and it works fine
I've got a simple app that parses Tumblr blog templates. It's modeled after their customization screen and contains a header with some configurable options and an iframe. Every time an option changes, the app reloads the iframe, serializing the config form and updating the iframe's src with the serialized data as a query string.
This all works fine, except every time this happens, I am only able to reload the main index page with the changed options. If the user wants to view, say, a single post page by navigating away from the index, the template renders that page, only with the default options.
In other words, the changed options do no persist while the user navigates the blog.
I've been able to have 'persisting changes' with an $('iframe').load() event like so:
$('iframe').load(function(){
updateTheme();
});
The only problem is, as you can tell, this would wait for the iframe to fully render the page using the default options, then re-renders it with the updated options. I mean... it works, but it's not really a great solution.
Does anybody know how I can prevent the iframe from loading, capturing the users desired location, then re-render the frame with the current options as represented in the header?
Any help would be greatly appreciated. Thanks in advance!
Are you hosting both the top-level page and the embedded iframe page? If so, there are some games you can play, but it's not pretty. For example you can rewrite links within the embedded iframe in order to pre-fill the config options, e.g. with something like:
$('iframe').load(function(){
$('a', $('iframe')).each(function() {
var new_url = this.attr("href");
new_url += config_options;
this.attr("href", new_url);
});
});
Here's what I came up with:
var p = top || parent;
(function($){
$('a').click(function(e) {
var prevent = e.isDefaultPrevented(),
is_local = p.isLocal(this.href),
is_hash = $(this).attr('href').match(/^#/);
if(prevent || ! is_local || is_hash) return;
e.prevenDefault();
p.updateTheme(this.href);
return false;
});
})(jQuery);
My worry was that I would be affecting the javascript events attached to <a/> tags by the user, but apparently jQuery will detect default prevented events, even if they weren't prevented with jQuery itself. I tested it like this:
document.getElementById('test-link').onclick = function() {
return false;
}
jQuery detects that the original event has been prevented, so I am just assuming I shouldn't continue.