Unable to retain values of dependent select box in Drupal 7 - ajax

I have two dependent dropdown one for country and one for state. I am using the concept multiple times in a multistep webform.
When the form loads, the list of country is okay. On selecting a particular country the state list is also okay.
Now comes the issue:
If there is some mandatory field in the same part of webform, and the user does not fill that up the form refreshes and loses the entire list of states. Also, when moving to the next step (Multi step webform) and coming back to the same page the value is lost.
However in the entire process the value of country select list is retained.
The select options generated using ajax are not retained.
Thanks in advance. Below is the code used.
$form['submitted']['employment_history']['employer_1']['address_of_employer']['country']['#ajax'] = array(
'callback' => 'my_custom_ajax_callback_for_employer_one',
'wrapper' => 'edit-submitted-employment-history-employer-1-address-of-employer-state',
'method' => 'replace',
);
/*
* Implements Ajax callback for populating list of provinces (Employer One).
*/
function my_custom_ajax_callback_for_employer_one($from, $form_state) {
$selected_country = $form_state['values']['submitted']['employment_history']['employer_1']['address_of_employer']['country'];
$states = location_get_provinces($selected_country);
$form['submitted']['employment_history']['employer_1']['address_of_employer']['state']= array(
'#type' => 'select',
'#options' => $states,
'#attributes' => array('id' => 'edit-submitted-employment-history-employer-1-address-of-employer-state'),
);
$form['rebuild'] = TRUE;
return $form['submitted']['employment_history']['employer_1']['address_of_employer']['state'];
}

My initial thought is that you should be passing the form and form state by reference ($from, $form_state) should be (&$form, &$form_state) on your ajax callback (as well you have a typo).

Related

Magento Admin formfield multiselect selected

I´m currently developing a custom module for magento thats going to list employees.
I have figured out almost everything. The only thing I got left is how the selected values is going to be highlighted.
The problem I´m having is for the backend.
I got 2 tabs per employee, one for employee data and one tab for magento categories.
1 employee can have 1 or more categories.
The database table that the categories are stored in are a non-eav table.
So my question is
What in a multiselect determines which values are selected? As it is now, only one value is selected.
I think you can do this by simply passing in an array of the id's to be selected into the 'value' attribute of the field being added for the multiselect in the _prepareForm() method. Something like the following.
$fieldset->addField('category_id', 'multiselect', array(
'name' => 'categories[]',
'label' => Mage::helper('cms')->__('Store View'),
'title' => Mage::helper('cms')->__('Store View'),
'required' => true,
'values' => Mage::getSingleton('mymodule/mymodel')->getMymodelValuesForForm(),
'value' => array(1,7,10),
));
The id of the form element (e.g. category_id) must not be an attribute in your model, otherwise when the form values get set with $form->setValues() later on, the attribute value will be overwritten.
I normally store multiple selections as a text column separated by commas much like most magento modules handles stores which requires a slightly different approach as shown below.
In the form block for the tab with the multiselect, you firstly define the element to be displayed like so in the _prepareForm() method. You then get the values from the model and set put them into the form data.
protected function _prepareForm()
{
...
$fieldset->addField('store_id', 'multiselect', array(
'name' => 'stores[]',
'label' => Mage::helper('cms')->__('Store View'),
'title' => Mage::helper('cms')->__('Store View'),
'required' => true,
'values' => Mage::getSingleton('adminhtml/system_store')->getStoreValuesForForm(false, true),
));
...
if ( Mage::getSingleton('adminhtml/session')->getMymodelData() )
{
$data = Mage::getSingleton('adminhtml/session')->getMymodelData();
} elseif ( Mage::registry('mymodel_data') ) {
$data = Mage::registry('mymodel_data')->getData();
}
$data['store_id'] = isset($data['stores']) ? explode(',', $data['stores']) : array();
$form->setValues($data);
}
I normally store the selected stores (categories as in your case) in the main model as a text column and comma separated values of ids, hence the explode.
In the controller for for the edit action, I put the model being edited into the mage registry so we can load it and it's values in the step above.
Mage::register('mymodel_data', $model);
Thanks for answering.
This is how my field looks like:
$fieldset->addField('npn_CatID', 'multiselect', array(
'label' => Mage::helper('employeelist')->__('Kategori'),
'class' => 'required-entry',
'required' => true,
'name' => 'npn_CatID',
'values' => $data,
'value' => array(3,5)
));
npn_CatID is the value in my db where the category id is saved.
I have tried to change the name and field ID but cant get it working.
When its the field id is like above ONE value is selected and its the last one inserted for the chosen employee
My data array looks likes
array(array('value' => '1', 'label' => 'USB'), array('value' => '2', 'label' => 'Memories'))

