Showing posts with label how to. Show all posts
Showing posts with label how to. Show all posts

Monday, July 19, 2010

Dojo templates & Google Maps InfoWindow

I have been building mashups using Google Maps since 2007 and one problem I had is passing in the content for these bubbles that show up when you click on a marker i.e. the InfoWindow. One big annoyance with it, is that you have to pass in the HTML content as a string when opening the marker. I don't like it because I have to intertwine HTML inside of JavaScript. So how can we make this better?

Dojo Templates


Dojo Templates is the number one reason I love this library, I like the PubSub mechanism I posted about last time but not as much as the templates.

Dojo Templates allow you to associate HTML template files with your widgets, that get instantiated when Dojo parses your page and constructs the widget, basically replacing the references to your widgets with the widget's markup in the HTML template file.

First: my infowindow.html template file

<div class="infoWindowContainer" dojoattachpoint="infoWindow">
<h1>${title}</h1>
</div>

Don't worry about that ${title} thing just yet, but I guess you already can see where I am going with this.

Second: my infowindow.js widget

dojo.declare(
"nael.widgets.infowindow",
[dijit._Widget, dijit._Templated],
{
templatePath: new dojo.moduleUrl('nael.templates', 'infowindow.html'),
constructor: function(){

}
}
);

In this example, my infowindow widget's constructor is empty.

Next we create the marker. Here we using dojo.forEach to loop over all the points that were returned with our AJAX response to fetch points. this.map is the object within this map controller that references the Google Map. (This is the same controller from the previous post on Dojo PubSub mechanism)

dojo.forEach(pois, dojo.hitch(this, function(p){
var marker = new google.maps.Marker({
position: new google.maps.LatLng(p.lat, p.lng),
map: this.map
});
var content = "<div class='infoWindow' dojoType='nael.widgets.infowindow' title='"+p.Pois.name+"'>
" ;
this.addInfoWindowToMarker(marker,content);
}));


The important part is the line where we add the content. Yes, we still need markup in there, but now its just a placeholder for the real markup. We can pass attributes like title into the placeholder for nael.widgets.infowindow. So anything that you want displayed in the info window becomes an attribute. This allows you to focus on content, and not worry about presentation just yet.

The last method we call addInfoWindowToMarker creates a Google maps listener on the marker and connects the info window to it. Note, that the info window here, is not the widget we created in the beginning. The one at the top is "nael's infowindow" and only serves the purpose of templating.

addInfoWindowToMarker: function(marker,content){
google.maps.event.addListener(marker, 'click', dojo.hitch(this, function(){
this.infoWindow.content = content;
this.infoWindow.maxWidth = 300;
this.infoWindow.open(this.map, marker);
}));
}


If you try the above, and click on the marker, the content will still be empty. Because this is a widget, it needs to be constructed. You need to tell Dojo when to parse the DOM to look for new widgets that you introduced since the last parse.


google.maps.event.addListener(this.infoWindow, "domready", dojo.hitch(this, function(){
dojo.parser.parse(this.mapCanvasNode.id);
}));


We don't want Dojo to go looking through the whole DOM for new widgets, we know where the widget was added. So we can just tell Dojo to look for widgets within the div referenced by the HTML id this.mapCanvasNode.id)

Finally, back to our infowindow.html template:

<div class="infoWindowContainer" dojoattachpoint="infoWindow">
<h1>${title}</h1>
</div>


We can now adjust the template as we please without trouble, and this sure is much cleaner than doing this like I used to for years.

var content = "<div class='infoWindowContainer'>";
content += "<h1>" + title + "</h1>";
content += "</div>";


Maybe one day Google Maps will support templating the HTML for InfoWindows internally, until then, I'm sticking to the above when I can.



The benefit of templating the InfoWindow becomes obvious when you are dealing with complex InfoWindows with functionality built into it such as sharing on social networks, embedded videos, AJAX requests, pictures, etc. All that stuff can be templated, and only the dynamic stuff that comes from the backend is passed through.

Of course, on top of being able to template your InfoWindow markup, it is much easier now to replace an InfoWindow with a version 2.0 of the InfoWindow. You just have to drop in the new and improved widget and template, then abide by the Pub Sub channels you have defined between the widget and the rest of the application.

