AngularJs SEO with Prerender.io

Built a beautiful, snappy, user-friendly website using AngularJS, and now you want more users to start browsing it. But, AngularJS which fully embraces the asynchronous model creates problems for Google’s crawlers and this is how the google indexes an angularJs sites:

Angular Seo

Obviously, this is not what you want your users to view. Now what do we do?

What to do?

Thought a lot 🙄 but still no success???? 🙁

Nevermind 🙂 , here’s how to get your site to the top of the search results.

Lets start  by setting up your prerender server :

1.git clone https://github.com/prerender/prerender.git. Then cd Prerender, to go to the directory
2. npm install Instal module dependencies
3. node server.js Run the service (available nohup node server.js & so that it runs in the background)

Client Side: Follow Google’s Javascript crawling protocol with hashbang

For reference, the full protocol is here.

Google’s Javascript crawling protocol is meant for websites that render using Javascript or need to wait on AJAX calls to complete. If you follow Google’s protocol, Google will send a request to your server with a modified url so that you can return the static HTML version of the page rendered by prerender server. Here’s how:

If you use urls with #

Nothing after the hash in the url gets sent to your server. Since Javascript frameworks originally used the # as a routing mechanism, that’s the main reason why Google created this protocol.

Change your urls to #! instead of just using #.

angular.module('exampleApp').config([  
    '$locationProvider',
    function($locationProvider) {
        $locationProvider.hashPrefix('!');
    }
]);

Google looks for #! urls and then takes everything after the #! and adds it as a query parameter called _escaped_fragment_. That way, Google effectively fixes the problem of everything after the hash not being sent to your server.

Google finds urls like this:

http://www.example.com/#!/product/123

Google sends a request to your server like this:

http://www.example.com/?_escaped_fragment_=/product/123

, intercept the request on the nginx server like this (imagine the prerender server resides on port 3000 whereas your backend server on port 4000) :

server {
 listen 80;
 server_name www.example.com example.com;
 location ~* / {
   set $prerender 0;
   if ($http_user_agent ~* "baiduspider|
       twitterbot|facebookexternalhit|
       rogerbot|linkedinbot|embedly|
       quora link preview|showyoubot|
       outbrain|pinterest|slackbot") {
   set $prerender 1;
 }
 if ($args ~ "_escaped_fragment_") {
   set $prerender 1;
 }
 if ($uri ~ "\.(js|css|xml|less|png|jpg
     |jpeg|gif|pdf|doc|txt|ico|rss|zip|
     mp3|rar|exe|wmv|doc|avi|ppt|mpg|
     mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|
     swf|dat|dmg|iso|flv|m4v|torrent)") {
   set $prerender 0;
 }
 if ($prerender = 1) {
  rewrite .*/$scheme://$host$request_uri?break;
  proxy_pass http://localhost:3000;
 }
 if ($prerender = 0) {
   proxy_pass http://localhost:4000;
 }
}

Thats it!

SEO issue solved

Now you’re well on your way to lots of new users, courtesy of the search engines! Good luck  😉

How to implement SwiperJs slides with IONIC 1.x using ion-slides

With the release of V1.2, ionic announced the support for SwiperJs with the new directive ion-slides. The older ion-slidebox will be deprecated over the new ion-slides. As there was very less documentation and a lot of developers confused on how to get an instance of the swiper in your controller, I decided to write this blog post.

Here’s the forum where I posted the original answer

AngularJS Controller

 

How to use ion-slides in IONIC
AngularJS Controller


HTML

Screen Shot 2016-07-05 at 11.38.31 AM
HTML part

REFERENCES

  1. It can be made much more extensible. The different options available for
    the swiper like effect, speed ..etc is here (Look under Swiper parameters)
  2. Take a look at the api docs here
  3. Awesome Swiper Demos here
  4. Here’s an example gist

With IONIC 1.2 release’s support of swiper, the mobile hybrid developers could now unleash the power of SwiperJS!

Happy coding!

Top 9 mistakes to avoid for Angular v1 developers

AngularJs has seen a tremendous improvement over the last couple of years. With a strong core team, thousands of supporting modules in the ecosystem and backed by Google, AngularJs is the most favourite UI javascript library currently!

