Hi!

I am new to Drupal (only 6 months) and I am writing my first module (code name: ActiveField). The module is fully documented on my blog:
http://www.sageofcode.com/?p=59

This module takes the values entered for pre-defined “source” fields and dynamically sets the values of predefined “target” fields via jQuery and AJAX. The “source” and “target” fields can be of any field type; i.e., they are not limited to drop down menu fields.

I think this module may be useful to the Drupal community as a whole (I welcome you feedback on this).
If so, I would like to submit it as a Contributed Module and updated and move the documentation into the drupal.org site proper.

I hope that's enough background.

Using the AJAX example in "Pro Drupal Development" [1st ed.] as a starting point, I have build the module.
But, unfortunately, it's not working. The module activates. But, when I look into the menu table, I do no see an evidence that the menu item was actually created. According to VanDkyk and Westgate (p. 47):

"The menu item type, such as MENU_CALLBACK or DEFAULT_LOCAL_TASK, is represented in the database by its decimal equivalent."

I do not suspect that I am missing any critical module (c.f. http://www.sageofcode.com/?p=59 ) and so I do not think it's an environmental issue.

I now have 6 books on Drupal and 2 on jQuery on my book shelf, several videos, and articles on the subject.
But, unfortunately, I am now losing gray hairs over this issue. I have gone through ever line and character in my code. I simply cannot slay this bug. I will continue reaching a higher level of ignorance, but if anyone could help me out, I would greatly appreciate it.

CODE
Here is the code for each file. I did not create a .install file because I am not creating or deleting any database tables based in the drupal database schema.

FILE: ActiveField.info

/** ActiveField
* 
* This file registeres the ActiveField module with Drupal.
* The module can hence be enabled or disabled via admin interface
*
*/

name = ActiveField
description = "This module dynamically sets the value of a field based on another field(s) values."
version = 5.x-1.9.1

File: ActiveField.js

/** ActiveField.js
*
* JavaScript file with jQuery
* Used by ActiveField.module
*
*/

//$Id$

if(Drupal.jsEnabled)
{
    $(document).ready(function()
		      {
                          // Parent field & respective event    
                          // Adding change event to source Field
                          $('#edit-field-location-key').change(function(event)
                                                {
                                                    
                                                     // Call back function for AJAX call
                                                     var frmDrupal = function(data)
                                                           {
                                                               // convert the value from Drupal to JSON
                                                               var result = Drupal.parseJson(data);
                                                               // Set the child field(s)' value(s)
                                                               // setting the text for the "test" text field
                                                               $('#edit-field-capital-value').text(result);
                                                        
                                                           }

                                                     //AJAX call
                                                    // URL: add/field-trip/      - maps to a Drupal function
                                                    // Parameters: null for now.
                                                    // Call back function:  "fromDrupal"
                                                  
                                                    $.get(add/field-trip, null, frmDrupal);
                                                    // preventing entire page from reloading
                                                    return false;
                                                }); 

                      });	

}

FILE: ActiveField.module

<?php
//$id$

/**
* @file
* A dynamic parent-to-child field module via Drupal, Ajax, and jQuery
*
*/

/**
*
* Implementation of hook_perm().
* Adding the "Use ActiveField" permission to Drupal's role-based access control page.
* Prevents Anonymous use.
*
*/
function ActiveField_perm(){
    return array('Use ActiveField');
}

/**
* Implementation of hook_menu()
* Allows for the mapping for jQuery intercepted URL to a Drupal PHP function
* The Drupal PHP function will return the child field's value(s) to jQuery in JSON
*/
function ActiveField_menu($may_cache){
    $items = array();

    if($may_cache)
    {
        $items[] = array(
                           'path'     => 'add/fishing-trip',
                           'callback' => 'ActiveField_getTargets',
                           'type'     => MENU_CALLBACK,
                           'access'   => user_access('Use ActiveField360')                      
                        );
    }
    
    return $items;

}

/**
*
* Theming function
* 
*/
function theme_ActiveField()
{
    // Loading the jQuery .js and CSS files
    drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');
    drupal_add_css(drupal_get_path('theme', 'garland'), '/style.css');

}

/**
*
* Called by jQuery
* Submits the parent field value and returns the child field's value(s) as a JSON
*/
function ActiveField_getTargets()
{
   
    // NOTE: GET PARENT FIELD VALUE FROM jQuery TO BUILD sQL STRING
  
    // this particular query is just for testing.
    $sql="SELECT title FROM {node} WHERE type='location'";
    $result = db_result(db_query($sql));
    
    print drupal_to_js($result);


exit();

}

[Edited to add code tags: nevets]

Comments

nevets’s picture

To start I see the following issues

theme_ActiveField: 1) I do not see where it is invoked. 2) No reason to include the theme style sheet