Sunday, July 18, 2010

Dojo How To: Publish / Subscribe

I haven't been using Dojo for a very long time, just over a year now, but its time I blog about all the little great features I have learned.

One of the features I like the most in Dojo is the Publish / Subscribe mechanism. Its flexibility allows you to cleanly implement communication between different components like modules, widgets, portlets, etc.

Lets get down to business. Say I have two controllers, a map controller, and the main app controller. The map controller owns the map object in my application, in this case a Google Map object. The app controller owns the communication with the user, browser, AJAX, etc. When my app loads, I want the map to go right to the user's location. The map doesn't care where I get the coordinates from, it just needs the coordinates.


startup: function(){

//some other startup code

//subscribe to the event we will get back from the app when coordinates are available
dojo.subscribe("nael.controller.app.currentPosition", this, this.eventHandlers.updateMapCenter);

//when Im done starting up, yell to the app controller saying Im ready for coordinates
dojo.publish("nael.controller.app.requests",["getBrowserCoordinates"]);
},


So the map widget will initialize the Google Map I'm using, do some other stuff, and when it is done it will publish to the app controller's "nael.controller.app.requests" channel. The message it sends to the channel is an array of arguments. In this case it is the request ["getBrowserCoordinates"]. Your channel names can be anything, I just use the Dojo module path to that widget and end with a good description of the channel, i.e. "requests"

On to the app controller:

The startup function of the widget just subscribes to the required channels

startup: function(){
dojo.subscribe("nael.controller.app.requests",this,this.eventListener);
},


One event listener for the "nael.controller.app.requests" channel. Notice that the listener just passes it to the appropriate event handler, the one we passed in to the channel.

eventListener: function(event){
this.eventHandlers[event]();
},


Finally, the eventHandlers object which will contain all our actual event handlers. Here we have the "getBrowserCoordinates" handler which was the argument ["getBrowserCoordinates"] the map passed into the channel.

eventHandlers:{
getBrowserCoordinates: function(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
var coords = {lng:position.coords.longitude, lat:position.coords.latitude};

dojo.publish("nael.controller.app.currentPosition",[coords]);

}
);
}
},

Once the app controller receives the coordinates from the browser's geo location API, it publishes the coordinates to the "nael.controller.app.currentPosition" channel - which the map widget has subscribed to during its startup step. When the map receives that event, it tells the Google Map to re-center around the new point.

I'm doing it this way, to reduce the number of channels each module needs to listen to. I can easily bring the browser to its knees if I have a unique channel for each event I'm thinking of raising. Remember, kitchens get dirty one dish at a time. It makes sense to have one "requests" channel for the main app controller, (or every widget as a matter of fact) that all other components can just send requests to.

You may ask why I have a specific channel for the current position? I guess I could have done it similarly to the requests channel. However, I figured that all widgets will be asking the app controller for stuff, while not all widgets would need to know about the user's current position. If we have a "responses" channel that all widgets subscribed to, it could lead to a lot of unnecessary chatter amongst the widgets and too many event listeners. The second reason, is that the current position channel, may get pretty noisy if it is a mobile browser. Couple both reasons together, and you have a recipe for disaster

So why should you care about JavaScript Publish / Subscribe?


The same reason you would care about it for other technologies. Its a better and much more powerful interface between JavaScript modules. Without these channels and event listeners, you would have just called the getBrowserCoordinates method from the map, or worse, you would have called the GeoLocation API straight from the map. You don't have to use Dojo for this, other libraries also provide you with a pub sub mechanism, like YUI EventTarget. Other JavaScript libraries have it, or have plugins for it. Its a design pattern that makes sense.

Note: if you are still not using a JavaScript library for your web app development, you should seriously reconsider because you are wasting a lot of time re-inventing wheels and light bulbs.

Another example, say you need to add a new widget, instead of trying to figure out where you are all the right places to call a method in this widget from another, you just add the widget and subscribe to the event that is triggered. Change request done.

