Monday 17 September 2007

Let's play with Sortables

Ok, this seriously took me such a long time to figure out. I'm trying to create sortable menu customisation, like the one you see in your personalised Google homepage. The tutorial I followed was the one provided by scrtip.aculo.us.

First of all, you have to create a block container. In this case I used a div element. Its child div elements are the ones that I want to move around, and sort according to the user's preference.

For example,

<div id="container">
<div id="left" style="float:left;width:50%;background:red;">Left Div</div>
<div id="right" style="float:right;width:50%;background:green;">Right Div</div>
</div>

Left Div


This displays the two child divs next to each other, like a table format but without using table tags. Using Rails helper,

<%= sortable_element "content_container", :tag => "DIV", :constraint => false %>

This specifies that the sortable list is the container which has lists in it. By default, the li tag is treated as the list, that is why you have to specify :tag => "DIV". The :constraint => false all allows free movement when dragging (default is vertical).

After doing so, you'd think that it should all work perfectly. Not really to be exact. This bug caused me lots of trouble to figure out as I had no idea why it wasn't working. You would be able to drag the elements, but it just would not sort.

Fiddling around for some time, I came across what the problem was. When the right div is sorted to the left, the css tells it to float right meaning the css is preventing the sorting from happening. In other words, when it is in the left position, it wants to float right. Obviously this is incorrect.

So I changed the right div to float left, which will still keep the alignment correct. This solved the problem pretty much. However, you are only able to sort the elements from right to left, and not the other way around. I still can't figured out why and how to fix this.

Try it out here,

Left Div
Right Div



[edit date="18/09/07"]

Hmm, a bloody weird discovery. Left to right actually works! It isn't as responsive as right to left though. Left to right is harder to sort, but if you position it kinda properly, it works.

Weird.

[/edit]

Rendering Global Partials

Just a quick note to point out. If you want to render a partial thats available globally (by default its local), all you have to do is create a folder in ..\app\views.

Assuming we created a folder called shared and a partial called _menu.rhtml, you can then access it by doing,
render :partial => "shared/menu"

Simple.

Dealing with JavaScript Object Notation (JSON)

Ruby on Rails provides an easy way to render JavaScript Object Notation (JSON) without actually having to deal with itty gritty details of forming the object literal.

# Controller
render :json => { :name => "Danny", :title => "Mr" }.to_json

produces

{ name: 'Danny', title: 'Mr' }

Once you have done so, you can create an object in javascript by running

var person = eval("(" + request.responseText) + ")");

You can then access the attributes by going person.name and person.title which returns "Danny" and "Mr", as expected.

Saturday 15 September 2007

Thesis Update!

It's been a busy past few weeks, getting familiar with the prototype and scriptaculous javascript framework but it has paid off. Been looking into user interface design where there can be more interaction with the user.

What I've been trying to do is to try and simulate a 'desk' environment, where you would normally use your hands to move things around depending on what you want to do. What that means is to make things draggable, collapsible, expandable etc.

As you can see below, I've added drop down menus for menu selection. I've also made the right hand navigation bar draggable, as well as the main content in the middle. Can't really see it from a picture I guess. The main content and navigation bars are also expandable and collapsible.
I've also rethought the design of performing actions that does not require a lot of data loading. In other words, utilizing AJAX to perform actions without really leaving the page which means no refresh.

Created a hovering div that disables the content below, and displays the content above it depending on what type of action it is. I used scriptaculous' effects (Fade and Switchoff) to display and close the hover div.

I've only tested this on a Firefox environment as the browser has great plugin tools (Firebug) which makes debugging JavaScript, HTML and CSS a walk in the park.





Over the next couple of weeks I plan to add drag & drop features which will allow more user interaction by using the mouse which in return, simulates using a hand to do stuff. Hopefully there will be minimal user input where it isn't necessary.

Hopefully I'll have the chance to post up how I did all these things soon.

Wednesday 5 September 2007

JavaScript Validation

I've spent some time writing a validation framework for checking user text field inputs on the client-side.

The parameters it takes are:
function(valField, type, options) where valField and type are required. valField is the element to validate, and type is a choice of ["required", "exists", "update", "password"].
Options is optional and is an object. In the options object, it takes these attributes:

infoField: element - the element to display messages to the user,
repeatField: element - the element which is used to repeat (like repeat password or email),
required: boolean - specifies whether input is required (defaults to true),
ajax: boolean - specifies whether Ajax is used to communicate to the server (defaults to false),
url: string - url for Ajax call (only works if ajax: true),
column: string - the column in the table (only works if ajax: true),
idkey: string - the id parameter (only works if ajax: true)

Examples of different types of usage:

  • new Validator(this, 'exists', { infoField: $('project_name_span'), ajax: true, idkey: $('user_id').value , column: 'name', url: '/project/check_exists' });
  • new Validator(this, 'required', { infoField: $('project_name_span') });
  • new Validator(this, 'update', { infoField: $('user_lname_span'), ajax: true, column: 'lname', idkey: $('user_id').value, url: '/user/ajax_update' });
  • new Validator(this, 'password', { infoField: $('user_pwd_span') });