But ‘with great powers comes great responsibilities!‘ More and more developers are flocking to AngularJs everyday and it becomes more important to understand the nuances and code accordingly.  Here are some top mistakes developers make while coding in AngularJs and the steps to avoid them!

  1. Modularity

    Keeps angularjs code modular - By Srijith Radhakrishnan, SquashApps
    Keeps angularjs code modular

    Oh please! Please put stuff where they belong! We all have our tendencies to piece together a quick app for a client demo or something and put all stuff in the controllers. You wont believe me how many times I have seen a controller go beyond 500 lines of code!

    • Use Services/Factories wherever required
    • Directives are meant for DOM related stuff. Use them!
    • Keep a watch on the size of the file. Its sometimes as simple as that. If you think it is getting big, time to refactor!
  2. Overloading the RootScope
    Dont overload the rootscope
    Don’t overload the rootscope

    Lot of us think that rootScope in the angular world is some kind of a goods train and we could load all the stuff we want to on it and it would be delivered at the right spot! (Forgive my analogy!) Hope you get the point. When we want to share information between controllers or other modules, the first thing that comes to the mind is ‘Why not use rootscope!?

    • Well, at first, its all going to be fine
    • Then one day, rootScope is going to be a huge datastore on your client side application
    • This could overload angular’s digest cycle in watching these variables and cause a performance degradation
    • You could argue that the performance hit is going to take a while, but hey! It is going to happen!
    • Check out this wonderful explanation on SO about this
  3. Too many watchesDont have too many watches in AngularJSKind of relates to my previous point. Watch is usually used to ‘watch’ a variable and do operations when its value changes. The problem is that ‘watch expressions’ are run multiple (!) times during angular’s digest cycle. Boils down to a simple math. If 1 watch expression is triggered multiple times during a digest cycle, just imagine the performance hit that an app with 100 watch expressions takes!
  4. Broadcasting (events) like a radio station!
              There may be scenarios where broadcasting an event is the right way to go. But sometimes, we tend to solve the problems in the easiest way. After all, we are humans. (or are we??) . If there is a need to transfer data from modules within the app, you could use a factory, or a location parameter in the url or rootscope (sparingly!) or session/local storages or using controller inheritance (so in your child controller you would just do $scope.$parent.getSomeData())
  5. Cancelling Timeouts/IntervalsThere might be a scenario where you might need to add a $timeout to perform some actions after a stipulated time.
    Although timeouts could be totally avoided, if you really have to deal with them, cancel them after the controller is destroyed. You could listen to the controller’s destroy event like this and cancel the timeouts/intervals you created within the controller

    // Creating a timeout
    $scope.timer = $timeout(function() {
           doSomething();
    }, 1000 );

    // Cancel it when the controller is destroyed
    $scope.$on(‘$destroy’, function (){
        $timeout.cancel($scope.timer);
    });

     

  6. DOM manipulations

    DO NOT absolutely do DOM manipulations in controller. Why?

    • DOM manipulations are slow
    • Controllers are not meant for DOM manipulations!
    • Directives are meant for just that.
    • And if you have a directive for DOM manipulation you could reuse it in any other controller. But you cant reuse it if its within the controller itself
  7. Understanding callbacks
    Understand callbacks before you jump into Angular. A lot of times timeouts have been created for the lack of understanding on callbacks. Take for example this simple scenario

    • You want to call a REST api and get a response
    • Then print something on the html. Simple.WRONG: $http.get(some_URL, function(success){}, function(error){});
      $timeout(function(){
      #Hey it takes 5 secs for the above call to complete. Let me just do a timeout!
      $scope.message  = ‘Call success’;
      }, 5000);Btw, this does not exactly execute in 5 seconds. There will be a blog soon on why! (I know you know it already, but we still want to write about it!)RIGHT : 1)  $http.get(some_URL, function(success){$scope.message = ‘Call success’;}, function(error){$scope.message = ‘Call failed’;});(OR)2) var promise = $http.get(some_URL);
      promise.then(function(success){$scope.message = ‘Call success’;}, function(error){$scope.message = ‘Call failed’;});Read more about promises here and about callbacks here
  8. Dependency Injection
    • We found this the hard way but dont inject dependencies for your angular module like thisBAD app.controller(‘MyController’, function(Dep1, Dep2){
      });GOOD app.controller(‘MyController’, [‘Dep1’, ‘Dep2’, function(Dep1, Dep2){
      }]);ok Why do I have this redundancy? Why are my dependencies first injected as inline array annotations and then as parameters to the controller?
      Simple answer
      : Minification
      Detailed: JS minifiers rename the arguments before minification and then  Angular has no absolute idea of what those arguments are! There are two solutions to this
    • Either follow the implementation in the ‘Good’ section above (or)
    • Use tools like ng-annotate which would automatically do the inline array annotations for you.Read more here
  9. JSHINT (static code anaylis) and Tests!

    JS Linting and TESTS are your friends!
    JS Linting and TESTS are your friends!


    Often times, we ignore these guys as we need to meet that one deadline or go to the market before the other guy does! So much that we forget the good stuff that they bring. I am just going to jot down the advantages

    • JSHINT has caught more bugs than all the fish ever caught in human history (Crazy Analogy again. Forgive!)
    • If you had a friend who would sit with you all day while you code and test all your stuff, would you be happy?
      1. If the answer is YES, Karma is the name! Not the literal meaning, but this awesome test runner
      2. If the answer is NO, you need some socializing 😉(Note: if you have a large inherited codebase with no absolutely no tests, dont go about making that your priority! Of course tests are important but not more than the core business! Allocate may be 20% of your time weekly to write tests for the old code and dont write any new code without tests. Eventually you would be boasting a decent test coverage!)I am planning to do a blog later on how Panitr maintains near perfect test coverage

Following good coding practices not only enriches your knowledge but also helps the future app maintainer to not go mad and file a lawsuit against you 😉