<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rommel Santor&#039;s Clog &#187; SPA</title>
	<atom:link href="https://rommelsantor.com/clog/tag/spa/feed/" rel="self" type="application/rss+xml" />
	<link>https://rommelsantor.com/clog</link>
	<description>my exploits and misadventures in programming</description>
	<lastBuildDate>Thu, 04 Feb 2016 12:56:01 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.0.38</generator>
	<item>
		<title>AngularJS Scroll to Top for New Pages on Single-Page-App</title>
		<link>https://rommelsantor.com/clog/2015/10/20/angularjs-scroll-to-top-for-new-pages-on-single-page-app/</link>
		<comments>https://rommelsantor.com/clog/2015/10/20/angularjs-scroll-to-top-for-new-pages-on-single-page-app/#comments</comments>
		<pubDate>Wed, 21 Oct 2015 07:53:35 +0000</pubDate>
		<dc:creator><![CDATA[rommel]]></dc:creator>
				<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[ngRoute]]></category>
		<category><![CDATA[SIngle Page App]]></category>
		<category><![CDATA[SPA]]></category>

		<guid isPermaLink="false">http://rommelsantor.com/clog/?p=444</guid>
		<description><![CDATA[As promised in my last post, I&#8217;m going to try to keep updating with things I learn on my trek down the long road to Angular-ville. As far as I could tell, there isn&#8217;t much info about the issue I was facing online, so either I&#8217;m doing something wrong (and if I am, for heaven&#8217;s [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>As promised in my last post, I&#8217;m going to try to keep updating with things I learn on my trek down the long road to Angular-ville. As far as I could tell, there isn&#8217;t much info about the issue I was facing online, so either I&#8217;m doing something wrong (and if I am, for heaven&#8217;s sake, don&#8217;t just sit there; please tell me!) or there aren&#8217;t as many people concerned with what I felt was a noticeable UX flaw in single page apps (SPA). To try to make clear my explanation of the issue, I&#8217;ll just walk through some steps that an end-user might experience with an SPA and how the page responds by default:</p>
<ol>
<li>User loads the home page. Angular fetches the template and inserts it into the ng-view.</li>
<li>User scrolls down the page a bit, say 600px.</li>
<li>User clicks a link to another page. Angular fetches the requested page&#8217;s template and inserts it into the ng-view, but the browser stays scrolled 600px, so the user doesn&#8217;t actually see the content they loaded.</li>
<li>User clicks the browser&#8217;s back button and sees again the content they were at before clicking the link.</li>
</ol>
<p><span id="more-444"></span>
<div style="float: left; margin: 0 10px 10px 0;"><script type="text/javascript">// <![CDATA[
google_ad_client = "ca-pub-7639656561235411";
/* Square (250x250) */
google_ad_slot = "8522038794";
google_ad_width = 250;
google_ad_height = 250;
// ]]&gt;</script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">// <![CDATA[

// ]]&gt;</script></div>
</p>
<p>The quick, dirty, incomplete &#8220;solution&#8221; to this might be to simply detect when the route is changing and auto-scroll to the top of the window in all cases. However, if we did that, upon clicking back in step 4 above, the browser would scroll back to the top of the page instead of landing the user back where they came from. So what we need is a real solution that will auto-scroll to the top of the page when a *new* page is loaded, but allow the browser to restore the scroll position for any page that was navigated away from.</p>
<p>My first thought was to just store the most recently viewed page and detect a new page load, compare the new page to the most recent page, and if it&#8217;s different, scroll to the top. This doesn&#8217;t work, however, when the user clicks back more than one page.</p>
<p>My second thought was to just store the history of pages viewed and only scroll to top if the newly loaded page is not in that history. This, however, doesn&#8217;t take into consideration that a user may go back in the browser then go forward again, and if they go forward to a page they just came from, we don&#8217;t want that scrolling away on them.</p>
<p>No, what we need is to store the history of all pages loaded both backward and forward. Below is the solution I implemented which stores the current page, a stack for all backward pages, and another stack for all forward pages. When the location changes, the new URL is compared to the top of the backward stack and the forward stack. If it&#8217;s either one, the matching element is popped and we leave the scroll position where it is. If it&#8217;s neither, the user must be linking forward into a new path, so the forward stack is emptied.</p>
<p>Overall the solution works pretty nicely. The only less-than-usual thing to be aware of is that if you click a link that takes you backward or forward one page (i.e., instead of clicking the back button back to the homepage you click a &#8220;home&#8221; link), it will look to the code like you used your back button and leave the scroll position unchanged, but I actually like that behavior. It makes the whole SPA thing feel much more solid.</p>
<p>[<a href="https://github.com/rommelsantor/ng-bwdfwdscroll" target="_blank">GitHUB &raquo;</a>]</p>

<pre class="js:nocontrols:nogutter">&lt;script&gt;
// first thing's first; initialize the app
var app = angular.module('yourAppName', ['ngRoute']);// '

// use $rootScope since this is for application-wide behavior
app.run(function _bwdFwdDetect($rootScope, $location) {
  $rootScope.path = $location.path();// stores only the current page being viewed
  $rootScope.pathBwd = [];// stores up to the last page viewed before the current one
  $rootScope.pathFwd = [];// stores starting with the page viewed before coming back to the current one

  // detect that the location is about to change
  $rootScope.$on('$locationChangeStart', function(event) {
    var newPath = $location.path();// take note of the new path (using path instead of url is good)

    // grab the last backward page for comparison
    var bwd = !$rootScope.pathBwd.length ? null :
      $rootScope.pathBwd[$rootScope.pathBwd.length - 1];

    // grab the last forward page for comparison
    var fwd = !$rootScope.pathFwd.length ? null :
      $rootScope.pathFwd[$rootScope.pathFwd.length - 1];

    // if the new page is the last backward page, assume the user went "back"
    if (bwd == newPath) {
      // it's no longer the last backward page, so remove it
      $rootScope.pathBwd.pop();

      // and push into the forward stack the page we're just leaving to go backward from
      $rootScope.pathFwd.push($rootScope.path);
    }
    // the new page is the last page we came backward from, assume the user went "forward"
    else if (fwd == newPath) {
      // it's no longer the last forward page, so remove it
      $rootScope.pathFwd.pop();

      // and push into the backward stack the page we're going forward away from
      $rootScope.pathBwd.push($rootScope.path);
    }
    // we didn't go back or forward; assume it's a new forward trail being started
    else if ($rootScope.path != newPath) {
      // remember the page we're leaving as our last backward page
      $rootScope.pathBwd.push($rootScope.path);

      // empty out our forward stack since we're now starting a wholly new forward path
      $rootScope.pathFwd = [];

      // this is what it's all about; scroll to the top of the freshly-loaded page
      $('html,body').animate({ scrollTop: 0 }, 'slow');
    }

    // now that we're done with our comparisons, we can remember our new current page
    $rootScope.path = newPath;
  });
});
&lt;/script&gt;</pre>

<p><script type="text/javascript">// <![CDATA[
//< ![CDATA[
$("pre").attr({name:'code'});
// ]]&gt;</script></p>
<p>Of course this whole &#8220;new page detection&#8221; thing I&#8217;m doing doesn&#8217;t only have to be used to scroll to the top of the page. There could be any number of things you might want to do depending on whether the route is changing backward or forward or down a new path.</p>
]]></content:encoded>
			<wfw:commentRss>https://rommelsantor.com/clog/2015/10/20/angularjs-scroll-to-top-for-new-pages-on-single-page-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AngularJS Auto-Focus Input After Router Template is Loaded in SPA</title>
		<link>https://rommelsantor.com/clog/2015/10/14/angularjs-auto-focus-input-after-router-template-is-loaded-in-spa/</link>
		<comments>https://rommelsantor.com/clog/2015/10/14/angularjs-auto-focus-input-after-router-template-is-loaded-in-spa/#comments</comments>
		<pubDate>Wed, 14 Oct 2015 10:46:16 +0000</pubDate>
		<dc:creator><![CDATA[rommel]]></dc:creator>
				<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[autofocus]]></category>
		<category><![CDATA[ngRoute]]></category>
		<category><![CDATA[SIngle Page App]]></category>
		<category><![CDATA[SPA]]></category>

		<guid isPermaLink="false">http://rommelsantor.com/clog/?p=422</guid>
		<description><![CDATA[It&#8217;s been just a little while (oh, only about 3.5 years) since I last published any new posts here. Too busy. At the moment I&#8217;ve been delving head-first into some modern frameworks like AngularJS and Bootstrap, so I plan to document things I learn along the way that I think will help other developers also new [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>It&#8217;s been just a little while (oh, only about 3.5 years) since I last published any new posts here. Too busy.</p>
<p>At the moment I&#8217;ve been delving head-first into some modern frameworks like AngularJS and Bootstrap, so I plan to document things I learn along the way that I think will help other developers also new to these technologies who might come up against some of the same issues or who might just find interesting the things I&#8217;m discovering.</p>
<p>Without further ado, this is my first of such posts, which is a little more advanced than just starting to learn everything from scratch, but I do plan to back-track and cover more in future posts the basics of using AngularJS.</p>
<p><span id="more-422"></span>
<div style="float: left; margin: 0 10px 10px 0;"><script type="text/javascript">// <![CDATA[
google_ad_client = "ca-pub-7639656561235411";
/* Square (250x250) */
google_ad_slot = "8522038794";
google_ad_width = 250;
google_ad_height = 250;
// ]]&gt;</script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">// <![CDATA[

// ]]&gt;</script></div>
</p>
<p>If you&#8217;re developing a Single Page Application (SPA) with Angular, you may find that loading an HTML fragment with a form input that should be auto-focused might not work. Assuming you&#8217;ve already set up ngRoute to dynamically control the routing of your application, the simplest way I came up with to set focus on an input after ensuring the HTML template is fully loaded is to define a callback for <code>$routeChangeSuccess</code> which is invoked after the route has finished changing. The best place to respond to that event is in a run block, since it&#8217;s an application-wide action we&#8217;re taking. The trick is that the DOM may still not be ready for you to set focus at that point, but setting focus inside a $timeout will ensure the $digest loop has ended.</p>
<p>The other, more obvious decision was to ensure the input I want to auto-focus has the &#8220;autofocus&#8221; attribute so it can be targeted using a simple jQuery selector. Below is a bit of sample code demonstrating what I just described with comments, so hopefully it&#8217;s all pretty obvious to you (and it <i>should</i> be if you&#8217;re using Angular yourself).</p>
<p>[<a href="https://github.com/rommelsantor/ng-dynautofocus" target="_blank">GitHUB &raquo;</a>]</p>

<pre class="js:nocontrols:nogutter">&lt;script&gt;
// first thing's first; initialize the app
var app = angular.module('yourAppName', ['ngRoute']);// '

//... do other things like set up your routing (not covered here)

// set up a Run Block
app.run(function _forceAutofocus($rootScope, $timeout) {

  // tell Angular to call this function when a route change completes
  $rootScope.$on('$routeChangeSuccess', function() {
    // we can't set focus at this point; the DOM isn't ready for us

    // instead, we define a callback to be called after the $digest loop
    $timeout(function(){
      // once this is executed, our input should be focusable, so find (with jQuery)
      // whatever is on the page with the autofocus attribute and focus it; fin.
      $('[autofocus]').focus();
    });
  });

});
&lt;/script&gt;</pre>

<p><script type="text/javascript">// <![CDATA[
//< ![CDATA[
$("pre").attr({name:'code'});
// ]]&gt;</script></p>
]]></content:encoded>
			<wfw:commentRss>https://rommelsantor.com/clog/2015/10/14/angularjs-auto-focus-input-after-router-template-is-loaded-in-spa/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