CakePHP data change during validation and beforeSave is not being save with the changes

I'm saving data sent from a form.
In the Controller I am doing :
$this->User->create();
$this->User->save($this->request->data)
The $this->request->data looks like this:
'User' => array(
'password' => '*****',
'username' => 'ddddd',
'role' => '256/aa01bdf80d42beb48dd3225acddf447fdd7a39f3',
'parent_id' => '0/b6ba9bd57f6c70cf738891d4c6fac22abed4161d'
)
There are validation rules that works on 'role' and 'parent_id' to insure the role/parent ids are among those the user can access.
The validation changes the field values if the data is valid.
I also have a Tree behavior that is setting some tree fields in a beforeSave() filter in the behavior.
The validation rule is writing the change to $this->data->[$model][$field] as shown below.
public function checkListHash($check, $field) {
$explodedCheck = explode('/', $check[$field]);
if ($this->secureId($explodedCheck[0], $explodedCheck[1])) {
$this->data['User'][$field] = $explodedCheck[0];
return true;
}
return false;
}
The beforeFilter() in the behavior is changing the data array with statements like this:
$Model->data[$Model->alias][$ancestors] = $ancestorList;
When validation and the beforeFilter() processing is complete, I have a beautiful and correct array of data at $this->User->data that looks like this:
'User' => array(
'password' => '*****',
'active' => '0',
'role' => '256',
'parent_id' => '0',
'node' => '0',
'username' => 'ddddd',
'modified' => '2013-09-15 09:55:02',
'created' => '2013-09-15 09:55:02',
'ancestor_list' => ',0,'
)
However, $this->request->data is unchanged. And that is what is being save.
Clearly I'm not understanding the relationship of these various ways to get to the data. I've tried a variety of ways to address the data in the three contexts:
Controller
Model
Behavior
And I've tried $this->User->create($this->request->data); before the Controller save() statement.
In the controller, what I'm seeing as available data arrays:
PRIOR TO THE SAVE
$this->request->data = $this->data = proper data from the form
$this->User->data = some default, unpopulated array
PRIOR TO THE SAVE when I use $this->User->create($this->request->data)
all three arrays contain raw form data
AFTER THE SAVE in either case
$this->request->data = $this->data = exactly as before
$this->User->data = the properly massaged data
Can anyone sort me out?
Don Drake
Just to explain the data arrays to you, when you submit the form, the data from it is stored in $this->request->data on the controller. You are then modifying $this->User->data from inside the model, which is a different array on the model itself. It would not affect $this->request->data because it's a completely different array which belongs to the controller, and the model has no knowledge of it.
You are then saving the User model using the request data, which remains unchanged from when the form was submitted. This is logical and normal behaviour because you're not actually using the $this->User->data array that you've modified.
Your save might always be failing because the data the model is trying to save isn't the updated data, it's just the basic data from $this->request->data.
Try this:
$this->User->set($this->request->data);
$this->User->save();
Also, if you are using a beforeSave in your model, make sure the method returns true, or it will never actually go on to save.

Yii CGridview - search/sort works, but values aren't being displayed on respective cells