ActiveField.js vs ActiveField.module: ActiveField.js uses 'add/field-trip' for the path, ActiveField.module uses 'add/fishing-trip'.

ActiveField.js: add/field-trip needs quotes around it and should probably have a leading slash ie '/add/field-trip'.

SageOfCode’s picture

nevets,

Thank for for the feedback. I have made the code changes that you suggested, but the module still does not work.
Is there anything else I can do?

I trying to debug this, I came up with some questions...but I am not sure how to answer them:

1. Is the AJAX call being made to Drupal?
2. Does the _getTargets() method get called?
3. Does the SQL query get executed?
4. Does the call back function in the AJAX function get called?

Is there a way to "step through" the code in "debug" mode and see what the values of certain variables?
Is there a way to see a "stack trace" of all the functions that get called?

Thank you again!! Much appreciated.

Take care.

www.sageofcode.com

nevets’s picture

There are two parts to your puzzle, the client/js side and the server/php side. There are debuggers for PHP that allow you to step through the code though I have never bothered to set one up. On the client (browser) side if you use Firefox the firebug extension is really useful for tasks like this, It includes a debugger that lets you place breakpoints in you javascript.

drawk’s picture

As nevets mentioned, firebug is your best friend here to determine whether the ajax callback is being invoked, and to examine what is being returned (use the 'console' tab in firebug).

The cheap and easy way to debug the rest (ie: PHP side) of it is to place 'print "DEBUG: here"; exit;' in code locations that you are curious about.

Also try a "DELETE FROM cache_menu" on your database.

nevets’s picture

Minor point the 'print "DEBUG: here"; exit;' does not work very well for callbacks that are expected to return javascript.

Also over 'print' I prefer to use drupal_set_message(), something like drupal_set_message("FunctionName: entered"); as it does not interfere with the normal output from the page.

drawk’s picture

Agreed, as mentioned the "print" statements should be used for the 'PHP proper' portions, while those that are expected to return javascript are best monitored via firebug (although you could, if you wished, monitor the printed output through firebug as well, and in realtime).

I prefer the quick and dirty "show me what I want to know and terminate immediately", but nevets makes a good point that this method may be jarring and does prevent the normal rendering of the page, which could be desirable or undesirable depending on what you are attempting to accomplish, and what you need to see.

SageOfCode’s picture

Thank you for the debugging info. Very helpful.

Starting with client side debugging via FireBug, I noticed that no posts show up in the "console" tab as I go through the use case.
That is, after I select a "date" and a "location", the change event on the "location" field should fire an Ajax call--- which it does not.
Therefore, it looks like the URL is not being mapped to the Drupal function and the Javascript file is not being loaded?
Is my understanding correct? Any suggestions?

Aside:

The current version of the jQuery library that I am running is , does this version support AJAX? Should I update to version ?
Also, what should I look for in the menu table to verify that the menu item that for the URL was created? I looked but I did not "see" any evidence that a row was created.

File: ActiveField.info

/** ActiveField
* 
* This file registeres the ActiveField module with Drupal.
* The module can hence be enabled or disabled via admin interface
*
*/

