Categories Selection
The user can select a category in 2 ways:
- Start typing the category, or
- Press the down arrow, which will provide a list of categories
Either way will produce the drop-down of categories as shown below:
There is code for the categories selection in:
The PHP Edit View:
<div class="input select" id="select_categories">
Categories<br>
<ul class="item-list categories">
<?php
// Loop through each category
foreach ($categories AS $categoryId=>$categoryName)
{
// If this category is in the array of categories already associated with this post (in the categories_posts table), then set class=active to show the category. Otherwise the category stays hidden and isn't selected
if (in_array($categoryId,$categoriesSelected))
{
$selected = "class='active'";
}
else
{
$selected = "";
}
// Create each category in a list, but only display if class=active is set
echo '<li item="' . $categoryId. '" title="' . $categoryName . '"' . $selected . '>' . $categoryName;;
// Allow removal of an active category
echo '<a class="close" title="Remove ' . $categoryName . '">x</a>';
echo '</li>';
}
// This is an autocomplete input form, that displays available categories. You can also cursor down to display a list
?>
<li class="input"><input class="item-input" value="" tabindex="2" placeholder="Start typing or press down"></li>
</ul>
</div>
The Javascript for the Edit View:
$(function()
{
// Enable a multi auto complete drop down box when selecting categories to apply blog post to
$(".categories").multiautocomplete({formField: "#categories"});
});
And the Multi Auto Complete Javascript code:
$.widget("ui.verboseautocomplete", $.extend({}, $.ui.autocomplete.prototype, {
_response: function(contents)
{
$.ui.autocomplete.prototype._response.apply(this, arguments);
$(this.element).trigger("autocompletesearchcomplete", [contents]);
//alert("response")
}
}));
(function($) {
//multiautocomplete is called upon loading the page from \admin\app\webroot\js\posts\edit.js
$.fn.multiautocomplete = function(options) {
var options = options || {};
options.formField = options.formField || "";
// itemList is binded to <ul class="item-list categories"> and we'll loop through each <li>
var itemList = this;
// Will hold all the categories
var names = [];
// Used to create the indexed array for the HABTM relation between a Post and its Categories
var postCategoryCounter = 0;
// This will hold all the selected categories to pass through the form
var outArray = [];
function setOutput(){
var categoryIdField;
var postIdField;
var postIdValue = document.getElementById("PostId").value;
var x;
// Find already selected (active) categories, and add to the outArray
itemList.find("li.active").each(function(){
// Check if it already exists
if(outArray.indexOf($(this).attr("item")) == -1)
{
outArray.push($(this).attr("item"));
categoryIdField = '<input type="hidden" class="categories" name="data[Category][' + postCategoryCounter + '][id]" value="' + $(this).attr("item") + '">';
$("#select_categories").append(categoryIdField);
postCategoryCounter++;
}
});
}
// Add all the categories to the names array
itemList.find("li").each(function(){
names.push({label:$(this).attr("title"), value:$(this).attr("item")});
});
itemList.find("a.close").click(function(){
var parentItem = $(this).parent();
var parentItemValue = parentItem.attr("item");
var v;
var categoryValue;
var index = outArray.indexOf(parentItemValue);
// Remove the selected category from the active list displayed
parentItem.slideUp('fast',function(){parentItem.removeClass('active');setOutput();});
// Remove category from the hidden categories class fields
$.each($(".categories"), function(key, value)
{
// If the category looped over equals the category removed, then remove this categories hidden field
categoryValue = $(".categories")[key].value;
if (parentItemValue == categoryValue)
{
// Found hidden field with this categories value and remove
$(this).remove();
// Also remove from the outArray, which allows us to add it back again.
if (index > -1) {
outArray.splice(index, 1);
}
}
});
return false;
});
// Just finding the input field within the item list
var _InputField = itemList.find("input");
// Listen to any events on the input field
_InputField.verboseautocomplete({
minLength: 0,
source: names,
select: function(event, ui){
var selectedItem = itemList.find("li[item="+ui.item.value+"]");
selectedItem.slideDown('fast',function(){selectedItem.addClass('active');setOutput();});
_InputField.val('');
return false;
},
focus: function( event, ui ) {
// Only works if the mouse is focusing on it, not the arrow keys
$(".item-input").val(ui.item.label);
}
}).bind("autocompletesearchcomplete", function(event, contents) {
if(contents.length == 0){
$(this).autocomplete("widget").html('<li><p class="ui-corner-all">No results found.</p></li>');
$(this).autocomplete("widget").show();
}
});
setOutput();
}
})(jQuery);
Where it is all saved in the Posts Controller Code
/* Form is being submitted. Save data */
if ($this->request->is(array('post', 'put')))
{
$this->Post->id = $id;
$saveStatus = $this->Post->save($this->request->data);
if ($saveStatus )
{
// Saving (associating) the categories selected for this post
$this->loadModel('CategoriesPosts');
$this->CategoriesPosts->saveCategories($this->request->data["Category"], $this->Post->id);
$this->Session->setFlash(__('Your post has been updated.'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('Unable to update your post.'));
}


Comments