While nodes, comments, blocks and many other things in Drupal are themed using theme template files (like node.tpl.php), forms are a different story. There are no theme template files for forms. How can I get a particular form to use a custom theme template? |
| |
| It's completely reasonable to want to use a tpl file to display a form. You can use lots of extraneous CSS and #prefix /#suffix properties to achieve similar results, but by using tpl's you don't have to clutter up the separation of your logic and presentation layers and don't have to target ugly CSS selectors like #user-login label . Here's an example in Drupal 7... mytheme/template.php:
function mytheme_theme($existing, $type, $theme, $path) {
// Ex 1: the "story" node edit form.
$items['story_node_form'] = array(
'render element' => 'form',
'template' => 'node-edit--story',
'path' => drupal_get_path('theme', 'mytheme') . '/template/form',
);
// Ex 2: a custom form that comes from a custom module's "custom_donate_form()" function.
$items['custom_donate_form'] = array(
'render element' => 'form',
'template' => 'donate',
'path' => drupal_get_path('theme', 'mytheme') . '/template/form',
);
return $items;
}
custom_donate_form():
function custom_donate_form($form, &$form_state) {
$form['first_name'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('First name')),
);
$form['last_name'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('Last name')),
);
$form['address'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('Address')),
);
$form['city'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('City')),
);
$form['state'] = array(
'#type' => 'select',
'#options' => array(
'default' => 'State',
'...' => '...',
),
);
$form['zip'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('Zip')),
);
$form['email'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('Email')),
);
$form['phone'] = array(
'#type' => 'textfield',
'#attributes' => array('placeholder' => t('Phone')),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
mytheme/template/form/donate.tpl.php:
<div class="row">
<div class="small-12 medium-12 large-8 columns">
<div class="row">
<div class="small-12 columns">
<h5>Contact Information</h5>
</div>
</div>
<div class="row">
<div class="small-12 large-6 medium-6 columns">
<?php print render($form['first_name']); ?>
</div>
<div class="small-12 large-6 medium-6 columns">
<?php print render($form['last_name']); ?>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 large-6 columns">
<?php print render($form['address']); ?>
</div>
<div class="small-12 medium-6 large-6 columns">
<?php print render($form['city']); ?>
</div>
</div>
<div class="row">
<div class="small-12 medium-3 large-3 columns">
<?php print render($form['state']); ?>
</div>
<div class="small-12 medium-3 large-3 columns">
<?php print render($form['zip']); ?>
</div>
<div class="medium-6 large-6 columns"></div>
</div>
<div class="row">
<div class="small-12 medium-6 large-6 columns">
<?php print render($form['email']); ?>
</div>
<div class="small-12 medium-6 large-6 columns">
<?php print render($form['phone']); ?>
</div>
</div>
</div>
<div class="row">
<div class="small-12 medium-12 large-8 large-offset-2 columns">
<?php print render($form['submit']); ?>
</div>
</div>
</div>
<!-- Render any remaining elements, such as hidden inputs (token, form_id, etc). -->
<?php print drupal_render_children($form); ?>
This is using Foundation, which gives us a form like this: | | answered Feb 19 '14 at 22:01 |
|
| |
| Even though you may be able to use kiamlaluno's solution I personally wouldn't. What is your reason for needing a template file for a form? If it's because you want slightly different markup for an existing form? If so then you can use hook_form_alter() to modify the form before it is rendered. Using the Form API you you can modify all the form fields inject html elements etc. Here is an example of hook_form_alter() that I've created that modifies the standard drupal login form block:
/**
* Implements hook_form_alter().
*/
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
switch ($form_id) {
case 'user_login_block':
// Form modification code goes here.
$form['divstart'] = array(
'#value' => '<div style="background-color: red;">',
'#weight' => -1,
);
$form['instruct'] = array(
'#value' => '<p>Enter your username and password to login</p>',
'#weight' => 0,
);
$form['divend'] = array(
'#value' => '</div>',
'#weight' => 4,
);
break;
}
}
The above example wraps the entire form within a DIV which has an inline style to turn the background colour to red. It also adds a paragraph of help text to the beginning of the form. This is what my user login form looks like now once the above code is loaded: See the Form API reference for more information: Form API Reference | | answered Mar 4 '11 at 9:59 |
|
| |
| I have actually never needed to use a template file for a form. As far as I can see, Drupal core code uses theme functions, when a form, or part of a form needs to be rendered in a particular way; a theme function that calls drupal_render() is normally enough for any purposes. To reply to the question, creating a template file for a form is not different from creating a template file that is not for a form. Define a theme function, using as theme function the name of the form builder callback. The code should be similar to the following:
/**
* Implementation of hook_theme().
*/
function mymodule_theme() {
return array(
'mymodule_form' => array(
'template' => 'mymodule-form',
'file' => 'mymodule.admin.inc',
'arguments' => array('form' => NULL),
),
);
}
If the form contains the value $form['field_1'] , its value will be available in the template file as $field_1 . The template file will be also able to use any values passed from template_preprocess_mymodule_form() . | | answered Mar 4 '11 at 2:57 |
|
| |
| You have to implement hook_form_alter() in a module or template.php and set the form's #theme property:
/**
* Implements hook_form_alter().
*/
function hook_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'user_login') {
$form['#theme'] = array('overwrite_user_login');
}
}
Then implement new theme:
/**
* Implements hook_theme().
*/
function hook_theme($existing, $type, $theme, $path){
return array(
'overwrite_user_login' => array(
'render element' => 'form',
'template' => 'form--user_login',
'path' => $path . '/templates',
),
);
}
And then add form--user_login.tpl.php template with follow code to render form:
<?php print drupal_render_children($form) ?>
| | answered Jan 2 '13 at 16:05 |
|
| |
| I would always style by adding to my CSS file using selectors to identify the element to be styled as follows for the core login form
#user-login
{
border:1px solid #888;
padding-left:10px;
padding-right:10px;
background-image: url(http://www.zaretto.com/images/zlogo_s.png);
background-repeat:no-repeat;
background-position:right;
}
#user-login label
{
display: inline-block;
}
The above I simply add to sites/all/themes/theme-name/css/theme-name.css If what you need to style doesn't have an ID or a sufficiently accurate selector then it is necessary to use the hook approach to modify the HTML too add identifiers. IMO using inline style on elements is a very bad practice that should be deprecated and replaced by use of class and id | answered Mar 4 '11 at 13:01 |
|
| |
| We at DesignsSquare are starting to use Template API that comes with basic templates for contact, login, password-reset, register, comment, search forms. All, you have to do is drop you template in themes/YOUR-THEME/templates/template-api dir and user the Template API to insert content. See more info at http://designssquare.com/tapi-docs/ | answered Nov 25 '14 at 16:08 |
|
| |
| To theme a form, you can use a custom css, as explained in Themeing Drupal 7 Forms (Including CSS and JS). Basically you need to perform these steps: - Register a path to the form using hook_menu()
- Define the form
- Register a theme function with hook_theme()
- Write the theme function
- Create the CSS and JavaScript files
| | answered Apr 19 '13 at 5:11 |
|
| |
| I'm pretty sure you're able to use a template for forms, but you have to use hook_theme to register the template in the first place. I had a situation where the form really needed to be table based rather than div based and the simple #prefix and #suffix changes didn't really cut it. If interested I could probably try and dig up an example. |
来自
http://drupal.stackexchange.com/questions/312/how-to-a-use-a-template-file-to-theme-a-formDrupal Custom Elements API Errors
Posted on July 29, 2013, 4 p.m.
The Drupal Form API is powerful, but very extensive and complicated. In another post we discussed how to add a custom element from scratch. Watch out for the errors and mistakes highlighted in this advanced-level post.
Element and Field Defaults
To fully understand the API, you need to understand how elements are built. When you define a form field, custom or not, you have to give it a #type
.
When the form is being built, Drupal will look at the #type
and decide what to do with it. If it's a custom element you've defined, Drupal will look at the hook_element_info()
that defined that custom element, and grab all the information from that array. That is, keys and values in that array will be merged into your field. This allows you to set defaults for that element #type
. Let's look at an example. For simplification, let's define a basic custom element type:
|
<?php
function example_element_info() {
$elements['custom_element'] = array(
// Element declaration here ...
);
return $elements;
}
|
Note that this element won't do much. But now, let's use it in a form:
|
<?php
function example_some_form() {
$form['my_field'] = array(
'#type' => 'custom_element',
);
return $form;
}
|
If we want each usage of the element to, by default, have certain properties, we can just add it to the hook_element_info()
function. So doing this:
|
<?php
function example_element_info() {
$elements['custom_element'] = array(
// Rest of element declaration here ...
'#some_property' => 'some_property',
);
return $elements;
}
|
Will mean that all uses of your element in forms would be as if you had declared the form like so:
|
<?php
function example_some_form() {
$form['my_field'] = array(
'#type' => 'custom_element',
'#some_property' => 'some_property',
);
return $form;
}
|
In other words, #some_property
will "bubble" up from the element declaration to the actual element useful.
This means you can do powerful things, like declaring default #theme
functions, default #prefix
and #suffix
content, etc.
To summarize: the form declaration for that element will get merged to the properties defined in hook_element_info()
. So keep this in mind when defining your custom elements.
Hook Theme and Render Element
So you've defined your custom element, but now want to control its appearance. Easy right? Just give it a #theme
function in hook_element_info()
. Well, it's not that simple.
You might notice it's not rendering now or ignoring your theme function. So you go and add that function to the theme registry, using hook_theme()
.
But now we have another problem: our theme function isn't being called with the right arguments. That's because when defining this function in hook_theme()
, you need to use the render element
property, rather than your typical variables
property. As follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?php
// DO THIS
function example_theme() {
return array(
'gitbucket_field' => array(
'render element' => 'element',
),
);
}
// NOT THIS
function example_theme() {
return array(
'gitbucket_field' => array(
'variables' => array('element' => NULL),
),
);
}
|
And there you go: two big secrets about custom Drupal elements. With this knowledge on hand, you should be able to create all the custom elements you'll need. Without surprises.
Tags:
Drupal Drupal Planet来自
https://www.silviogutierrez.com/blog/drupal-custom-elements-api-errors/Custom Drupal Elements for Forms
Posted on July 8, 2013, 8:41 p.m.
One of the most cryptic tasks in Drupal is defining a custom form element. That is, defining your own element types to be used and resused in your forms. This post will walk you through the basics. And there's no better way than to learn by example.
For our example element, let's define one that links to a github or bitbucket repository. These two sites host a lot of open source projects, so it makes sense to have an element that handles both. Note: this guide applies to Drupal 7. You should be able to make it work on Drupal 6 with some minor modifications. Let's call our element gitbucket
.
A custom Drupal element in use.
Part of what makes the Form API in Drupal so powerful is that we can define compound custom elements. In other words, elements that contain one or more existing elements. This is quite useful for our use case, where we want our gitbucket
field to have both, a source
field (github or bitbucket) and ausername/repository
field. So let's get started.
The key to creating a custom element is hook_element_info()
, where we provide information about our element. So let's define it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?php
function example_element_info() {
$elements['gitbucket'] = array(
'#default_value' => '',
'#input' => TRUE,
'#process' => array('example_gitbucket_element_process'),
'#theme' => array('gitbucket_field'),
'#theme_wrappers' => array('form_element'),
'#tree' => TRUE,
'#value_callback' => 'example_gitbucket_element_value_callback',
);
return $elements;
}
|
As you can see above, we're providing the Form API with quite a bit of info. Let's walk through this hook step by step.
Our #process
callback is what gets called when we're going to use the element in a form. This is where we turn our element from a simple definition into the compound element that we want. So we provide a callback function for it. We'll get to that in a bit.
We also define a #theme
callback. This is what renders the inside of our element, including the subelements that we'll define. We have a lot of flexibility here when rendering. Using this callback, we'll make sure our element looks nice and readable.
Next we provide #theme_wrappers
. As you'd imagine, this wraps all the elements after they come back from the #theme
callback. Generally, you can just use the built-in form_element
and let it do the work for you. That's exactly what we're doing here.
Last but not least, we define #tree
and #value_callback
. The first makes sure our nested element structure is preserved for our values, rather than flattened. The second provides a way to process values before they are returned to the form submit function. You need to provide a #value_callback
if you want to do fancy processing on the input.
With all of that declared, we can now implement our #process
function to construct our very own custom element:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<?php
function example_gitbucket_element_process($element, $form_state, $complete_form) {
$element['gitbucket']['source'] = array(
'#type' => 'select',
'#empty_option' => '- ' . t('Source') . ' -',
'#options' => array(
'github' => 'github://',
'bitbucket' => 'bitbucket://',
),
'#required' => $element['#required'],
'#title' => t('Repository Source'),
'#title_display' => 'invisible',
'#theme_wrappers' => array(),
);
if (isset($element['#default_value']['gitbucket']['source'])) {
$element['gitbucket']['source']['#default_value'] = $element['#default_value']['gitbucket']['source'];
}
$element['gitbucket']['address'] = array(
'#type' => 'textfield',
'#size' => 50,
'#required' => $element['#required'],
'#title' => t('Repository Name'),
'#title_display' => 'invisible',
'#theme_wrappers' => array(),
'#attributes' => array('placeholder' => 'username/repository'),
);
if (isset($element['#default_value']['gitbucket']['address'])) {
$element['gitbucket']['address']['#default_value'] = $element['#default_value']['gitbucket']['address'];
}
return $element;
}
|
As you can see, we're nesting elements in the snippet above. Our custom element consists of a select field for the repository source, and a textfield for the actual repository. The actual declaration of these fields is straightforward, and you can use almost anything in the Form API.
When nesting elements, there are a couple of quirks to watch out for. First, you probably don't want a bunch of nested titles shown. That's why we set #title_display
to invisible
. But we still declare a title for error handling purposes. We want the Form API to be able to pinpoint fields required nested fields and the like.
You also don't want the wrapping HTML structure around each individual nested element. You want it outside the entire compound element. So for each nested element, we set #theme_wrappers
to an empty array.
And that's it. We load up default values as necessary, and we're done processing our custom elemenet. Let's move on to theming.
We want a nice inline look for our field, so let's keep the theming simple. First we need to register our function, and then implement it. Like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?php
function example_theme() {
return array(
'gitbucket_field' => array(
'render element' => 'element',
),
);
}
function theme_gitbucket_field($variables) {
$element = $variables['element'];
$output = '';
$output .= drupal_render($element['gitbucket']['source']);
$output .= " "; // This space forces our fields to have a little room in between.
$output .= drupal_render($element['gitbucket']['address']);
return $output;
}
|
Now we're ready for the last part: the value callback. As you can see, our custom element has two parts. You should select either or none. But what happens if they just select a source with no repository name? If we marked the field as required, Drupal handles that for us by requesting both fields be populated. The problem appears when our field is not marked as required. In this case, if we fill just one and not the other, we want to treat this as the user having left the field empty. We can use the value callback for this .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?php
function example_gitbucket_element_value_callback($element, $input = FALSE, &$form_state) {
if ($input !== FALSE) {
if ($input['gitbucket']['source'] && !$input['gitbucket']['address']) {
$input['gitbucket']['source'] = '';
}
if ($input['gitbucket']['address'] && !$input['gitbucket']['source']) {
$input['gitbucket']['address'] = '';
}
return $input;
}
elseif (!empty($element['#default_value'])) {
return $element['#default_value'];
}
return;
}
|
Value callbacks should be divided into three conditions:
- Input is being provided directly. This happens when a form is submitted.
- No input is provided, but the field definition has a default value.
- No input is provided and there is no default value.
In the first part, we handle the edge case of partial input. If only half the field is filled out, we simply treat it as empty. Then we return the modified input.
And that's it. You're done. You've now defined a custom compound field that can be used in any Drupal forms. For example, let's declare a simple system settings form that uses our newly minted element.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php
function example_form() {
$form = array();
$form['required_field'] = array(
'#type' => 'gitbucket',
'#title' => t('Your favorite repository'),
'#required' => TRUE,
'#default_value' => variable_get('required_field', array()),
);
$form['optional_field'] = array(
'#type' => 'gitbucket',
'#title' => t('Your least favorite repository'),
'#default_value' => variable_get('optional_field', array()),
);
return system_settings_form($form);
}
|
Try it out. Fill in partial input and hit submit. You'll see how our fields behave properly even with missing input.
来自
https://www.silviogutierrez.com/blog/custom-drupal-elements-forms/ | I'm having trouble understanding the 'render element' key purpose when implementing hook_theme().
function personal_news_theme($existing, $type, $theme, $path) {
return array(
'teaser_list_by_user' => array(
'render element' => 'element',
),
);
}
I did my research and the way I understand it, the render element key is use when creating a theme function to modify another theme function's output ONLY! And there's no way to use it as to implement a new theme function. If I'm wrong, how can I use it instead of variables ? |
| |
| 正确答案 In the example below, I created a new theme function using a render element.
/**
* Implements hook_theme().
*/
function rojo_theme() {
$items = array(
'rojo_content' => array(
'render element' => 'element',
),
);
return $items;
}
/**
* Theme function.
*/
function theme_rojo_content($vars) {
return '<pre>' . print_r($vars, TRUE) . '</pre>';
}
/**
* Render function.
*/
function rojo_render() {
$build = array(
'#theme' => 'rojo_content',
'#module' => 'rojo',
'content' => 'Page content for the render function',
'list' => array('one', 'two'),
'tag' => 'div',
);
return render($build);
}
This will print the output of the $vars passed into the theme function. From here, you should be able to see what is going on. The #theme property will be called with theme() and passed the $build array during the render() process. Notice I added #module property and Drupal added the #printed / #children properties. This is purely an example to demonstrate the creation of a new theme function using render element and argument passing. I hope this helps somebody out.
Array
(
[element] => Array
(
[#theme] => rojo_content
[#module] => rojo
[content] => Page content for the render function
[list] => Array
(
[0] => one
[1] => two
)
[tag] => div
[#printed] =>
[#children] =>
)
)
| | answered Oct 10 '14 at 21:24 |
|
| |
| If your theme function returns an array - you can specify which key of the array to use for rendering. render element: The name of the renderable element or element tree to pass to the theme function. This name is used as the name of the variable that holds the renderable element or tree in preprocess and process functions.
(Source: hook_theme)
来自 http://stackoverflow.com/questions/23644323/drupal-7-how-to-use-render-element-instead-of-variables-...
Join the Stack Overflow Community Stack Overflow is a community of 6.4 million programmers, just like you, helping each other. Join them; it only takes a minute: Sign up | Drupal doesn't call my theme function for my form in my module. I added the hook_theme in the .module file as this :
function agil_theme() {
return array(
'agil_list_form' => array(
'render element' => 'form',
),
);
}
where : - agil is the name of my module (not my theme)
- agil_list_form is the name of my form declaration (chich render with default theme)
I want to call a function to create my own markup, as this one :
function theme_agil_list_form($form) {
$output = "<table><th><td></td><td>".t('Title')."</td><td>".t('Link')."</td></th>";
$output .= "<tr><td>";
$output .= drupal_render($form['name']);
...
But Drupal is never calling this function... I cleared the cache but nothing.. Where do I miss something ? I read also this about new theme declaration in Drupal 7 :http://drupal.org/update/modules/6/7#hook_theme_render_changes | | asked Apr 21 '12 at 14:35 |
| | |
| 正确答案 All theme functions in Drupal 7 take a single array argument (usually named as $vars or $variables by convention), and that array contains the variables/render elements you've declared. The theme function itself would look like this:
function theme_agil_list_form($vars) {
$form = $vars['form'];
// Now manipulate $form
}
Also you need to tell Drupal that your form will be using this theme, by doing this in your form function:
$form['#theme'] = 'agil_list_form';
| answered Apr 21 '12 at 16:12 |
| | | When making my ticket-system I want managers to be able to reply to tickets when viewing the ticket. On the page to view any ticket I load my form. Because I want it to be a bit more fancier than just the default 'element under element' style I render each item by item manually.
<?php print drupal_render($form['open']); ?>
Now the problem is, my form doens't work. Clicking on my rendered submit doesn't perform any action. My guess is that rendering each item manually doesn't include the 'form container'. How should I include is? Or is my style of working completely wrong? | | asked Jun 11 '12 at 16:55 |
| | |
| The best way to implement custom rendering of a form would be to use a custom theme hook for the form:
/**
* Builder function for the ticket form.
*/
function MODULE_ticket_form($form, $form_state) {
// Build the $form array here...
// Let the theme API knows that this form use it own theme hook.
$form['#theme'] = 'ticket_form';
return $form;
}
/**
* Implements hook_theme().
*/
function MODULE_theme() {
return array(
// Custom theme hook for the ticket form.
'ticket_form' => array(
'render element' => 'form',
),
);
}
/**
* Default implementation for the ticket_form theme hook.
*/
function theme_ticket_form($variables) {
$form =& $variables['form'];
//Custom rendering of forms elements goes here...
$output = ...;
// Render every other elements (incl. hidden system ones).
$output .= drupal_render_children($form);
return $output;
}
Instead of using only a theme function, you can also use a template file. The theme function can also return a render array instead of a string. | answered Jun 11 '12 at 17:20 |
| | |
| Try following will work:
<?php $form = drupal_get_form('yourform_id'); ?>
<?php print '<form id="'.$form['#id'].'" accept-charset="UTF-8" method="'.$form['#method'].'" action="'.$form['#action'].'">'; ?>
<?php print drupal_render($form['form_id']);?>
<?php print drupal_render($form['form_build_id']);?>
<?php print drupal_render($form['form_token']);?>
<?php print '</form>'; ?>
|
|
|
print drupal_render_children($form)
which makes the form actually do stuff :). – Chris Rockwell Nov 13 '15 at 16:52engine
, if you are using something not default. E.g.'engine' => 'twig'
. – milkovsky Jul 21 at 16:47