name = ActiveField
description = "This module dynamically sets the value of a field based on another field(s) values."
version = 5.x-1.9.1

File: ActiveField.js

/** ActiveField.js
*
* JavaScript file with jQuery
* Used by ActiveField.module
*
*/

//$Id$

if(Drupal.jsEnabled)
{
    $(document).ready(function()
		      {
                          // Parent field & respective event    
                          // Adding change event to Hot Spot Field
                          $('#edit-field-location-key').change(function(event)
                                                {
                                                    
                                                     // Call back function for AJAX call
                                                     var frmDrupal = function(data)
                                                           {
                                                               // convert the value from Drupal to JSON
                                                               var result = Drupal.parseJson(data);
                                                               // Set the child field(s)' value(s)
                                                               // setting the text for the "test" text field
                                                               $('#edit-field-capital-value').text(result);
                                                        
                                                           }

                                                     //AJAX call
                                                    // URL: add/field-trip/      - maps to a Drupal function
                                                    // Parameters: null for now.
                                                    // Call back function:  "frmDrupal"
                                                  
                                                    $.get('/add/field-trip', null, frmDrupal);
                                                    // preventing entire page from reloading
                                                    return false;
                                                }); 

                      });	

}

File: ActiveField.module

<?php
//$id$

/**
* @file
* A dynamic source-to-target field module via Drupal, Ajax, and jQuery
*
*/

/**
*
* Implementation of hook_perm().
* Adding the "Use ActiveField" permission to Drupal's role-based access control page.
* Prevents Anonymous use.
*
*/
function ActiveField_perm(){
    return array('Use ActiveField');
}

/**
* Implementation of hook_menu()
* Allows for the mapping for jQuery intercepted URL to a Drupal PHP function
* The Drupal PHP function will return the child field's value(s) to jQuery in JSON
*/
function ActiveField_menu($may_cache){
    $items = array();

    if($may_cache)
    {
        $items[] = array(
                           'path'     => 'add/field-trip',
                           'callback' => 'ActiveField_getTargets',
                           'type'     => MENU_CALLBACK,
                           'access'   => user_access('Use ActiveField')                      
                        );
    }
    
    return $items;

}


/**
*
* Called by jQuery
* Submits the parent field value and returns the child field's value(s) as a JSON
*/
function ActiveField_getTargets()
{
     drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');
     
    // this particular query is just for testing.
    $sql="SELECT title FROM {node} WHERE type='location'";
    $result = db_result(db_query($sql));
    
    print drupal_to_js($result);


exit();

}



drawk’s picture

Hmm, ok, let's debug this ..

First thing to do is to clear your cache, particularly the cache_menu table (via MySQL, the command line, or devel, whichever suits you)

Then, check via firebug that ActiveField.js is actually being included when you visit add/field-trip. You can do this by clicking the js tab in firebug and going through the list of files. You should see ActiveField.js .. if so, select it and make sure the contents of your js file are show up.

nevets’s picture

To test the path simply enter it in your browser URL bar, so if your site is at www.example.com, use http://www.example.com/add/field-trip (assumes use have clean urls, otherwise add 'add/field-trip' after 'q='). If the path does not work visit the menu administration page and try again.

Something I notice is the only place you show you add ActiveField.js is in ActiveField_getTargets() which is the wrong place since that function is simply the callback for the ajax call. At some point you need to add ActiveField.js for the page that displays the form.

Another suggestion is to use $.ajax instead of $.get, while a little harder to use it allows you define a callback for failures (can make debugging easier).

Also firebug can be useful by looking at the net tab.

drawk’s picture

Ah, nevets caught it. He's right -- you aren't including your javascript which has the AJAX callback anywhere except inside of the callback itself, so it will never get added.

SageOfCode’s picture

nevets and drawk, once again, great feedback. THANK YOU!

I did some digging, thinking, and hacking...

In summary:

1. hook_nodeapi appears to be the appropriate place to load the .js file because it allow one to modify a node as it's being created and updated. I hence implemented the hook (although, most likely improperly).

