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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10118868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 10:44:57  来源:igfitidea点击:

How to use Ajax within Sonata Admin forms?

ajaxsymfonysymfony-sonatasonata-admin

提问by Amit

I have a Merchant entity with the following fields and associations:-

我有一个具有以下字段和关联的商家实体:-

/**
 * @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;

The Tags and Categories also have a ManyToMany mapping. So we have Tag_Category, Merchant_Tag, Merchant_Category mapping tables.

标签和类别也有一个多对多映射。所以我们有 Tag_Category、Merchant_Tag、Merchant_Category 映射表。

Now I want to perform some ajax on these fields.

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

I want to allow the user to select the Primary Tag first. On the basis of the Primary Tag, ajax refresh the categories to only those which belong to this Tag and some more operations.

我想让用户先选择主标签。ajax在Primary Tag的基础上,将category刷新为只属于这个Tag的类别,并进行一些操作。

How can I achieve this?

我怎样才能做到这一点?

Thanks!

谢谢!

回答by Amit

I was able to make this work a few months back. While what a.aitboudad has shared is accurate. There are a few gotcha's that first timers with Symfony/Sonata might face.

几个月前我能够完成这项工作。而 a.aitboudad 分享的内容是准确的。第一次接触 Symfony/Sonata 的人可能会遇到一些问题。

Here are the steps.

以下是步骤。

1> Extend Sonata CRUD's edit.html.twig/ base_edit.html.twig.For simplicity, I'll use only the latter. Copy vendor/bundles/Sonata/AdminBundle/Resources/views/CRUD/base_edit.html.twiginto the views folder corresponding to the MerchantAdminController - YourBundle/Resources/views/Merchant/base_edit.html.twig

1> 扩展 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> We need to tell our MerchantAdmin class to use this template.So we override SonataAdmin's getEditTemplatemethod like this:

2> 我们需要告诉我们的 MerchantAdmin 类使用这个模板。所以我们getEditTemplate像这样覆盖 SonataAdmin 的方法:

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

3>Next we need to code the Ajax functionalityin our base_edit.html.twig. Standard Ajax comprises of the following:

3>接下来,我们需要编写Ajax功能在我们的base_edit.html.twig。标准 Ajax 包括以下内容:

3.1>-- Create an Action in the controller for the Ajax request We primarily want to get a list of category IDs corresponding to a particular tag. But most likely you are just using Sonata's CRUD Controller.

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

Define your MerchantAdminController which extends CRUDController

定义扩展 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>-- Tell your Admin service to use this newly created controller instead of the default CRUDController by defining it in YourBundle/Resources/config/services.yml

3.2>-- 告诉你的管理服务使用这个新创建的控制器而不是默认的 CRUDController 通过定义它YourBundle/Resources/config/services.yml

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]

Notice that the 3rd argument is the name of your controller. By default it would have been null.

请注意,第三个参数是您的控制器的名称。默认情况下它会是空的。

3.3>-- Create an Action named getCategoryOptionsFromTagActionin your controller. Your Ajax call will be to this Action.

3.3>-- 创建一个getCategoryOptionsFromTagAction在你的控制器中命名的动作。您的 Ajax 调用将针对此操作。

// 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>-- Create the corresponding route in app/config/routing.yml. Remember to expose your route if you are using the FOSJsRoutingBundle (else you'll have to hardcode which is not a good idea).

3.4>-- 在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>-- Make the Ajax Request and use the response

3.5>-- 发出 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 %}

Gotcha 1:How to get the Unique ID that is appended to all Sonata elements

问题 1:如何获取附加到所有奏鸣曲元素的唯一 ID

Solution:Use the admin variable which will give you access to all the Admin Class's properties including uniqId. See code on how to use it.

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

Gotcha 2:How to get the Router in your JS.

问题 2:如何在 JS 中获取路由器。

Solution:By default Symfony2 Routing doesn't work in JS. You need to use a bundle called FOSJSRouting (explained above) and expose the route. This will give you access to the Router object within your JS too.

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

I have modified my solution slightly to make this example clearer. If you notice anything wrong, please feel free to comment.

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

回答by Paul Prijs

At step 1 of Amit and Lumbendil answer you should change

在 Amit 和 Lumbendil 回答的第 1 步中,您应该更改

{% extends base_template %}

into

进入

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

if you get an error like

如果你得到这样的错误

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

回答by guillermogfer

Very detailed post, just to update the way of override and use the edittemplate in the Admin class.
Now, you should do it this way:

很详细的帖子,只是为了更新覆盖的方式和使用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);
    }
}

Or inject it in the service definition used the provided method, to keep the Admin class as cleaner as possible:

或者将其注入使用提供的方法的服务定义中,以保持 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]]

回答by Hibatallah Aouadni

in the block javascripts, you have to change "liszt:updated"to "chosen:updated"

在块 javascripts 中,您必须更改"liszt:updated""chosen:updated"

hope it helps someone ;)

希望它可以帮助某人;)