I'm creating a custom user settings page. I have one field: zip_code that get's it's initial value from a custom user field. I have a custom function that pulls external data using the value of the zip_code.
I currently have the default value of the field set to the custom user field (if it's available). This is working as designed; however, I want to give the user the ability to change their zip code via an Ajax callback. This would replace the already populated radio buttons with new ones. I can't seem to wrap my head around this. Here's my code:
function settings_shopping_form($form, &$form_state) {
include_once "external.inc";
// Get user fields
global $user;
$user_fields = user_load($user->uid);
$zipcode = $user_fields->zip_code['und']['0']['value'];
if(isset($zipcode)) {
$form['zip_code'] = array(
'#title' => t('Zip Code'),
'#type' => 'textfield',
'#required' => TRUE,
'#default_value' => $zipcode,
'#ajax' => array(
'callback' => 'settings_form_callback',
'wrapper' => 'textfields',
),
);
$storename = getmystorename($zipcode);
if(count($storename) > 0) {
$form['textfields'] = array(
'#prefix' => '<div id="textfields">',
'#suffix' => '</div>',
'#type' => 'fieldset' );
$form['textfields']['stores'] = array(
'#type' => 'radios',
'#title' => t('Choose your store:'),
'#options' => $storename,
'#default_value' => $storename[1], );
} else {
$form['textfields']['incorrect'] = array(
'#title' => t('Sorry, there are no stores available near you. Check back later'),
'#type' => 'fieldset', );
}
}
My callback function is very simple:
function settings_form_callback($form, $form_state) {
return $form['textfields'];
}
To reiterate: I want to add the ability to replace the populated radio buttons with new buttons generated by the getmystorename function when the zip_code field is changed.
I ended up taking an example from the examples module (love it!):
$defaults = !empty($form_state['values']['zip_code']) ? $form_state['values']['zip_code'] : $zipcode;
$storename = getmystorename($defaults);
I put this before the start of my form so that the values load before the form builder.
Related
I am currently developing a custom module for Drupal 8.
While adding a backend form to get some data from users I tried to get the ckeditor configured to replace my textareas... and failed >.<
here is the form definition:
$form['text'] = array(
'#type' => 'textarea',
'#title' => t('Text'),
'#required' => TRUE,
'#attributes' => array(
'id' => 'text',
'style' => 'max-width: 650px'
),
'#default_value' => $data['text']
);
where do I need to load the ckeditor to replace my textarea?
things I already tried:
$build['#attached'] = array(
'js' => array(
drupal_get_path('module', 'ckeditor') . '/js/ckeditor.js'
drupal_render($build);
and
drupal_load_library("ckeditor", "ckeditor");
but I can't load all the dependencies
Refer to the Drupal 8 form API which has a text_format form type. A check on your #default_value is also recommended.
Navigate to Configuration > Content authoring > Text formats and editors giving you an overview of the current available text formats. The ones listed here can be used in the #format proprty of your form field. (Default fallback currently is basic_html)
Try to change your code to:
$form['text'] = array(
'#type' => 'text_format',
'#title' => t('Text'),
'#required' => TRUE,
'#default_value' => isset($data['text']) ? $data['text'] : '',
'#format' => 'full_html',
);
I had a similar issue but had issues saving the data with above solution. In the end it looked like this:
$form['body'] = [
'#type' => 'text_format',
'#title' => $this->t('Body'),
'#description' => $this->t('Message'),
'#default_value' => $config->get('body'),
'#format' => 'full_html',
];
and in submitForm method I save config like so
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$this->config('my_module.default')
->set('body', $form_state->getValue('body')['value'])
->save();
}
Note the reference to the attached ['value'] array.
I am using the hook_form_FORM_ID_alter in Drupal 7 to create a custom form so that the user can enter and edit data which is then attached to a custom node type.
For a new node the default number of input boxes in a group is 10, this can then be added to in groups of 5. When the node is reloaded for editing the saved data is used to create the form with whatever number of inputs have been saved previously and also the ability to add more fields in the same manner as required.
I have managed to get both the initial version of the form and the editing version to work using the following code however when the 'add five' button is pressed and the AJAX called (in both cases), any values which have been entered without saving are removed.
<?php
/**
* Implements hook_form_FORM_ID_alter().
*/
function entries_form_form_entries_node_form_alter(&$form, &$form_state, $form_id) {
$node = $form['#node'];
// fieldset
$form["section"] = array(
'#type' => 'fieldset',
'#title'=> 'Section 1',
);
$form["section"]["termwrapper"] = array(
"#type" => 'container',
"#attributes" => array(
"id" => 'groupWrapper',
),
);
$form["section"]["termwrapper"]["terms"]["#tree"] = TRUE;
if(!isset($form_state['fired'])){
$form_state['terms'] = $node->entries_form['term'];
}
foreach ($form_state['terms'] as $key => $values) {
$form["section"]["termwrapper"]["terms"][$key] = array(
'#type' => 'textfield',
'#size' => 20,
'#attributes' => array(
'class' => array('left'),
),
'#value' => $values,
);
}
$form['section']['addFive_button'] = array(
'#type' => 'submit',
'#value' => t('+5'),
'#submit' => array('entries_form_add_five_submit'),
'#ajax' => array(
'callback' => 'entries_form_commands_add_callback',
'wrapper' => 'groupWrapper',
),
'#prefix' => "<div class='clear'></div>",
);
dpm($form_state);
}
function entries_form_commands_add_callback($form, $form_state) {
return $form["section"]["termwrapper"];
}
function entries_form_add_five_submit($form, &$form_state){
$form_state['rebuild'] = TRUE;
$form_state['fired'] = 1;
$values = $form_state['values'];
$form_state['terms'] = $values['terms'];
$numterms = count($values['terms']);
$addfivearray = array_fill($numterms,5,'');
$form_state['terms'] = array_merge($values['terms'],$addfivearray);
}
/**
* Implements hook_node_submit().
*/
function entries_form_node_submit($node, $form, &$form_state) {
$values = $form_state['values'];
$node->entries_form['term'] = $values['term'];
}
/**
* Implements hook_node_prepare().
*/
function entries_form_node_prepare($node) {
if (empty($node->entries_form)){
$node->entries_form['term'] = array_fill(0, 10, '');
}
}
/**
* Implements hook_node_load().
*/
function entries_form_node_load($nodes, $types) {
if($types[0] == 'entries'){
$result = db_query('SELECT * FROM {mytable} 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);
}
}
}
/**
* Implements hook_node_insert().
*/
function entries_form_node_insert($node) {
if (isset($node->entries_form)) {
db_insert('mytable')
->fields(array(
'nid' => $node->nid,
'term' => json_encode($node->entries_form['term']),
))
->execute();
}
}
How can I keep the values which have been typed in and retain the ajax functionality?
Any help or pointers much appreciated. This is my first dip into Drupal so I'm sure there is something which hopefully is quite obvious that I'm missing.
Ok, I think I finally have the answer.
Within the foreach that builds the form input boxes I had set '#value' => $values, when it seems that '#default_value' => $values, should have been set instead.
The updated section of the code that is now working for me is as follows
foreach ($form_state['terms'] as $key => $values) {
$form["section"]["termwrapper"]["terms"][$key] = array(
'#type' => 'textfield',
'#size' => 20,
'#attributes' => array(
'class' => array('left'),
),
'#default_value' => $values,
);
}
Seems to be as simple as that. Hope this helps someone else.
I've done a bit of searching online but I have not found any answers to this question yet. I have a situation where I need a product attribute that is a decimal value and it must support negative numbers as well as positive and must also be sortable. For some reason, Magento does not have a "decimal" attribute type. The only type that uses decimal values is Price, but that doesn't support negative numbers. If I use "text" as the type, it supports whatever I want, but it doesn't sort properly because it sees the values as strings rather than floating point numbers. I have been able to work around this issue, as others have in posts I've found, by manually editing the eav_attribute table and changing 'frontend_input' from 'price' to 'text', but leaving the 'backend_type' as 'decimal'. This works great...until someone edits the attribute in the admin panel. Once you save the attribute, Magento notices that the frontend_input is 'text' and changes the 'backend_type' to 'varchar'. The only way around this that I can think of is by creating a custom attribute type, but I'm not sure where to start and I can't find any details online for this.
Has anyone else experienced this problem? If so, what have you done to correct it? If I need to create a custom attribute type, do you have any tips or can you point me at any tutorials out there for doing this?
Thanks!
What you want to do is create a custom attribute type.
This can be done by first creating a installer script (this updates the database).
startSetup();
$installer->addAttribute('catalog_product', 'product_type', array(
'group' => 'Product Options',
'label' => 'Product Type',
'note' => '',
'type' => 'dec', //backend_type
'input' => 'select', //frontend_input
'frontend_class' => '',
'source' => 'sourcetype/attribute_source_type',
'backend' => '',
'frontend' => '',
'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_WEBSITE,
'required' => true,
'visible_on_front' => false,
'apply_to' => 'simple',
'is_configurable' => false,
'used_in_product_listing' => false,
'sort_order' => 5,
));
$installer->endSetup();
After that you need to create a custom php class named:
Whatever_Sourcetype_Model_Attribute_Source_Type
And in there paste this in:
class Whatever_Sourcetype_Model_Attribute_Source_Type extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
{
const MAIN = 1;
const OTHER = 2;
public function getAllOptions()
{
if (is_null($this->_options)) {
$this->_options = array(
array(
'label' => Mage::helper('sourcetype')->__('Main Product'),
'value' => self::MAIN
),
array(
'label' => Mage::helper('sourcetype')->__('Other Product'),
'value' => self::OTHER
),
);
}
return $this->_options;
}
public function toOptionArray()
{
return $this->getAllOptions();
}
public function addValueSortToCollection($collection, $dir = 'asc')
{
$adminStore = Mage_Core_Model_App::ADMIN_STORE_ID;
$valueTable1 = $this->getAttribute()->getAttributeCode() . '_t1';
$valueTable2 = $this->getAttribute()->getAttributeCode() . '_t2';
$collection->getSelect()->joinLeft(
array($valueTable1 => $this->getAttribute()->getBackend()->getTable()),
"`e`.`entity_id`=`{$valueTable1}`.`entity_id`"
. " AND `{$valueTable1}`.`attribute_id`='{$this->getAttribute()->getId()}'"
. " AND `{$valueTable1}`.`store_id`='{$adminStore}'",
array()
);
if ($collection->getStoreId() != $adminStore) {
$collection->getSelect()->joinLeft(
array($valueTable2 => $this->getAttribute()->getBackend()->getTable()),
"`e`.`entity_id`=`{$valueTable2}`.`entity_id`"
. " AND `{$valueTable2}`.`attribute_id`='{$this->getAttribute()->getId()}'"
. " AND `{$valueTable2}`.`store_id`='{$collection->getStoreId()}'",
array()
);
$valueExpr = new Zend_Db_Expr("IF(`{$valueTable2}`.`value_id`>0, `{$valueTable2}`.`value`, `{$valueTable1}`.`value`)");
} else {
$valueExpr = new Zend_Db_Expr("`{$valueTable1}`.`value`");
}
$collection->getSelect()
->order($valueExpr, $dir);
return $this;
}
public function getFlatColums()
{
$columns = array(
$this->getAttribute()->getAttributeCode() => array(
'type' => 'int',
'unsigned' => false,
'is_null' => true,
'default' => null,
'extra' => null
)
);
return $columns;
}
public function getFlatUpdateSelect($store)
{
return Mage::getResourceModel('eav/entity_attribute')
->getFlatUpdateSelect($this->getAttribute(), $store);
}
}
Hope this helps.
For further info see here.
As per bens comment and answer I updated my script, comments indicate changes
{Magento 1.4.0.1} currently i have an installer script:
$installer = $this;
$installer->startSetup();
//commented out to use factory method
//$setup = new Mage_Catalog_Model_Resource_Eav_Mysql4_Setup('core_setup');
$setup = Mage::getResourceModel('catalog/setup','core_setup');
if(!$setup->getAttribute('catalog_product','attribute_code')){
$newFields = array(
'attribute_code' => array(
'type' => 'text',
'label' => 'Attribute Label',
//added visible option
'visible' => false,
),
);
$entities = array(
'catalog_product',
);
foreach($newFields as $attributeName => $attributeDefs) {
foreach ($entities as $entity) {
$setup->addAttribute($entity, $attributeName, array(
'type' => $attributeDefs['type'],
'label' => $attributeDefs['label'],
//added visible option
'visible' => $attributeDefs['visible'],
'class' => '',
'required' => false,
));
}
}
}
$installer->endSetup();
It works wonderfully! Except the attribute shows up in the General attribute group when editing the product and I don't want it to show up at all (its a secret ninja attribute) is there something I'm doing wrong? or perhaps something I should be doing to let Magento know not its not supposed to show up?
Using addAttribute() you can set the 'visibleindex tofalse. UsingupdateAttribute()` you should do the following:
$setup->updateAttribute('catalog_product','attr_code','is_visible',false);
Let me know if I'm wrong.
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'];
}