2. After reactivating the new version of the module, I cleared my cache via Devel.

3A. I set the site up for clean URLs. I have been meaning to do it; just never got to it. The following path works:
node/add/location (this is for the first content type in my example: http://www.sageofcode.com/?p=59 ). But, now the path node/add/field-trip (which is the path that is mapped the Drupal function) doe NOT work.
I get the following error message:

"Palo Alto"
Queries taking longer than 5 ms and queries executed more than once, are highlighted.
ms # where query

It almost looks like Drupal is having a hard time rendering the "field-trip" page.

3B. No post showed up in the Firebug console tab.

4. I just finished watching the Lullabot DVD on Understanding Drupal. I feel like I am at the "I suck" inflection point of the Drupal learning curve. :-)

Once again, thank you(!) in advance for your feedback.

Here is the updated code:

File: ActiveField.js

/** ActiveField.js
*
* JavaScript file with jQuery
* Used by ActiveField.module
*
*/

//$Id$

if(Drupal.jsEnabled)
{
    $(document).ready(function()
		      {
                          // Parent field & respective event    
                          // Adding change event to Hot Spot Field
                          $('#edit-field-location-key').change(function(event)
                                                {
                                                    
                                                     // Call back function for AJAX call
                                                     var frmDrupal = function(data)
                                                           {
                                                               // convert the value from Drupal to JSON
                                                               var result = Drupal.parseJson(data);
                                                               // Set the child field(s)' value(s)
                                                               // setting the text for the "test" text field
                                                               $('#edit-field-capital-value').text(result);
                                                        
                                                           }

                                                     //AJAX call
                                                    // URL: node/add/field-trip/      - maps to a Drupal function
                                                    // Parameters: null for now.
                                                    // Call back function:  "frmDrupal"

                                                    //using clean URLs, "node" is in the url; ergo, added it.                                                  
                                                    $.get('node/add/field-trip', null, frmDrupal);
                                                    // preventing entire page from reloading
                                                    return false;
                                                }); 

                      });	

}

File: ActiveField.module

<?php
//$id$

/**
* @file
* A dynamic parent-to-child field module via Drupal, Ajax, and jQuery
*
*/

/**
*
* Implementation of hook_perm().
* Adding the "Use ActiveField" permission to Drupal's role-based access control page.
* Prevents Anonymous use.
*
*/
function ActiveField_perm(){
    return array('Use ActiveField');
}

/**
* Implementation of hook_menu()
* Allows for the mapping for jQuery intercepted URL to a Drupal PHP function
* The Drupal PHP function will return the child field's value(s) to jQuery in JSON
*/
function ActiveField_menu($may_cache){
    $items = array();

    if($may_cache)
    {
        $items[] = array(
                           'path'     => 'node/add/field-trip',
                           'callback' => 'ActiveField_getTargets',
                           'type'     => MENU_CALLBACK,
                           'access'   => user_access('Use ActiveField')                      
                        );
    }
    
    return $items;

}

/**
*
* Implementation of hook_nodeapi()
*
*/
function ActiveField_nodeapi(&$node, $op, $teaser, $page)
{
    switch($op)
    {
        // handling the creation of content type use case
        case 'insert':
          // only authenticated users
          if ($user ->uid > 0) 
          {    
              drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');        
          }
          break;
          
        // handling the editing of content type use case
        case 'update':
        // only authenticated users
          if ($user ->uid > 0) 
          {    
              drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');        
          }
          break;                   
    }
}

/**
*
* Called by jQuery
* Submits the parent field value and returns the child field's value(s) as a JSON
*/
function ActiveField_getTargets()
{   
    // this particular query is just for testing.
    $sql="SELECT title FROM {node} WHERE type='location'";
    $result = db_result(db_query($sql));
    
    print drupal_to_js($result);

    exit();

}



www.sageofcode.com

nevets’s picture

