Element creation in jQuery 1.4

Before I start, I'd like to say hats off to the jQuery team, as 1.4 is looking beautifully so far. I've already ran into about 3 problems that were fixed with the new features in 1.4, like the parentsUntil() function. However, the post today will be about the new element creation system using the core jQuery method, $(). In 1.4 you can now create an element by passing the tag name as the first parameter, and an object of attributes as the second. The parameters should be a named value pair of attributes to be appended to the element, like the ID, class, value, title, text, etc. View more about it in the official documentation.

var input = $('<input>', {
	id: 'FormUsername',
	name: 'username',
	type: 'text',
	value: 'Enter your username...',
	focusin: function() {
		$(this).val('');
	}
}).appendTo(someElement);

This new approach opens up tons of new opportunities and uses, compared to the previous usage of writing out the whole element, or doing it the old school way with document.createElement().

// We don't need you anymore outdated code!
var input = $('<input type="text" id="FormUsername" name="username" value="Enter your username..." />').focusin(function() {
	$(this).val('');
}).appendTo(someElement);
var input = document.createElement('input');

Just earlier today I needed a way to dynamically create specific elements, with a specific set of attributes. This new functionality allows me to easily do that by just passing an object, and wallah! Here's just a quick example of some of the usages. It can get quite handy when you need to loop through objects/arrays and create elements accordingly.

// Some data returned from an AJAX call
var data = jsonObj;
// Loop it and build the fields!
$.each(data, function(key, params) {
	var parent = $('<div>', {
		id: 'Comment_'+ params.id,
		text: params.message
	}).addClass(params.className);
	var child = $('<a>', {
		href: '/user/profile/'+ params.user_id,
		text: params.username
	}).prependTo(parent);
	parent.appendTo('#comments');
});

In the code above we are getting a list of comments as a JSON object, then iterating over the object and dynamically creating the elements. Albeit this is just a quick and dirty example, as it would be best to just have the AJAX spit out the HTML, but you get the picture.

You may have noticed that I used addClass() on the parent instead of declaring a param of "class". You can use class, but class must be quoted as it causes problems in IE (of course). Personally, I just stick to addClass() as it will not cause any problems in any browser. Furthermore, if you want to apply some content text (like the link text, not the title), you would pass a param of "text".

The new element creation is just one of the more awesome features in jQuery 1.4. I hope you guys enjoy it as much as I am; and if you are not yet, get to it! What are you waiting for??

Proxy Search - Doing a search while using named parameters

While working on a project of mine, I wanted to be able to do a search, but have the post data be retained as named parameters (basically like a $_GET string). My first attempt was setting the form method to get, and trying to convert the $_GET by doing some mod_rewrite magic. This didn't work out at all, mainly because I couldn't get it to work for multiple named parameters. The only alternative I found was suggested by someone in the Google groups; they suggested posting a form to another action to deal with the logic, and finally redirect applying named parameters. This works perfectly, albeit adding an extra step in the process.

The process I created is something id like to call a Proxy Search. The following code should be placed in your app_controller.php file, so that it can be used as a global search by all controllers. You should not overwrite the proxy() action, unless you want specific logic in a certain controller.

/**
 * Sorts the named params for a posted page
 * @param string $model
 * @access public
 */
public function proxy($model) {
	$data = array_map('urlencode', $this->data[Inflector::camelize($model)]);
	$referer = explode('/', trim($this->referer(), '/'));
	$router = array_merge(array('controller' => $referer[0], 'action' => $referer[1]), $data);
	$this->redirect($router);
}	
/**
 * Applies the named params to the controller data
 * @return array
 */
public function _proxyGather() {
	return array_map('urldecode', $this->params['named']);
}

I will quickly try to explain how the form works. For example, if we are in the Users controller and want to do a search using the search() action, the forms url should point to /users/proxy/user instead of /users/search. Now shouldn't it be /users/proxy you ask? Well no, we need to know what model should be used in $data, so we need to pass an argument for the model that is used for the form (during $form->create()).

// UsersController::search()
echo $form->create('User', array('url' => array('controller' => 'users', 'action' => 'proxy', 'user')));
// If searching through books
echo $form->create('Book', array('url' => array('controller' => 'books', 'action' => 'proxy', 'book')));

Now back in the search() action, we need to grab the named params and pass them to the fields in the form. We do this by using the _proxyGather() method. No model name needs to be passed to _proxyGather(), simply because the data is pulled from the controller.

/**
 * Search players
 * @access public 
 */
public function search() {
    $this->data['User'] = $this->_proxyGather();
    $this->pageTitle = 'Search Users';
    $this->set('results', $this->paginate('User'));
}

You can also have this interact with pagination quite easily. All you need to do is add some conditional logic in the controller, and pass the data to paginator.

/**
 * Search players
 * @access public 
 */
public function search() {
    $this->data['User'] = $this->_proxyGather();
    if (!empty($this->data['User']['username'])) {
        $this->paginate['User']['conditions']['User.username LIKE'] = '%'. $this->data['User']['username'] .'%';
    }
    if (!empty($this->data['User']['firstName'])) {
        $this->paginate['User']['conditions']['User.firstName LIKE'] = '%'. $this->data['User']['firstName'] .'%';
    }
    $this->pageTitle = 'Search Users';
    $this->set('results', $this->paginate('User'));
}
// In the views using $paginator
$paginator->options(array('url' => $this->passedArgs));

I hope this has been helpful to someone who was looking to do a search system using named params, I know I had fun building it! I was thinking of turning this into a component, but have not figured out a way to do so yet, enjoy anyways!

Method for grabbing named parameters

So I never worked much with named parameters before in CakePHP, but recently I needed a way to filter data. It seemed passing many arguments to the action wasn't the best or correct approach. My initial thought was to find a method that can easily grab the param, but after much searching and looking at the API and Cookbook, no such method exists. The next alternative was doing a long ugly shorthand if statement that looked something like this (assuming our param and URL is /search/filter:username/):

$filter = (isset($this->params['named']['filter'])) ? $this->params['named']['filter'] : '';

Now seeing that CakePHP is in 1.2 Stable, you would assume they would have a simple method to fix this. But no worries, I have created a method that is very easy to use and add to your application. To add it to your application, you would place it in your AppController.

/**
 * Used to get the value of a named param
 * @param mixed $var
 * @param mixed $default
 * @return mixed
 */
public function getNamedParam($var, $default = '') {
	return (isset($this->params['named'][$var])) ? $this->params['named'][$var] : $default;
}

The first argument $var would be the name of the param (e.g., "filter" in our example above) and the second argument $default is the value you can return if the param does not exist. To use the method, simply call it from anywhere in your controller like so:

$filter = $this->getNamedParam('filter');

I have also created a second method for grabbing the value of query string param (Example URL: /search/?filter=username). I hope this has been helpful to someone, it has certainly been helpful and easily readable to me.

/**
 * Used to get the value of a get query
 * @param mixed $var
 * @param mixed $default
 * @return mixed
 */
public function getQueryParam($var, $default = '') {
	return (isset($this->params['url'][$var])) ? $this->params['url'][$var] : $default;
}