2

I would like to populate the "Owner" select option list by SELECTing the owner column from the #__gscrm_accounts table WHERE the gscrm_account_id (primary key) is the selected "Person or Business" value.

This ajax needs to be triggered by the onchange() event of <field name="account"> then populate <field name="owner">.

enter image description here

To be clear, I don't know if this is "fresh" or "modified" copy of SimpleCRM because I've never used SimpleCRM and this project was just thrown at me.

In components/com_gscrm/View/Notes/tmpl/form.form.xml, the "Person or Business" select field is generated from:

<field name="account" class="high28" labelclass="label_left" label_placement="top"
    label="COM_GSCRM_NOTES_ACCOUNT_LABEL" tooltip="COM_GSCRM_NOTES_ACCOUNT_DESC"
    type="Model"
    model="Accounts"
    key_field="gscrm_account_id"
    value_field="title"
    apply_access="true"
    apply_enabled="true"
    none="GS_SELECT"
>
    <state key="code">[ITEM:CODE]</state>
</field>

The "Owner of this contact" field is generated by:

<field name="owner" emptylabel="1" class="high28" labelclass="label_left" label_placement="top" label="COM_GSCRM_NOTES_OWNER_LABEL"
    type="Model"
    model="Beads"
    key_field="gscrm_bead_id"
    value_field="user_name"
    apply_access="true"
    apply_enabled="true"
    none="GS_NOT_AVAILABLE"
>
    <state key="code">[ITEM:CODE]</state>               
</field>

I know that I will need to add a bit of complexity to the query that I actually require and it might not be a single "owner" value that I am returning, but my question is: How do I execute the ajax process using best practices and pre-existing classes/methods if possible?

I had a look at https://docs.joomla.org/J3.x:Developing_an_MVC_Component/Adding_AJAX but that isn't specifically dealing with <field> tags.

This looks close: How to change select list from another using AJAX in Joomla but I am not sure about the best placement of the files and I am not dealing with chozen elements.

Soren's answer indicates that I should be crafting a url like:

index.php?option=com_gscrm&task=ajax.getOwners&format=raw&var=[the_selected_value]

If so, where do I best park my getOwners() method?

Loading the options of a select form field via jQuery Ajax looks similar, but mostly yatta-yattas over the fundamental pieces that I require:

All of the posts have a fair bit of age on them and I expect to see a token used in this process.

4

You can use JSON format in the controller directly by adding .json suffix and changing format in your URL to json. In this case the controller would be placed in /administrator/components/com_example/controllers/ajax.json.php. It's contents:

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Response\JsonResponse;

class ExampleControllerAjax extends JControllerLegacy
{
    public function getOwners()
    {
        // Check for request forgeries.
        $this->checkToken();

        // Get our stuff.
        $db = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select($db->quoteName(['name', 'id']))
            ->from($db->quoteName('#__example'))
            ->where($db->quoteName('id') . ' = ' . $this->input->getInt('exampleId'));
        $result = $db->setQuery($query)->loadObject();

        // Output as JSON
        echo new JsonResponse($result);
    }
}

In the form view's layout:

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;

// Add session token script.
HTMLHelper::_('behavior.core');

// Optional AJAX error message.
Text::script('COM_EXAMPLE_AJAX_ERROR');

AJAX script:

// Optional, define namespace.
Example = window.Example || {};

Example.updateOwners = function(exampleId, field) 
{
    // Get session token.
    var token = Joomla.getOptions('csrf.token');

    // Build a selector for the field we want to update. This example is for the filter form.
    var fieldId = '#filter_' + field;

    // Doing it this way so token can be submitted via POST
    var postData = {'exampleId' : exampleId};
    postData[token] = 1;

    jQuery.ajax(
    {
        url:  'index.php?option=com_example&task=ajax.getOwners&format=json',
        data: postData,
        method: 'POST',
        success: function(response, status, xhr) {

            // Clear existing value.
            jQuery(fieldId).empty();

            // Add new value.
            jQuery('<option />', {value: response.data.id, text: response.data.name}).appendTo(fieldId);

            // Update chosen.
            jQuery(fieldId).trigger("liszt:updated");
        },
        error: function() { Joomla.renderMessages({"warning":[(Joomla.Text._('COM_EXAMPLE_AJAX_ERROR'))]}); },
    });

    return false;
}

In the XML form add this to the field that triggers the event. Where fieldToUpdate is the name of the field that will be updated.

onchange="Example.updateOwners(this.value, 'fieldToUpdate');"

This assumes single row is expected from the database. For multiple values, change loadObject() to loadObjectList() in the controller and run a loop in the script:

jQuery.each(response.data, function(){
    jQuery('<option />', {value: this.id, text: this.name}).appendTo(fieldId);
});

The plugin method. If this is a 3rd party component, we can use a plugin and interact with com_ajax instead. The advantage to this is that original files aren't touched at all. This allows to update the component without losing customizations. Instead of modifying the layout, the form file and adding a controller we just have one plugin:

defined('_JEXEC') or die;

use Joomla\CMS\Form\Form;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Session\Session;

class PlgContentExample extends CMSPlugin
{
    protected $app;

    protected $db;

    public function onContentPrepareForm(Form $form, $data)
    {
        // Check that we're manipulating the correct form.
        if ($form->getName() !== 'com_gscrm.Notes.form.form')
        {
            return true;
        }

        // Run only on HTML document.
        if ($this->app->getDocument()->getType() !== 'html')
        {
            return true;
        }

        // Load form token script.
        HTMLHelper::_('behavior.core');

        // Load AJAX script.
        HTMLHelper::_('script', 'plg_content_example/ajax.js', ['version' => 'auto', 'relative' => true]);

        // Add error message string.
        Text::script('PLG_CONTENT_EXAMPLE_AJAX_ERROR');

        // Add onchange attribute to the field.
        $form->setFieldAttribute('type', 'onchange', 'Example.updateOwners(this.value, \'owner\');');
    }