Your use of hook_nodeapi is node going to get you where you want as the cases for $op being 'insert' or 'update' happen after the form is submitted. It appears that the case where $op is 'prepare' should work though hook_form_alter is generally considers more appropriate.

Using 'node/add/field-trip' as your callback is probably risky especially if 'field-trip' is a content type. In general the path node/add /{content -type} is used by the node module to add a new node of the type specified by {content-type}, ex node/add/page.

Your callback should be unique to your module and used only for handling the ajax part of your task.

SageOfCode’s picture

Nevets,

I used the 'prepare' option and the result it the same.

I also did some investigation into hook_form_alter, which requires a form id. Based on my investigation, it looks like all CCK content types have the same generic form id: "node-form". For example:


<form action="/MySite/node/add/location"  method="post" id="node-form" enctype="multipart/form-data">
<form action="/MySite/node/add/field-trip"  method="post" id="node-form" enctype="multipart/form-data">

Given this non-unique form id, I am not sure that hook_form_alter will work with CCK content types. If the form id is non-unique, how do I differentiate between different content types? I could very well be wrong. If so, please correct me.

In addition, the use of "node" in the URL paths in the AJAX $get and hook_menu() functions causes issues. When I use it, I get a white page with the error:

"Palo Alto"
Queries taking longer than 5 ms and queries executed more than once, are highlighted.
ms	#	where	query

...and the .js file does not appear to be loaded because no posts show up in the console tab.

Moreover, when I do not use clean URLs, then all content types will be created via ?q=node/add/. When I use ?q=node/add/field-trip or 'add/field-trip' or '/add/field-trip', the content type is displayed properly, but the .js file does not appear to be loaded because no posts show up in the console tab.

I thought I was 95% of the way there, but now I am starting to think that the approach identified in "Pro Drupal Development" 1st ed. for adding AJAX to Drupal modules via jQuery (Chapter 17) may not work for CCK content types. If so, are there alternative approaches?

I have been working on this module for a good amount of time with incremental progress and would love to just get it working.

I welcome any feedback.

Take care. :-)

www.sageofcode.com

drawk’s picture

The "Queries taking longer than 5 ms and queries executed more than once, are highlighted.
ms # where query" you are seeing is from Devel module. Try disabling Devel or add $GLOBALS['devel_shutdown'] = FALSE to your function.

SageOfCode’s picture

drawk, I will try this latter today.

After some more hacking and investigation, I stand corrected!
Each form in Drupal has a unique form ID, including CCK based content types:

"Modifying Forms in Drupal 5 and 6"
http://www.lullabot.com/articles/modifying-forms-5-and-6

Lullabot rocks!

I will exploit the form ids and do some hacking with hook_form_alter() an post my results in "Quick Update: 2 of 2".

www.sageofcode.com

SageOfCode’s picture

Based on nevets' feedback, (1) I abandoned the use of the hook_node-api() and implemented the hook_form_alter() and (2) I gave the callback a unique path to just handle the AJAX call (i.e. '/ActiveField/field-trip'). The updated code is as follows below. But, the module is still not working.

If I understand the sequence of events correctly...

1. When I got to "node/add/field-trip", the field-trip form is built and the ActiveField_form_alter() function is called and the .js file is added.

2. When I click the NET tab in Firebug, followed by clicking the "JS" tab, I should see the ActiveField.js file.

Note: Unfortunately, I do not see the ActiveField.js file.

3. When the drop down menu is opened and a value is selected, the change event will trigger the AJAX call and the CONSOLE tab of FireBugs should display the AJAX call.

Note: Unfortunately, I do not see the AJAX call.

4. Drupal will receive the Ajax and return a JSON

4. The "target" field is updated.

Aside: What I find interesting is that even with deleting the cache and a unique path for the AJAX call, the menu table still does not have a row for this menu item.