One final reason, Publish / Subscribe is an excellent way to build a mockup of application workflow. You can stub in some datsabase data and when the backend is ready, you just replace the stub module with the one that will listen to the right request channel, and publish to the right response channel. And as an added bonus, if you screw up the channels, nothing breaks, the messages just won't get passed and you won't see browser errors when functions aren't defined. It fails gracefully - which is important.

Saturday, May 02, 2009

URL SEO Best Practices & Liferay Virtual Hosting

It is very important to have a good URL if you are looking to improve your search engine page rank. At least if you are doing everything wrong in terms of SEO, having a good URL gives your website a little bit of hope.

Don't use numbers, page ids, user ids, session ids, or even odd characters
Think about it, if the URL does not speak your language, you will have no idea what content it may point to and hence there is very little reason for you to click it. For example, what gives you more reason to click through this:
this:
http://www.youtube.com/watch/The%20Hostage%20Audi%20Ad
or this:
http://www.youtube.com/watch/The-Hostage-Audi-Ad

Unfortunately Youtube does not have friendly URLs - but they don't need it since they are blessed with high page ranks - on the other hand the rest of us should care.

Don't have multiple URLs for the same page
Here is a thought imagine you are asking for directions to the highway and someone tells you:
  1. You can go North on Cawthra turn East on Burnhamthorpe, then North on Hurontario and you will see the ramps after Robert Spec....OR
  2. You can stay North on Cawthra until you get to the ramps when you reach East Gate
If I am already late or lost I really don't need to think about different routes to my destination. So stick to canonical URLs - that is the standard way of giving out directions i.e. http://www.elshawwa.blogspot.com and not http://elshawwa.blogspot.com

Also, if you are using friendly URLs for your content don't even think about leaving the non friendly URLs. Whatever reason you may have to think it is good, you are wrong. So having http://www.mywebsite.com/members/12 and http://www.mywebsite.com/members/nael is never a good idea. You may tell yourself "But this way I give my users more ways to access my content and they can choose what they like best". Do you know what happens to restaurants with overstuffed menus operating under the misguided notion that more choice is better and that to be successful in that business you need to offer every dish under the sun - even if it means offering french fries in an authentic Indian restaurant? Well, they end up on Kitchen Nightmares with Gordon Ramsay. What you would do in this case, is point these old URLs to a 404 page where you can direct the user to the right URL.

Bloggers: say no to a short URL
Goes back to the first point of having descriptive URLs:
  • http://www.elshawwa.blogspot.com/seo
  • http://www.elshawwa.blogspot.com/seo-url-best-practices
Which would you click on? Sure they are both descriptive but clearly one is better than the other. URLs can be multi word - just separate them with dashes - so make use of max size. On blogger I first publish the post with the keywords that describe my post the best as the title. I then edit it and save it with the proper title I want.

Say no to long URLs
Ok now you might think I am being indecisive here, but hear me out. For the sake of the argument, say you are going for an interview and you are lead through 6 doors, down 4 flights of stairs, through more doors, down an elevator into the basement, and finally through even more doors to meet the boss. What would you think at this point? I would think this is some kind of joke as clearly if this person was of any importance they would not be tucked away where the sun does not shine.
So back to URLs, if you see yourself doing something like:
http://www.mywebsite.com/news/world/europe/italy/soccer/ac-milan 
then think of that boss tucked away in the basement's basement - that is what your visitors will think.

So what is behind my reasons of publishing this? Mainly because by default content management applications generate URLs for you and attach all sorts of stuff to the url, from session ids, content ids, user ids, dates, times, you name it they have it. For example Liferay Portal does that, and if you go to Liferay's website you will see it. That /web/guest/home part is really annoying and adds no value to your URL. Last week I figured out how to get that rubbish out, and from now on this is the first thing I will do when promoting any Liferay portal to production. I'm actually surprised Liferay has it on their production site, and that is mainly the reason why I have incorrectly assumed it can't be removed.

