Magento 1.8 – How to Structure Ajax Module/Widget

ajaxmagento-1.8module

I'm fairly new to Magento and have never attempted anything like this before, I'm still getting used to how Magento does things.

I'm trying to create a widget which will add a 'Spraydeck finder' to any CMS page. For those not sure what I mean, it's exactly the same principal as a car finder – just think of two drop-downs where you first pick a brand of car (or in this case Kayak) – this action populates a second drop-down with available Models from that manufacturer, you select your model and this action displays available products or information about a specific vehicle (or Kayak in my case).

At the moment, the widget is displaying and I have the first dropdown populated with brands of kayak, but as you'll see when you scroll down, the second dropdown is currently dealt with completely in the controller, I'm 99% sure this is not 'the Magento way' to go about this, should I be putting all this into the original block or should I use the controller to load a new block and output that via an Ajax.Updater call? – obviously once the model is selected I need to go through this process again to display the specific information for that Kayak.

Can someone offer some pointers or maybe point me to a tutorial better suited to answer this? i've had a lot of trouble finding good literature on Ajax implementation in Magento.

In my block I have the following:

class Dsingleton_Spraydecks_Block_Finder
    extends Mage_Core_Block_Template
    implements Mage_Widget_Block_Interface {
    /**
     * A model to serialize attributes
     * @var Varien_Object
     */
    protected $_serializer = null;

    /**
     * Initialization
     */
    protected function _construct()
    {
        $this->_serializer = new Varien_Object();
        parent::_construct();
    }

    /**
     * Produce links list rendered as html
     *
     * @return string
     */
    protected function _toHtml()
    {
        $html = '';
        $list = $this->getBrands();
        $this->assign('list', $list);

        return parent::_toHtml();
    }

    private function getBrands()
    {
        $brands_collection = Mage::getModel('dsingleton_spraydecks/finder')
            ->getCollection()
            ->distinct(true)
            ->addFieldToSelect('brand')
            ->load();

        $brand_list = array();
        $brand_list[] = '-- Please Select --';

        foreach ( $brands_collection as $brand )
        {
            $brand_list[] = $brand->brand;
        }

        return $brand_list;

    } }

In my template template file I have the following:

<label for="finder">Brand: </label>
<select name="finder" id="sd-brand-select">
    <?php foreach ($list as $item) : ?>
        <option value="<?php echo $item ?>"><?php echo $item ?></option>
    <?php endforeach; ?>
</select>
<div id="sd-model"></div>
<script type="text/javascript">

    document.observe("dom:loaded", function() { 
        $$('#sd-brand-select').invoke('on', 'change', function(){

            var brand = this.getValue();
            if (brand != '-- Please Select --')
            {
                new Ajax.Updater('sd-model', '<?php echo Mage::getBaseUrl() ?>' + 'spraydecks/ajax/model/brand/' + brand, { method: 'post' });  
            }
        }); 
    });

</script>

And in my Controller I have the following:

class Dsingleton_Spraydecks_AjaxController extends Mage_Core_Controller_Front_Action {

    /**
     * Return the brands for a given manufacturer
     */    
    public function modelAction()
    {
        //check we have an ajax request
        $isAjax = Mage::app()->getRequest()->isAjax();

        if ($isAjax) {

            $this->loadLayout(false);
            $this->renderLayout();

            $brand = $this->getRequest()->getParam('brand');

            if($brand)
            {
                $boat_model_collection = Mage::getModel('dsingleton_spraydecks/finder')
                    ->getCollection()
                    ->addFieldToSelect('model')
                    ->addFieldToFilter('brand', $brand)
                    ->load();

                $model_list = array();
                $model_list[] = '-- Please Select --';

                foreach ( $boat_model_collection as $boat_model )
                {
                    $model_list[] = $boat_model->model;
                }

                $dropdown = '<option value="%s">%s</option>';

                echo '<label for="boat">Model: </label>';
                echo '<select name="finder" id="sd-brand-select">';

                 foreach ($model_list as $item){
                     echo sprintf($dropdown, $item, $item);
                 }

                echo '</select>';

            }
        }
    }
}

Best Answer

Your approach it's one hundred percent the Magento way. Although i didn't liked that you are echoing HTML in your controller.

Put all of your HTML elements in your block .phtml file. Export JSON in your controller and then in your Javascript do:

document.observe("dom:loaded", function() { 
  $$("#sd-brand-select").invoke("on", "change", function(){

    var brand = this.getValue();
    if (brand != "-- Please Select --")
    {

       new Ajax.Request("<?php echo Mage::getBaseUrl() ?>" + "spraydecks/ajax/model/brand/" + brand, {
         method:"get",
         onSuccess: function(transport) {
           /* populate second select box with the results */
         },
         onFailure: function() { alert("Something went wrong..."); }
      });
  }); 
});
Related Topic