I have also updated the "ActiveField Specification" article (http://www.sageofcode.com/?p=59). How I have the module working, I will work on getting it on the contributed modules list and add documentation on the drupal.org site itself. My thought is that this module can be generalized for wider use. I welcome feedback on this idea also.

ANY help or feedback will be much, much, much, appreciated.

Here is the updated code.

/** ActiveField.js
*
* JavaScript file with jQuery
* Used by ActiveField.module
*
*/

//$Id$

if(Drupal.jsEnabled)
{
    $(document).ready(function()
		      {
                          // Parent field & respective event    
                          // Adding change event to Hot Spot Field
                          $('#edit-field-location-key').change(function(event)
                                                {
                                                    
                                                     // Call back function for AJAX call
                                                     var frmDrupal = function(data)
                                                           {
                                                               // convert the value from Drupal to JSON
                                                               var result = Drupal.parseJson(data);
                                                               // Set the child field(s)' value(s)
                                                               // setting the text for the "test" text field
                                                               $('#edit-field-capital-value').text(result);
                                                        
                                                           }

                                                     //AJAX call
                                                    // URL: node/add/field-trip/      - maps to a Drupal function
                                                    // Parameters: null for now.
                                                    // Call back function:  "frmDrupal"

                                                    //The URL (i.e. the server side resource to be called)
                                                    //needs to be unique to the module                                               
                                                    $.get('/ActiveField/field-trip', null, frmDrupal);
                                                    // preventing entire page from reloading
                                                    return false;
                                                }); 

                      });	

}

<?php
//$id$

/**
* @file
* A dynamic parent-to-child field module via Drupal, Ajax, and jQuery
*
*/

/**
*
* Implementation of hook_perm().
* Adding the "Use ActiveField" permission to Drupal's role-based access control page.
* Prevents Anonymous use.

*
*/
function ActiveField_perm(){
    return array('Use ActiveField');
}

/**
* Implementation of hook_menu()
* Allows for the mapping for jQuery intercepted URL to a Drupal PHP function
* The Drupal PHP function will return the child field's value(s) to jQuery in JSON
*/
function ActiveField_menu($may_cache){
    $items = array();

    if($may_cache)
    {
        $items[] = array(
                           'path'     => '/ActiveField/field-trip',
                           'callback' => 'ActiveField_getTargets',
                           'type'     => MENU_CALLBACK,
                           'access'   => user_access('Use ActiveField')                      
                        );
    }
    
    return $items;
    
}

/**
*
* Implementation of hook_form_alter()
*
*/
function ActiveField_form_alter($form_id, &$form)
{
    if ($form_id == 'field_trip_node_form')
    {
        drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');                                
    }
}

/**
*
* Called by jQuery
* Submits the parent field value and returns the child field's value(s) as a JSON
*/
function ActiveField_getTargets()
{   
    // this particular query is just for testing.
    $sql="SELECT title FROM {node} WHERE type='location'";
    $result = db_result(db_query($sql));
    
    print drupal_to_js($result);

    exit();

}

www.sageofcode.com

nevets’s picture

A couple things to try, first in ActiveField_form_alter() before the 'if' statement add drupal_set_message("ActiveField_form_alter($form_id, ...)"); to make sure that a) the function is being called and b) you have the correct form id. Visit node/add/field-trip to see what prints in the message area.

Also, after visiting the menu admin page (it will force the menus to rebuild) visit ActiveField/field-trip to see what happens.

SageOfCode’s picture

Nevets,

Thank you for the feedback on the "printf" statement (sorry, just had a C moment).

Ok, after some debugging, here are the results:

1. I added drupal_set_message("ActiveField_form_alter($form_id, ...)"); as you mentioned, and when I went to node/add/field-trip I got:

"ActiveField_form_alter(field_trip_node_form, ...)"

, which is the same form ID as in the IF statement of ActiveField_form_alter($form_id, &$form)

This also validates that function is being called. But, when I went to to the NET -> JS tab in FireBug, I did not see ActiveField.js.

When I went to ActiveField/field-trip , I got:
"Page not found"

2. Feeling creative, I added
drupal_set_message("ActiveField_menu($may_cache,...)");

just before the if statement in ActiveField_menu($may_cache)

