Attached is an example screenshot of the form I'm using PHP and AJAX in Drupal 7.
By default, the form will display 10 fields. The form uses AJAX to enable users to add a potentially infinite number of input fields to the initial 10 in groups of 5. What I need it to do is upon retrieval, repopulate the fields as it was saved, with the same functionality available, should the user wish to edit.
I have on a previous attempt used $form_state in entries_form_add_five to add to the number of input boxes and returned using the ajax call but I couldn't get this to work when loading the data to edit. How can I rebuild the form with a new $node-->entries_form['term'] array which has been increased by 5?
<?php
function entries_form_form_entries_node_form_alter(&$form, &$form_state, $form_id) {
//trimmed
$node = $form['#node'];
$form["section"]["term"]["#tree"] = TRUE;
$items = $node->entries_form['term'];
foreach($items as $key => $item) {
$form["section"]["term"][$key] = array(
'#type' => 'textfield',
'#size' => 10,
'#attributes' => array(
'class' => array('left'),
),
'#value' => $item,
);
}
//trimmed
}
function entries_form_commands_add_callback($form, $form_state) {
return $form['section']['term'];
}
function entries_form_add_five($node, $form, &$form_state){
$node->entries_form['term'] = array_push($node->entries_form['term'],'', '', '', '', '');
$form_state['rebuild'] = TRUE;
}
function entries_form_node_prepare($node) {
if (empty($node->entries_form)) {
// Set default 10 empty values, since this only runs when adding a new node.
$node->entries_form['term'] = array_fill(0, 10, '');
}
}
function entries_form_node_load($nodes, $types) {
if($types[0] == 'entries'){
$result = db_query('SELECT * FROM {entries_form_node_form_alter} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)))->fetchAllAssoc('nid');
foreach ($nodes as &$node) {
$node->entries_form['term'] = json_decode($result[$node->nid]->term);
}
}
}
Any help very much appreciated.
Thanks!
I agree that there are no good examples of loading previously saved data into an unlimited values field. (I think this should have been in the examples module.
I wrote the code below to just manage a list of people. This list is stored in Drupal variable table, not as nodes, however the methodology should be similar, so hope should guide you in the right direction.
function people_list_form($form, &$form_state) {
$form['#tree'] = TRUE;
// load the list of names - here you could use node load instead
$names = variable_get('people_list', array());
if (empty($form_state['num_names'])) {
// store the number of names we have in $form_state
$form_state['num_names'] = count($names)>0 ? count($names) : 1;
}
$form['names_fieldset'] = array(
'#title' => 'List of People',
'#type' => 'fieldset',
'#prefix' => '<div id="names-fieldset-wrapper">',
'#suffix' => '</div>',
);
// loop for each name to add form elements
for ($i = 1; $i <= $form_state['num_names']; $i++) {
$form['names_fieldset']['name'][$i]['name'] = array(
'#type' => 'textfield',
'#title' => 'Name #'.$i,
'#default_value' => isset($names[$i-1]) ? $names[$i-1] : '',
);
}
$form['names_fieldset']['add_name'] = array(
'#type' => 'submit',
'#value' => t('Add another name'),
'#submit' => array('people_list_form_add_name'),
'#ajax' => array(
'callback' => 'people_list_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
if ($form_state['num_names'] > 1) {
$form['names_fieldset']['remove_name'] = array(
'#type' => 'submit',
'#value' => t('Remove last name'),
'#submit' => array('people_list_form_remove_name'),
'#limit_validation_errors' => array(),
'#ajax' => array(
'callback' => 'people_list_add_more_callback',
'wrapper' => 'names-fieldset-wrapper',
),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
function people_list_add_more_callback($form, $form_state) {
return $form['names_fieldset'];
}
function people_list_form_add_name($form, &$form_state) {
$form_state['num_names']++;
$form_state['rebuild'] = TRUE;
}
function people_list_form_remove_name($form, &$form_state) {
if ($form_state['num_names'] > 1) {
$form_state['num_names']--;
}
$form_state['rebuild'] = TRUE;
}
function people_list_form_submit($form, &$form_state) {
$names = array();
foreach($form_state['values']['names_fieldset']['name'] as $k => $v) {
$names[] = $v['name'];
}
variable_set('people_list', $names);
drupal_set_message('Names updated');
}
Related
I am attempting to implement a draggable table in a Drupal 7 module. Whenever I invoke either render() or drupal_render on my element of type #weight, an empty string is returned. The table does render and I am able to drag the rows, but cannot see the weights. I cannot figure out what I am doing wrong: I've explored some core Drupal components that follow the same pattern (of invoking drupal_render on a #weight type element) and nothing is jumping out at me.
My form begins here:
$table_header = array_map(t, array('Order', 'Item', 'Manage'));
$form['table'] = array(
'#theme' => 'mymodule_kw_table',
'#header' => $table_header,
'#rows' => $my_rows // array(string)
);
hook_theme() is implemented as such:
function mymodule_theme()
{
return array(
'mymodule_kw_table' => array(
'render element' => 'form',
'function' => 'mymodule_table'
)
);
}
Finally, mymodule_table() looks like this:
function mymodule_table($variables)
{
$table_data = $variables['form'];
$table_id = uniqid('tb');
$rows = $table_data['#rows'];
for ($i=0; $i < count($rows); ++$i) {
$weight = array(
'#type' => 'weight',
'#title' => t('Order'),
'#default_value' => $i,
'#delta' => 1,
'#attributes' => array('class' => array($table_id . '-weight'))
);
$rows[$i] = array(
'data' => array_map(render, array($weight, $rows[$i], 'abcd placeholder')),
'class' => array('draggable')
);
}
$table = theme('table', array(
'header' => array_map(t, array('Order', 'Item', 'Manage')),
'rows' => $rows,
'attributes' => array('id' => $table_id)
));
$table .= drupal_render_children($table_data);
drupal_add_tabledrag($table_id, 'order', 'sibling', $table_id . '-weight');
return $table;
}
I'm new on drupal, i've create a module that shows a form with select, button submit and i've got a tableselect that lists some records (from database). the tableselect works and it's listing all records, but i want to "filter" records on this tableselect by selecting what i want to see with select list (and submitting). when i choose something in the select list and i submit, the tableselect dont change but if i execute a dsm($form['tableselect']) it said that tableselect contains what the db_query returns.
here is my form :
function myBook_form ($form, &$form_state){
$form = array();
$form['options_state'] = array(
'#type' => 'value',
'#value' => array (
'all'=>t('All'),
'Valid'=>t('Valid'),
'published'=>t('published'),
'not published'=>t('not published')
)
);
$form['state_book'] = array(
'#type' => 'select',
'#title' => t('state :'),
'#options' => $form['options_state']['#value'],
);
// filter submit button
$form['filter'] = array(
'#type' => 'submit',
'#value' => t('filter')
);
}
$header = array(
'book_title' => t('title'),
'book_state' => t('state'),
);
$sql = db_select('field_data_field_title','ta');
$sql->join('field_data_field_state','st','st.entity_id = ta.entity_id');
$sql
->fields('ta', array('field_title_value','entity_id'))
->fields('st',array('field_state_value'));
$result = $sql->execute();
$rows = array();
foreach ($result as $res){
$rows [] = array(
'book_title' => l($res->field_title_value, 'node/'.$res->entity_id),
'book_state' => $res->field_state_value
);
}
$form['table1'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $rows,
'#empty' => t('empty !'),
);
return $form;
here is my submit function :
function book_form_submit($form, &$form_state){
$cond = $form_state['values']['state_book'];
$header = array(
'book_title' => t('title'),
'book_state' => t('state'),
);
$sql = db_select('field_data_field_title','ta');
$sql->join('field_data_field_state','st','st.entity_id = ta.entity_id');
$sql
->fields('ta', array('field_title_value'))
->fields('st',array('field_state_value'));
$sql->condition('st.field_state_value', $cond, '=');
$quer = $sql->execute();
$rows = array();
foreach ($quer as $q){
array_push($rows, array(
'book_title' => $q->field_title_value,
'book_state' => $q->field_state_value,
));
}
$form['table2'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $rows,
'#empty' => t('empty!')
);
}
Thanks
I have written the code with an idea of the core module named user, the role function.
my "user_admin_types" is like "user_admin_roles". If you take a brief look at "user.admin.inc" file you will understand that i have replaced my functions and variables on the basis of these:
"user_roles" = "user_types"
"role" = "type"
"rid" = "ut_id"
"name" = "ut_label"
Everything is that I've done is same as the Core has done but i have this warning in my page: Warning: Illegal offset type in isset or empty in element_info.
I have checked every line but i couldn't find the reason and the way i can disappear it.
`function user_admin_types($form, $form_state) {
$types = user_types();
$form['types'] = array(
'#tree' => TRUE,
);
$order = 0;
foreach ($types as $ut_id => $ut_label) {
$form['types'][$ut_id]['#type'] = (object) array(
'ut_id' => $ut_id,
'ut_label' => $ut_label,
'ut_weight' => $order,
);
$form['types'][$ut_id]['#weight'] = $order;
$form['types'][$ut_id]['weight'] = array(
'#type' => 'textfield',
'#title' => t('Weight for #title', array('#title' => $ut_label)),
'#title_display' => 'invisible',
'#size' => 4,
'#default_value' => $order,
'#attributes' => array('class' => array('type-weight')),
);
$order++;
}
$form['ut_label'] = array(
'#type' => 'textfield',
'#title' => t('User Type'),
'#title_display' => 'invisible',
'#size' => 32,
'#maxlength' => 64,
);
$form['add'] = array(
'#type' => 'submit',
'#value' => t('Add type'),
'#validate' => array('user_admin_type_validate'),
'#submit' => array('user_admin_type_submit'),
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save order'),
'#submit' => array('user_admin_types_order_submit'),
);
return $form;
}
/**
* Form submit function. Update the type weights.
*/
function user_admin_types_order_submit($form, &$form_state) {
foreach ($form_state['values']['types'] as $ut_id => $type_values) {
$type = $form['types'][$ut_id]['#type'];
$type->ut_weight = $type_values['weight'];
user_type_save($type);
}
drupal_set_message(t('The type order have been updated.'));
}
function user_types() {
$query = db_select('users_types', 'ut');
$query->addTag('translatable');
$query->fields('ut', array('ut_id', 'ut_label'));
$query->orderBy('ut_weight');
$query->orderBy('ut_label');
$result = $query->execute();
$types = array();
foreach ($result as $key => $type) {
switch ($type->ut_id) {
// We only translate the built in type names
case WEBNOVIN_CUSTOMER_UT_ID:
$types[$type->ut_id] = t($type->ut_label);
break;
case WEBNOVIN_PREFERRED_CUSTOMER_UT_ID:
$types[$type->ut_id] = t($type->ut_label);
break;
case WEBNOVIN_SALES_AGENT_UT_ID:
$types[$type->ut_id] = t($type->ut_label);
break;
default:
$types[$type->ut_id] = $type->ut_label;
}
}
return $types;
}`
I generated form:
function test_form($form_state) {
$form['hidden'] = array(
'#type' => 'hidden',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Save'
);
return $form;
}
After that I have a loop:
foreach($ea as $name){
$test_form = drupal_get_form('test_form');
$output .= $name->name . drupal_render($test_form);
}
It should somehow arrange that every time when do the loop, hidden in test_form take value of $name->name? Is it possible to do something with form_set_value($element, $value, &$form_state) ?
You'd be best off passing the name to the function as a parameter:
function test_form($form_state, $name) {
$form['hidden'] = array(
'#type' => 'hidden',
'#value' => $name
);
//...
}
foreach ($ea as $name) {
$test_form = drupal_get_form('test_form', $name->name);
$output .= $name->name . drupal_render($test_form);
}
I'm building a Drupal module to tie an icon to a particular page using an administration form. Each image placed within a certain directory needs to be output with a select box next to it showing all the primary link titles.
I've built the form using a foreach loop but when I check the output using dpm($form); in the _submit function the #value for each images page element is always equal to what ever is set for the last image.
Here's my code:
function titleicon_admin_settings() {
$settings = variable_get('titleicon_settings', $default);
//build an array of primary link titles
$primary_links_items = menu_primary_links();
foreach ($primary_links_items as $item) {
$title = $item['attributes']['title'];
$href = $item['href'];
$titles[$href] = $title;
}
//build array of icons
$directory = file_directory_path() . '/icons';
$mask = '(jpg|jpeg|gif|png|JPG|JPEG|GIF|PNG)';
$icons = file_scan_directory($directory, $mask);
foreach ($icons as $icon) {
$name = $icon->name;
$path = base_path() . $icon->filename;
$html = '<img src="' . $path . '" width="50" height="50" />';
$default_value = $settings[$name]['page'];
$form[$name] = array(
'#type' => 'fieldset',
'#title' => $name,
);
$form[$name]['path_to_icon'] = array(
'#type' => 'value',
'#value' => $path,
);
$form[$name]['icon'] = array(
'#type' => 'markup',
'#value' => $html,
);
$form[$name]['page'] = array(
'#type' => 'select',
'#title' => t('Show icon on page'),
'#default_value' => $default_value,
'#description' => t('Choose which page to show icon on.'),
'#options' => $titles,
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
Which makes perfect sense. If your fields are declared like this:
$form[$name]['path_to_icon'] = array(
'#type' => 'value',
'#value' => $path,
);
then for each file you are updating the same variable - 'path_to_icon'. Fieldset key "$name" does not matter here, as it is used only for grouping form fields together.
You would need to use something more like:
$form[$name]['path_to_icon_'.$name] = array(
'#type' => 'value',
'#value' => $path,
);
then you will get multiple values after posting the form.
However, to tell the truth, I wouldn't use $name as element of variable name, you should rather have something like auto-incrementing $fid (file id from files table) or any other unique and SAFE identifier for each file...
I must say that if you put "#tree => TRUE" in the declaration of your fieldset :
$form[$name] = array(
'#type' => 'fieldset',
'#title' => $name,
'#tree' => TRUE
);
you don't have to put "_'.$name" in all your form elements. Drupal will group all the form results in arrays keyed by $name.