How to embed a node form with drupal 6.

Submitted by fago on Mon, 08/11/2008 - 15:51
Developers often want to embed a node form. So I'll try to work out, how this is done best in drupal 6. For 5.x I provided the subform element module, which was an easy way to do it. However recently I found out, that embedding fully working node forms in 6.x isn't so easy any more. The funky JS stuff of node forms, which relies on the #ahah form API property, fails if $form doesn't look that way it expects.

So how to embed a node form with working AHAH?

Fortunately, it's not that hard either. We must just ensure to keep the structure of $form as in the original form. So don't move the embedded form in $form['fieldgroup'] - or it's #ahah stuff gonna break. So let's show how to embed a node creation form. First off we have to get the form array of the node form:
// Initialize new node:
$node = array('uid' =
$user->uid, 'name' => $user->name, 'type' => $type); $form = drupal_retrieve_form($type .'_node_form', $form_state, $node); drupal_prepare_form($type .'_node_form', $form, $form_state); ?> Now we have the form array, already altered by hook_form_alter() implementations. We could just go and use it - even its validation and submit handlers would be invoked. But we face two problems:

How to add own elements at the top and the bottom of the form?

That's not really hard. We have to add our #theme function that renders our own elements first and inserts the rendered, embedded form in the middle. Furthermore we make sure that the embedded form looks right by applying it's theme function. So we add our own #theme function and save the #theme property of the embedded form for later:
// Preserve the old #theme property.
$form['#theme_saved'] = $form['#theme'];
$form['#theme'] = 'embed_example_node_form_reuse';
Then we make sure the embedded form is themed too:
 * Themes the form to put the custom parts of the form to the top and the bottom.
function theme_embed_example_node_form_reuse($form) {
$top = drupal_render($form['top_example']);
$bottom = drupal_render($form['bottom_example']);

  if (isset(
$form['#theme_saved'])) {
// Apply the theme of the embedded form
$form['#theme'] = $form['#theme_saved'];

$top . drupal_render($form) . $bottom;

How to keep control over the form?
If the form comes with it's own validation, submit handlers and different buttons with different handlers it can be quite cumbersome to make sure that a own handler gets executed. So one can use a #after_build callback to just add the own handlers after the form API has determined the handlers, which are to be invoked.
// Register an after build callback
$form['#after_build'][] = 'embed_example_after_build';
When the #after_build callback gets executed, all button presses have been already detected, apart from a special case which fixes submitting single button forms for IE users. Usually this is not the case for node forms, but this code deals with this case too:
 * After build callback for the form.
 * This is used to register our own #validate and #submit handlers, which are invoked
 * regardless which button is pressed. So we keep control over the form.
function embed_example_after_build($form, &$form_state) {
// Call the ie cleanup first, so that all buttons are detected now.
_form_builder_ie_cleanup($form, $form_state);

$form_state['submit_handlers'][] = 'embed_example_form_submit';
$form_state['validate_handlers'][] = 'embed_example_form_validate';

That's it.

I've put this all together in a small example module, which can be found in my sandbox.

discursives@dr… (not verified)

Sat, 11/29/2008 - 03:46

the example is in the subform module! (not verified)

Wed, 01/21/2009 - 19:19

When calling $form = drupal_retrieve_form($type .'_node_form', $form_state, $node); with a type set to match the name of my CCK type $type = 'productregistration'; I keep getting the error: warning: call_user_func_array() []: First argument is expected to be a valid callback, 'node_form' was given in /var/www/html/drupal-6/includes/ on line 366. This is the start of the tutorial - so don't think i can have missed anything out previous to this call in my code. Any ideas?

liquidcms (not verified)

Tue, 01/05/2010 - 19:06

Should this technique be able to pull in the edit form of an EXISTING node? Rather than the $node init you have above i do this: $nrnode = node_load(896525); $nrform = drupal_retrieve_form($nrnode->type .'_node_form', $nrform_state, $nrnode); drupal_prepare_form($nrnode->type .'_node_form', $nrform, $nrform_state); at the end, $nrform is a form array but $form['nid'], etc are NULL and there are also none of the CCK fields included. Peter Lindstrom

so i tried your example module and replaced: $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type); wiith: $node = node_load(53383); and sure enough it is picking up the nid for the use in newly created form and does display title/body of $node - but doesn't include any of the CCK fields.

liquidcms (not verified)

Tue, 01/05/2010 - 20:19

In reply to by liquidcms (not verified)

not sure what i did; but this now seems to work... this snippet actually "attaches" a cck node edit form of an existing node to an field in another node edit form. the module this is in is weighted high so that the field groups have been established; then i am adding this to a field in a group. $nrnode = node_load(53383); $nrform_state = array(); $nrformid = $nrnode->type .'_node_form'; $nrform = drupal_retrieve_form($nrformid, $nrform_state, $nrnode); drupal_prepare_form($nrformid, $nrform, $nrform_state); $form['group_fsp']['field_pkg_fsp'][0]['_edit'] = $nrform; still messes up some of the vertical tabs of my original form but possibly some trimming or cleanup of attached from array to help that.

liquidcms (not verified)

Tue, 01/05/2010 - 20:21

In reply to by liquidcms (not verified)

hey fago, reason my post content was lost was because i used the < code > tag... posts don't display if that is used

LeonardNMark (not verified)

Fri, 03/26/2010 - 15:30

Basically this is what I used to do on Drupal 5.x: <?php print node_add('my_content_type'); ?> But on Drupal 6.x you need to put: <?php if( !function_exists("node_object_prepare")) { include_once(drupal_get_path('module', 'node') . '/'); } print node_add('my_content_type'); ?> Another alternative: <?php global $user; if( !function_exists("node_object_prepare")) { include_once(drupal_get_path('module', 'node') . '/'); } $type = 'your content type'; // Initialize settings: //research paper $node = array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => ''); print drupal_get_form($type .'_node_form', $node); ?>

mantuko (not verified)

Sun, 11/14/2010 - 13:05

I just started development with drupal and start hitting walls here and there. I tried to embed node forms into a multi step form created with the help of chaos tools as described here "" which at the moment still fails. So I dug into the code of "drupal_retrieve_form()" and its arguments and the first thing I don't get is why you pass along three arguments to a function that by definition only takes two ( "drupal_retrieve_form($form_id, &$form_state)" ). Update: Got it working. Yay! Just had to put some more items in the "$items"-array in the "'module-name'_menu"-function. Looks like I just get to know a few things I also should dig deeper into :) Thx for your post.

Imran (not verified)

Mon, 05/16/2011 - 10:03

What should I use for Drupal 7?

g (not verified)

Tue, 07/12/2011 - 01:47

Great approach. The link to your embed_example module seems to not point to the correct place anymore — can you provide an alternative. Also, how are you printing out the embedded for in tpl.php files? print drupal_render_form() outputs a form missing cck fields and broken fieldsets.