With jQuery it is very easy to change the value of a form element, here’s a quick example that changes the text for a form element with id=’input1′
$('#input1').val('this is the new value');
On the page you will see your input change as requested. If you look at the value with jQuery there are no surprises.
console.log($(#input1).val());
//will output 'this is a new value'
However you may be surprised that it hasn’t been changed if you try to look at it with jQuery in a different way. Assuming that input1 is contained in a div with id ‘div1’.
console.log($('#div1').html());
You will see this
The DOM doesn’t look like it has been updated. This certainly wasn’t the behaviour I expected from this so it took a lot of digging to find out what was going on.
It turns out that the HTML value attribute isn’t necessarily the value that is displayed on the page, instead it is known as the default value – used when the page is first loaded or if the form is reset.
The solution. If you want to update the value that appears on the page and the value that’s retrievable by the .html() method is this:
$('#input1').val('this is the new value');
//before jQuery 1.6
$('#input1').attr('defaultValue', 'this is the new value');
//after jQuery 1.5.2
$('#input1').attr('value', 'this is the new value');
Set the ‘defaultValue’ or ‘value’ attribute depending on your version of jQuery as well as using the .val() method. You’ll find that getting the html with jQuery will now show the correct value.
Most sites must have a separate admin area to the main site and many will be using a different layout for the admin area. CakePHP will render errors using the default template so your admin area can look inconsistent and messy if an error has to be raised.
The solution to this follows. I can’t take claim for coming up with this but I’ve adapted from a variety of sources and updated for CakePHP 2.0.
Firstly you need to add some code to /app/Controller/AppController.php, if you don’t have one of these then copy it over from /lib/Cake/Controller/AppController.php. The beforeRender method will check if there is an error then checks if you are using Admin routing. If that is the case the layout is changed:
class AppController extends Controller {
public $helpers = array('Ulc', 'Html', 'Form', 'Js', 'Session', 'Text');
public $components = array('Auth', 'Session');
public function beforeFilter() {
$this->Auth->allow('*');
}
public function beforeRender() {
$this->_configureErrorLayout();
}
public function _configureErrorLayout() {
if ($this->name == 'CakeError') {
if ($this->_isAdminMode()) {
$this->layout = 'admin';
} else {
$this->layout = 'default';
}
}
}
public function _isAdminMode() {
$adminRoute = Configure::read('Routing.prefixes');
if (isset($this->params['prefix']) && in_array($this->params['prefix'], $adminRoute)) {
return true;
}
return false;
}
}
Now in production mode (debug = 0 in core.php) CakePHP will tend to use 2 different error views so it may be worth creating your own customised views.
Copy error400.ctp and error500.ctp from lib/Cake/Views/Errors into app/Views/Errors.
If there is anything you want to display differently in the admin version you can use:
if ($this->layout == 'admin') {}
to detect that you are in the admin area and then hide unnecessary markup that may need to be shown to regular site visitors.
Just a quick note on this one as it took a while to discover how to it.
Say you have a relationship between two tables and you want to be able to use the built in pagination helper to generate sort links on the content in the related table.
One way of doing this is to add virtual fields to bring the related column(s) into the Model.
I wanted to validate the fields on a search form (e.g. date, price) like the add/edit forms work when you bake or use the scaffolding.
Why is this a problem.
CakePHP has great built in validation but it is tied into saving data.
If you have a search form on your website, nothing needs to be saved and there probably isn’t even a suitable Model anyway.
Another issue is that I wanted it to work where my index view contains the search form and this posts to the results action. The results action processes the search and the view displays the search results. If there’s a validation error you need to go back to the index view.
I did consider just using jQuery to validate the form contents before it went up to the server – but I’m sure you all know that it is poor practice to just use client side validation.
To validate like the add or edit scaffolding the view needs to submit to its own action just like they do.
However that would mean the search results would also be displayed within the same view.
I could run the query and then render a different view (but then the URL won’t change).
I could store the search params in a session and redirect to the results view, then retrieve the search params, run the search and display it.
I began to worry how well pagination would work with either of these options.
I decided to try this with the way I wanted it to work – the search view submitting to a different action which displays the results, then if invalid I just needed a way of displaying the search form again and highlighting the errors.
The trick is to store validation messages and posted form data in Sessions and redirect back to the search form.
First I considered reading the Session in the view and injecting into CakePHP variables.
The code below shows how to add validation information within a view:
validationErrors['Category']['name'][0] = 'Validation message text here';
$this->validationErrors['Category']['name'][0] = 'Price should be < 100';
$this->request->data['Category']['name'] = 'Form field data in here';
$this->request->data['Category']['price'] = 123;
?>
In the controller action which receives form input you do the following:
$post = $this->request->data['Category'];
if (empty($post['search_text'])) {
$this->Category->validationErrors['search_text'][0] = 'You must provide search text.';
}
if (empty($post['price'])) {
$this->Category->validationErrors['resort_id'][0] = 'You must provide a price.';
}
if (count($this->Category->validationErrors) > 0) {
$this->Session->write('Category', $this->data);
$this->Session->write('CategoryErrors', $this->Category->validationErrors);
$this->redirect(array('action' => 'index'));
}
This checks for errors, stores the errors and data in sessions and redirects back to the search form.
And in the controller action which displays the form:
//see if there were any validation problems
if ($this->Session->check('Category')) {
// get data the user posted
$chalet = $this->Session->read('Category');
// get the errors
$errors = $this->Session->read('CategoryErrors');
// set their data for view
$this->request->data['Category'] = $chalet['Category'];
// set validation errors for view
$this-> Category->validationErrors = $errors;
// delete the session data
$this->Session->delete('Category');
$this->Session->delete('CategoryErrors');
}
This breaks CakePHP’s principles of Fat Models and Skinny Controllers (maybe I could move some of this code into Model functions) but I’d be very interested to see what other ways there are of doing this.
I wanted some auto populating Select boxes in a site I was creating so that when I changed a Category the Subcategories would automatically update. CakePHP can do this pretty easily but it is let down by the documentation as there are no examples.
Initially I came up with a version that wrote JSON into a JavaScript variable in the page and then used jQuery to achieve the updating of select elements but I wanted to do this the Cake way which uses AJAX and as little code as possible.
Here is a simplified solution to demonstrate how it can be done.
This assumes a new CakePHP site already configured with a database connection. I used CakePHP 2.0.3 but this may also work with 1.3 (?)
Bake the 3 Models for these and allow CakePHP to define model associations for you.
2. Controllers
Bake a Controller for the Post Model with the default CRUD actions.
While you are at it you will also need to bake the CRUD Views for the Post Controller.
If you browse to the posts index page now you can view the data.
In this example I will add the Category and Subcategory select lists to the Add view, so now we need to change this so that the selection in the second list changes according to the selection in the first list.
This will be done via the Js Helper so make it available at the top of the Posts controller (after the Class declaration) with:
public $helpers = array('Js');
You need a Subcategories Controller with a single action to provide the data via AJAX:
The view is a very simple AJAX view that renders the option tags that go within the select tag.
$value): ?>
4. Putting it all together
In the Post Add view, file path: View/Posts/add.ctp we can finally add the Js methods that make the dynamic updating happen. This is the cryptic bit that I struggled with for a few hours as although the CakePHP documentation outlines all the options there are not any complete examples.
Firstly add a categories select element to the form (and change the order of the elements):
This is saying – watch the HTML element with Id PostCategoryId for a change. When it changes update the HTML element with Id PostSubcategoryId with the response from subcategories/GetByCategory, the data option is used to send the current value from the initial select element.
Before you leap to test this you need to make changes to the default layout to include jQuery and provide a place for your scripts to be written out.