Removing the /web/guest/home part from a Liferay URL is easy:
  • First go into your Communities Admin panel and decide on the Community you want to do this for
  • For your public facing site, that is your Guest community. So click on the Configure Pages action under Actions
  • One of the tabs is "Virtual Hosting" - click on it
  • Set up your public domain i.e. mywebsite.com
  • Clear out the field that appends /web after the domain. Usually you don't want anything in there unless you have multiple Communities in Liferay





Thursday, April 23, 2009

Pimp Your Slides Using Prezi.com

Learned about this neat presentation online application called Prezi. 100% online, free to use with some limitations (no private presentations, 100MB limit). Still awesome application, easy to learn and fun to use.

Love it.

Decided to give it a test drive and make a prezi out of my last topic "How To: Use Twitter Feed and Bitly to Promote your Blog"


Sorry about the bad quality images, I just saved them off the sites.



Link to it here or view full size/full screen: http://bit.ly/PoJB3

Wednesday, April 22, 2009

How To: Use Twitter Feed and Bitly to Promote your Blog

A couple of weeks ago I joined twitter in effort to experiment more with promoting my blog and get a bit more traffic.

Before I get into that, rule number one in generating traffic is

Content is King
Now I am not an expert in advertising via the social media nor advertising via any media, but I have learned a lot about promoting your blog and self using twitter over the past few weeks. As I learn more I will share more.

Seed your blog with content first
Don't start promoting if your blog is empty. Seed it with good, interesting and valuable content. Whether it is advice, comments on something you read in the news, challenges and how you overcame them (or if you did not, then what did you try and did not work) . So have some content on there so that when someone does visit, they are not welcomed by just a few short posts that do not add any value.

Creating good titles for your posts
When you create a post the first title you save it with is used to generate the URL for that post. So titles are very important and a good title will help that content get ranked on search engines and help your blog overall. A good tip I read was to first pick a series of words that will describe the new post best. For this post I picked "Use Twitter Feed Bitly Promote Blog Tips Suggestions". Publish the post with that title and then go back and add a proper title to it.

Join Twitter
Twitter is more than just reading what your friends are up to. Use twitter to stay up to date on news, weather, topics, technology, cars or whatever else interests you. Don't limit your follows to people you know, follow people based on what they are contributing - if what they posted today was valuable to you, odds are something they will post tomorrow will also be valuable. A lot of companies have joined twitter and promote their products and services on twitter. Follow these companies to get updates, news, and even help with their products.

Unlike Facebook where you can get 10,000 friends without even knowing 2% of them, you need to add value for someone to follow you, sure you can follow him, but the idea is to build your online identity; an interesting online identity. If your online identity revolves around following 10,000 people and not adding value then the opportunity you had there will not come again. Celebrities are a special case, even if they don't post that often. (If you are a celebrity and reading this post please leave a comment, if your're not still comment!)

Blog. Check. Twitter. Check. What's next?
So by now you have some good content on your blog, and you have built a small network on Twitter. Now what about Twitter Feed and Bitly?

Twitter Feed
Twitter Feed will publish your posts on Twitter on your behalf. Just sign up for an account and authorize it to do so by providing your username and password for Twitter or by authenticating your Twitter Feed username with Twitter. You can configure how often you want it to check your posts and what to prefix each post with.
Most if not every blogging tool provides you with a URL where your blog's RSS feed is published to. Simple point TwitterFeed to that RSS Feed and you are done.

Why use #hashtag for your blog posts?
I used #NESBlog to prefix my blog tweets giving me a way to easily search Twitter and see if any of my posts were retweeted. None so far, but I only started this yesterday. I'm optimistic I'll get my moment of fame sooner or later.

bit.ly
bit.ly is a url shortener. It also allows you to track each url and see how visits each got. Sign up for an account and configure TwitterFeed to use your bit.ly username and API key. There are many other services that do this, bit.ly was just the first I signed up for and so far its working out well.

Next time TwitterFeed reads your blog's RSS feed it will send your last post as
:


Note: If your posts show up on twitter with a "You must be authentication to a ..." the bit.ly API key is incorrect. Copy it and paste it back in again carefully.

That covers the set of tips I learned over the past few weeks at promoting my blog using Twitter, Twitter Feed, and Bitly url shortening.

Don't forget to comment and let me know what you think or share some other tips!