    public function onAjaxExample()
    {
        if (!Session::checkToken())
        {
            throw new \Exception('JINVALID_TOKEN');
        }

        // Get our stuff.
        $query = $this->db->getQuery(true)
            ->select($this->db->quoteName(['name', 'id']))
            ->from($this->db->quoteName('#__example'))
            ->where($this->db->quoteName('id') . ' = ' . $this->app->input->getInt('exampleId'));
        $result = $this->db->setQuery($query)->loadObject();

        return $result;
    }
}

In the AJAX script we need only two adjusments:

1) Set the request URL to com_ajax:

url:  'index.php?option=com_ajax&plugin=example&group=content&format=json',

2) When accessing response data, use the first array element:

jQuery('<option />', {value: response.data[0].id, text: response.data[0].name}).appendTo(fieldId);

Also in this specific case the form does not have a prefix. So the ID selector is simpler:

var fieldId = '#' + field;
  • And that ajax script, am I putting that in media/com_gscrm/js/getOwners.js or is there a more sensible place? – mickmackusa Mar 18 at 1:36
  • SimpleCRM seems to be doing some weird re-routing thing. Instead of using views/notes, it uses View/Notes (but still has index.html files in the views subdirectories. I don't know where to load my media/com_gscrm/js/getOwners.js file from. – mickmackusa Mar 18 at 2:27
  • @mickmackusa Corrected "your form page" to "form view's layout". Normally that would be components/com_example/views/someview/tmpl/default.php. – Sharky Mar 18 at 7:15
  • I don't see a php file to write into. This component is weird. I can do a screen capture of the directories if that helps. You don't happen to have a copy of your own do you? imgur.com/a/EpMYP73 – mickmackusa Mar 18 at 7:22
  • 1
    As a clean alternative I'm offering a plugin method. See updated answer. – Sharky Mar 19 at 6:39
3

This component is developed using Akeeba FOF, so you need to read the documentation to know how to complete the task.

Almost everything is configured using XML, this is to some extent of course.

To add the onchange javascript code to the dropdown list: https://github.com/akeeba/fof/wiki/XML-Form-Fields#additional-attributes-for-drop-down-list-fields

Also, look at the JSON view to know how to create the json response: https://github.com/akeeba/fof/wiki/The-JSON-view

EDIT

To load a js file in this form view, use the jsfiles attribute of the form xml element, so in the file components/com_gscrm/View/Notes/tmpl/form.form.xml change the form definition to be like this

<form 
    validate="true"
    jsfiles="media://com_gscrm/js/yourfile.js"
    lessfiles="media://com_gs_bootstrap337/css/gs.less||media://com_gs_bootstrap337/css/bootstrap.css"

>

make sure to replace yourfile.js with the correct file name.

For more details, look at this page: https://github.com/akeeba/fof/wiki/XML-Forms

Update

To be complaint with FOF, do the following:

  1. Create a folder in the admin part, name it Controller
  2. Create a PHP file inside it, name it Ajax.php
  3. The file contents should look like this:
<?php
/**
 * 
 */

namespace Gs\Gscrm\Admin\Controller;
use FOF30\Controller\Controller;

defined('_JEXEC') or die;

class Ajax extends Controller
{
  function getOwners()
  {
      // Token check
      $this->csrfProtection();
        /* insert your code to get owners in json format*/
      $this->container->platform->closeApplication();
  }
}
  1. Then the ajax call should look like this:
$.get(
              'index.php',
              {
                  'option':     'com_gscrm',
                  'view':       'Ajax',
                  'format':     'json',
                  'task':       'getOwners',
                  '<?php echo $this->container->platform->getToken(true) ?>':   1,
                  'id':    id
              },
              function (data, textStatus)
              {
                  // insert here the code to populate the owners list
              }           )
  • Sharky's advice seems to be a virtually complete solution for my question, but I am stuck trying to figure out how/where to load the ajax script that onchange() will point to. I don't expect to have any difficulties with the other aspects of this task. – mickmackusa Mar 18 at 10:13
  • In this kind of projects, js files are installed in the media/com_gscrm/js folder, so try to look for an existing js file that is called in this form view, and load the ajax script there. – Mohamed Abdelaziz Mar 18 at 10:28
  • Don't worry, I will try to help after coming back from the break, till then I recommend that you download the akeeba subs or release system extensions and have a look into the code, this may help to discover a solution – Mohamed Abdelaziz Mar 18 at 11:15
  • @mickmackusa I have added an example to load a js file in the form view, let me know if this works. – Mohamed Abdelaziz Mar 18 at 17:02
  • 1
    @mickmackusa I updated the answer with a blueprint on how the Ajax/json call is going in FOF, inspired from the Akeeba Release System component. – Mohamed Abdelaziz Mar 19 at 8:39
0

If I understand what you are trying to accomplish the easy way to do this would be with a bit of jQuery and an ajax call. Add a method to one of your controller files that returns the value of the owner field. Post the id of the person/business filed to that controller using ajax.Return the id of the owner field. Use jQuery to set the option to the appropriate value.

  • Sorry this is too vague for what I am after. Yes, that is generally the action that I wish to perform. My question is about how to construct the actual scripts and in which directories in accordance with best MVC and security practices. – mickmackusa Mar 15 at 4:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.