如何在Sonata Admin表单中使用Ajax?

时间:2012-04-12 06:57:53

标签: ajax symfony symfony-sonata sonata-admin

我有一个商家实体,其中包含以下字段和关联: -

/**
 * @ORM\ManyToMany(targetEntity="Category", inversedBy="merchants")
 */
public $categories;

/**
 * @ORM\ManyToMany(targetEntity="Tag", inversedBy="merchants")
 */
public $tags;

/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="merchants")
 */
protected $primaryCategory;

/**
 * @ORM\ManyToOne(targetEntity="Tag", inversedBy="merchants")
 */
protected $primaryTag;

标签和类别也有ManyToMany映射。 所以我们有Tag_Category,Merchant_Tag,Merchant_Category映射表。

现在我想在这些字段上执行一些ajax。

我想让用户先选择主要标签。在主标记的基础上,ajax将类别刷新为仅属于此标记的类别以及更多操作。

我怎样才能做到这一点?

谢谢!

4 个答案:

答案 0 :(得分:58)

几个月前我就能完成这项工作。虽然a.aitboudad分享的内容是准确的。 Symfony / Sonata的第一次定时器可能会面临一些问题。

以下是步骤。

<强> 1&GT;扩展Sonata CRUD的edit.html.twig / base_edit.html.twig。 为简单起见,我只使用后者。 将vendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twig复制到与MerchantAdminController相对应的views文件夹中 - YourBundle/Resources/views/Merchant/base_edit.html.twig

<强> 2 - ;我们需要告诉MerchantAdmin类使用此模板。因此我们覆盖SonataAdmin的getEditTemplate方法,如下所示:

public function getEditTemplate()
{
    return 'YourBundle:Merchant:base_edit.html.twig';
}

3&gt; 接下来,我们需要在base_edit.html.twig对Ajax功能进行编码。标准Ajax包含以下内容:

3.1&gt; - 在控制器中为Ajax请求创建一个Action 我们主要想获得与特定标签相对应的类别ID列表。但很可能你只是使用Sonata的CRUD控制器。

定义扩展CRUDController的MerchantAdminController

<?php

namespace GD\AdminBundle\Controller;

use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use GD\AdminBundle\Entity\Merchant;

class MerchantAdminController extends Controller
{

}

3.2&gt; - 通过YourBundle/Resources/config/services.yml

中的定义,告诉管理员服务使用这个新创建的控制器而不是默认的CRUDController
gd_admin.merchant:
        class: %gd_admin.merchant.class%
        tags:
            - { name: sonata.admin, manager_type: orm, group: gd_merchant, label: Merchants }
        arguments: [null, GD\AdminBundle\Entity\Merchant, GDAdminBundle:MerchantAdmin]

请注意,第3个参数是控制器的名称。默认情况下,它将为null。

3.3&gt; - 在控制器中创建名为getCategoryOptionsFromTagAction的操作。您的Ajax调用将是此Action。

// route - get_categories_from_tag
public function getCategoryOptionsFromTagAction($tagId)
    {   
        $html = ""; // HTML as response
        $tag = $this->getDoctrine()
            ->getRepository('YourBundle:Tag')
            ->find($tagId);

        $categories = $tag->getCategories();

        foreach($categories as $cat){
            $html .= '<option value="'.$cat->getId().'" >'.$cat->getName().'</option>';
        }

        return new Response($html, 200);
    }

3.4&gt; - 在app/config/routing.yml中创建相应的路线。如果你正在使用FOSJsRoutingBundle,请记住暴露你的路线(否则你必须硬编码,这不是一个好主意)。

get_categories_from_tag:
    pattern: /{_locale}/admin/gd/admin/merchant/get-categories-from-tag/{tagId}
    defaults: {_controller: GDAdminBundle:MerchantAdmin:getCategoryOptionsFromTag}
    options:
        expose: true

3.5&gt; - 发出Ajax请求并使用响应

{% block javascripts %}
    {{ parent() }}
    <script type="text/javascript">

        $(document).ready(function(){
            var primaryTag = $("#{{ admin.uniqId }}_primaryTag");
            primaryTag.change(updateCategories()); // Bind the function to updateCategories
            primaryTag.change(); // Manual trigger to update categories in Document load.

            function updateCategories(){
                return function () {
                    var tagId = $("#{{ admin.uniqId }}_primaryTag option:selected").val();
                    var primaryCategory = $("#{{ admin.uniqId }}_primaryCategory");
                    primaryCategory.empty();
                    primaryCategory.trigger("liszt:updated");
                    var locale = '{{ app.request.get('_locale') }}';

                    var objectId = '{{ admin.id(object) }}'

                    var url = Routing.generate('get_categories_from_tag', { '_locale': locale, 'tagId': tagId, _sonata_admin: 'gd_admin.merchant', id: objectId });
                    $.post(url, { tagId: tagId }, function(data){
                        primaryCategory.empty().append(data);
                        primaryCategory.trigger("liszt:updated");
                    },"text");

                    primaryCategory.val("option:first").attr("selected", true);
                };
            }
        });
    </script>
{% endblock %}

得知1: 如何获取附加到所有Sonata元素的唯一ID

解决方案: 使用admin变量可以访问所有Admin Class的属性,包括uniqId。请参阅有关如何使用它的代码。

得知2: 如何在JS中获取路由器。

解决方案: 默认情况下,Symfony2路由在JS中不起作用。您需要使用名为FOSJSRouting的包(如上所述)并公开路径。这样您也可以访问JS中的Router对象。

我稍微修改了我的解决方案,使这个例子更加清晰。如果您发现任何错误,请随时发表评论。

答案 1 :(得分:4)

在Amit和Lumbendil的第1步回答你应该改变

{% extends base_template %}

进入

{% extends 'SonataAdminBundle::standard_layout.html.twig' %}

如果您收到类似

的错误
Unable to find template "" in YourBundle:YourObject:base_edit.html.twig at line 34.  

答案 2 :(得分:4)

非常详细的帖子,只是为了更新覆盖方式并使用Admin类中的编辑模板。
现在,你应该这样做:

// src/AppBundle/Admin/EntityAdmin.php  

class EntityAdmin extends Admin
{  
    public function getTemplate($name)
    {
        if ( $name == "edit" ) 
        {
            // template 'base_edit.html.twig' placed in app/Resources/views/Entity
            return 'Entity/base_edit.html.twig' ;
        }
        return parent::getTemplate($name);
    }
}

或者将其注入使用provided method的服务定义中,以使Admin类尽可能清洁:

// app/config/services.yml  

app.admin.entity:
    class: AppBundle\Admin\EntityAdmin
    arguments: [~, AppBundle\Entity\Entity, ~]
    tags:
        - {name: sonata.admin, manager_type: orm, group: "Group", label: "Label"}
    calls:
        - [ setTemplate, [edit, Entity/base_edit.html.twig]]

答案 3 :(得分:2)

在块javascripts中

,您必须将"liszt:updated"更改为"chosen:updated"

希望它可以帮助某人;)