And after I activated the updated module, I got the following messages on the Admin page:

    * ActiveField_menu(1,...)
    * ActiveField_menu(1,...)
    * The configuration options have been saved.
    * ActiveField_menu(1,...)
    * ActiveField_menu(,...)
    * ActiveField_form_alter(system_modules, ...)

When I went to: node/add/field-trip I got:


    * ActiveField_menu(,...)
    * ActiveField_form_alter(field_trip_node_form, ...)

When I went to the menu table, I still to not see an entry for the call back path.
I do see two entries for the two content types used in this module:

path                    title             type
node/add/field-trip     Field Trip        22
node/add/location       Location          54

Could the if logic in ActiveField_menu($may_cache) be the issue?

www.sageofcode.com

SageOfCode’s picture

Eureka!

Ok. After some hacking, I got the .js file to load.
In short, here are the modification that I made to the code:

1. Removed the "/" from the paths in the following functions:

 $.get('ActiveField/field-trip', null, frmDrupal);
function ActiveField_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path'     => 'ActiveField/field-trip',
      'callback' => 'ActiveField_getTargets',
      'type'     => MENU_CALLBACK,
      'access'   => user_access('Use ActiveField'),
    );
  }
  return $items;
  
}

2. Replaced "," with "." in the function:

function ActiveField_form_alter($form_id, &$form)
{
    
    if ($form_id == 'field_trip_node_form')
    {

        //OLD: drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');

        drupal_add_js(drupal_get_path('module', 'ActiveField') .'/ActiveField.js');                
                             
    }
}

This is what was causing the .js file from not being loaded.

But, alas I am still stumped. The final bug appears to be in the MENU_CALLBACK path.

(1). The .js file is loaded and the following error is displayed in the Console tab of FireBug:

Firebug needs to POST to the server to get this information for url:
http://sageofcode.com/ActiveField/field-trip

This second POST can interfere with some sites. If you want to send the POST again, open a new tab in Firefox, use URL 'about:config', set boolean value 'extensions.firebug.allowDoublePost' to true
This value is reset every time you restart Firefox This problem will disappear when https://bugzilla.mozilla.org/show_bug.cgi?id=430155 is shipped.
 

I click the "Load Response" button and I get this error:

<html><head><title>404 Not Found</title></head>
<body bgcolor=white>
<h1>404 Not Found</h1>
The requested URL /ActiveField/field-trip does not exist.
</body></html>

When I got to the http:///ActiveField/field-trip , I get

Palo Alto

What I find interesting is that when I go to the Menu callback of the "plus1" module example in chapter 17 of "Pro Drupal Devlopment" 1st Ed., I get a blank white page. The "plus1" module works just fine on my site. I have compared every character in the ActiveField_menu() function, and I cannot see an issue. I have confirmed that the code is executed. In fact, the access privilege is created!
Another interesting observation is that the menu table does not have a row for the MENU_CALLBACK for the plus1 module; at least as far as I can see.

The issue appears to be with the actual, path. But, I am out of ideas on how to debug this.

I welcome any feedback.

Thank you in advance.

Aside: once I have the module working, I am going to create a lesson for the Drupal Dojo.

www.sageofcode.com

Summit’s picture

Subscribing, greetings, Martijn

SageOfCode’s picture

After some more debugging and hacking, I got the module working further.

The reason the callback was not working was because there is a bug in the SQL code in the ActiveField_getTargets() function. So, for now, I am just returning a string to get the module "walking", so to speak.

The string is returned, as can be seen in the Console tab of FireBug and by going to
ActiveField/field-trip

However, the actual field in the page is not updated. Using Firebug, the following code updates the field on the page:

$('#edit-field-capital-0-0-value').val('AJAX via jQuery');

But, the same code in call back function in the ActiveField.js file does not update the field.

If anyone has any ideas on how to debug or fix this, I would greatly appreciate it.

Here is the updated code:

<?php
//$id$