I am semi-frustrated with this Yii CGridView problem and any help or guidance would be highly appreciated.
I have two related tables shops (shop_id primary) and contacts (shop_id foreign) such that a single shop may have multiple contacts. I'm using CGridview for pulling records and sorting and my relation function in shops model is something like:
'shopscontact' => array(self::HAS_MANY, 'Shopsmodel', 'shop_id');
On the shop grid, I need to display the shop row with any one of the available contacts. My attempt to filter, search the Grid has worked pretty fine, but I'm stuck in one very strange problem. The respective grid column does not display the value that is intended.
On CGridview file, I'm doing something like
array(
'name' => 'shopscontact.contact_firstname',
'header' => 'First Name',
'value' => '$data->shopscontact->contact_firstname'
),
to display the contact's first name. However, even under circumstances that searching/sorting are both working (I found out by checking the db associations), the grid column comes out empty! :( And when I do a var_dump
array(
'name' => 'shopscontact.contact_firstname',
'header' => 'First Name',
'value' => 'var_dump($data->shopscontact)'
),
The dump shows record values in _private attributes as follows:
private '_attributes' (CActiveRecord) =>
array
'contact_firstname' => string 'rec1' (length=4)
'contact_lastname' => string 'rec1 lsname' (length=11)
'contact_id' => string '1' (length=1)
< Edit: >
My criteria code in the model is as follows:
$criteria->with = array(
'owner',
'states',
'shopscontacts' => array(
'alias' => 'shopscontacts',
'select' => 'shopscontacts.contact_firstname,shopscontacts.contact_lastname',
'together' => true
)
);
< / Edit >
How do I access the values in their respective columns? Please help! :(
Hmm, I have not used the with() and together() methods much. What's interesting is how in the 'value' part of the column, $data->shopscontacts loads up the relation fresh, based on the relations() definition (and is not based on the criteria you declared).
A cleaner way to handle the array output might be like this:
'value' => 'array_shift($data->shopscontacts)->contact_lastname'
Perhaps a better way to do this, though, would be to set up a new (additional) relation, like this in your shops model:
public function relations()
{
return array(
'shopscontacts' => array(self::HAS_MANY, 'Shopsmodel', 'shop_id'), // original
'firstShopscontact' => array(self::HAS_ONE, 'Shopsmodel', 'shop_id'), // the new relation
);
}
Then, in your CGridView you can just set up a column like so:
'columns'=>array(
'firstShopscontact.contact_lastname',
),
Cheers
Since 'shopscontact' is the name of the has-many relation, $data->shopscontact should be returning an array with all the shops related... did you modify the relation in order to return only one record (if I didn't get you wrong, you only need to display one, right?)? If you did it, may I see your filtering code?
P.S. A hunch to get a fast but temporal solution: have you tried 'value' => '$data->shopscontact['contact_firstname']'?

Drupal AJAX callback only getting called once

I'm trying to make a form in Drupal, so more than one element can be added to a form. For example, a page might contain data for an event, then the event might have multiple dates. So I have a form that looks like:
/**
* Implements hook_form_alter().
*/
function addextra_form_alter(&$form, &$form_state) {
if ($form['#form_id'] == 'test_content_node_form' ) {
$form['elements_table'] = array(
'#theme' => 'table',
'#title' => 'Elements already added',
'#header' => array('Item', 'Remove'),
'#empty' => 'No elements',
'#prefix' => '<div id="elements-table">',
'#suffix' => '</div>',
);
$form['add_elements'] = array(
'#title' => 'Add another element',
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['add_elements']['add_content'] = array(
'#type' => 'textfield',
'#description' => t('Add an element to the table'),
'#title' => t('Add another item'),
'#size' => '12',
'#maxlength' => '60',
'#prefix' => '<div id="addextra_content">',
'#suffix' => '</div>',
);
$form['add_elements']['add_another_btn'] = array(
'#type' => 'button',
'#name' => 'add_another',
'#button_type' => 'submit',
'#executes_submit_callback' => FALSE,
'#value' => 'Add another',
'#ajax' => array(
'callback' => 'addextra_element_to_table',
),
);
}
}
When 'add_another_btn' gets clicked, it will run the ajax callback 'addextra_element_to_table.
That callback is:
function addextra_element_to_table(&$form, &$form_state) {
$form['elements_table']['#rows'][] = array($form_state['values']['add_content'], l('Remove Item', '#'));
drupal_add_js(drupal_get_path('module', 'addextra') . '/addextra.js');
return array(
'#type' => 'ajax',
'#commands' => array(
ajax_command_replace('#elements-table', render($form['elements_table'])),
),
);
}
The js file called replaces the val of the input field to ''
(function ($) {
$('#edit-add-content').val('');
})(jQuery);
But this callback only gets called one time. I believe this is because the behaviour has to be attached again once it's been called. Sorry for my ignorance - I'm not sure how to achieve this. Can anyone help me out? It would be much appreciated. Thanks in advance.
The problem is that render(), which basically just calls drupal_render() , does not process the #ajax element, it's simply ignored. You might want to try and pass the element through ajax_pre_render_element() before the call to render().
That said, I personally don't have good experience with trying to reuse Drupal functions outside their normal calling sequence, especially not with forms. I prefer to stick to the very top level functions, such as drupal_get_form. I have followed those functions many times in my debugger, and they do a bunch of things in a precise order that is hard to get a hold of when you want to reuse pieces of that.
To alter a form with AJAX callbacks, I would always prefer one of two strategies:
In the callback, adjust the content of the $form argument and do return $form. This requires that you set #ajax['wrapper'] to the id (the value of the id attribute in the markup, as used for CSS) of the form on the originating element (the button in your case). Then Drupal gets to do its shpiel with the whole form, and the browser replaces the whole thing. Drupal takes care of preserving values already entered etc.
Alternatively, you can have the callback return a set of commands that do very specific modifications on the DOM. In your case that would be commands which create and append new rows. Keep in mind that with ajax_command_invoke(), you have the entire jQuery arsenal at your disposal.
From those two strategies, I usually prefer the second one, because it seems more elegant for little tweaks. However, if you want to build on Drupal's rendering, or if you have more massive changes to the form, you should use the first one.
As a side note, did you know that there is drupal.stackexchange.com? Drupal can be quite peculiar, and on that site, you'll find more experts on the specifics.

Problem when I am populating data into selectbox through ajax in Drupal

I have installed drupal 6, added some cck fields in one content type. Added two select box fields.
I am taking the selected value of parent select box and as per that selection passing relates options to next select box field using Ajax. (e.g Country -> State. When user selects country I want to pass state values into next select box.)
But when I am submitting the form it gives the following error:
"An illegal choice has been detected. Please contact the site administrator."
I don't know why it is not taking the ajaxified select box values while saving the node.
Does somebody has the solution on it. Is there any solution to handle this dynamic select options in Drupal.
Thanks in advance.
The same thing I'm working on drupal 7 and its work for me. Below is the code. Hope this help you. What I have done is on selection of car model car variant will change and the data save in the table.
function add_offer_form($form, $formstate) {
$form['add_offer_new_car_model'] = array(
'#type' => 'select',
'#required' => TRUE,
'#options' => $car_model,
'#ajax' => array(
'effect' => 'fade',
'progress' => array('type' => 'none'),
'callback' => 'variant_callback',
'wrapper' => 'replace_variant',
),
);
// Combo box to select new car variant
$form['add_offer_new_car_variant'] = array(
'#type' => 'select',
'#options' => array(),
// The prefix/suffix provide the div that we're replacing, named by #ajax['wrapper'] above.
'#prefix' => '<div id="replace_variant">',
'#suffix' => '</div>',
);
// An AJAX request calls the form builder function for every change.
// We can change how we build the form based on $form_state.
if (!empty($formstate['values']['add_offer_new_car_model'])) {
$model_id = $formstate['values']['add_offer_new_car_model'];
$rows = array();
$result = db_query("SELECT id, variant_name from {va_car_variant} where car_model_id in ($model_id,1) order by variant_name");
while ($data = $result->fetchObject()) {
$id = $data->id;
$rows[$id] = $data->variant_name;
}
$form['add_offer_new_car_variant']['#options'] = $rows;
}
}
////////////////////////////////////////////////////////
///////// FUNCTION FOR AJAX CALL BACK
function variant_callback($form, &$form_state) {
return $form['add_offer_new_car_variant'];
}

Resources