/**
* @file
* A dynamic parent-to-child field module via Drupal, Ajax, and jQuery
*
*/

/**
*
* Implementation of hook_perm().
* Adding the "Use ActiveField" permission to Drupal's role-based access control page.
* Prevents Anonymous use.
*
*/
function ActiveField_perm()
{
    return array('Use ActiveField');
}

/**
* Implementation of hook_menu()
* Allows for the mapping for jQuery intercepted URL to a Drupal PHP function
* The Drupal PHP function will return the child field's value(s) to jQuery in JSON
*/

function ActiveField_menu($may_cache) 
{
    $items = array();
    if ($may_cache) 
    {   
        $items[] = array(
                         'path'     => 'ActiveField/field-trip',
                         'callback' => 'ActiveField_getTargets',
                         'type'     => MENU_CALLBACK,
                         'access'   => user_access('Use ActiveField'),
                        );
     }

     return $items;

    // debug
    drupal_set_message("ActiveField_menu($may_cache,...if..)");

}

/**
*
* Implementation of hook_form_alter()
*
*/
function ActiveField_form_alter($form_id, &$form)
{
    
    if ($form_id == 'field_trip_node_form')
    {
        // the "," does not work. cxr why
        //drupal_add_js(drupal_get_path('module', 'ActiveField'),'/ActiveField.js');
        drupal_add_js(drupal_get_path('module', 'ActiveField') .'/ActiveField.js');                
                             
    }
}

/**
*
* Called by jQuery
* Submits the parent field value and returns the child field's value(s) as a JSON
*/
function ActiveField_getTargets()
{   
    // Bug in SQL...using a string for testing
    //$sql="SELECT title FROM {node} WHERE type='location'";
    //$result = db_result(db_query($sql));

    $result = 'AJAX via jQuery';                      
    
    print drupal_to_js($result);

    exit();

}


/** ActiveField.js
*
* JavaScript file with jQuery
* Used by ActiveField.module
*
*/

//$Id$

if(Drupal.jsEnabled)
{
    $(document).ready(function()
		      {
                          // Parent field & respective event    
                          // Adding change event to Hot Spot Field
                          $('#edit-field-location-key').change(function(event)
                                {
                                                    
                                     // Call back function for AJAX call
                                     var frmDrupal = function(data)
                                           {
                                               // convert the value from Drupal to JSON
                                               var parsedResult = Drupal.parseJson(data);
                                               // Set the child field value

                                               $('#edit-field-capital-0-0-value').val(parsedResult);
                                                               
                                           }

                                     //AJAX call
                                    // URL: node/add/field-trip/      
                                    //      maps to a Drupal function
                                    //      i.e. the server side resource to be called
                                    //      must to be unique to the module
                                    // Parameters: null for now.
                                   // Call back function:  "frmDrupal"                                          
                                   $.get('ActiveField/field-trip', null, frmDrupal);

                                 // preventing entire page from reloading
                                 return false;
                             }); 

      });	

}

www.sageofcode.com

SageOfCode’s picture

I realize this thread is getting long, but for those following this...here is another quick update.

After some additional debugging, the code inside:

function(data)
{
    // convert the value from Drupal to JSON
    var parsedResult = Drupal.parseJson(data);
    // Set the child field value
    $('#edit-field-capital-0-0-value').val(parsedResult);
                                                              
}

..does not appear to be getting executed.

I am not sure why this function is not being entered.

I determined this by putting code to hide and show the target field, and nothing happened.

$('#edit-field-capital-0-0-value').hide();
$('#edit-field-capital-0-0-value').show();

I tested this code via FireBug, and it worked.

I am truly stumped.
Any feedback welcome.

www.sageofcode.com

interestingaftermath’s picture

I know I'm late to the party... by a couple years... but this is EXACTLY the functionality I need for a project I am working on. Was this ever completed? Is there another module that can do this?

Will Igetit’s picture

looking also for tat module or any similar ones.
Thanks for letting me know where to get a hand on it .

Sincerely,

Will