我有3个实体(国家、地区、城市)
namespace ********BundleEntity;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
class Country
{
private $id;
private $name;
/**
* @var integer $regions
*
* @ORMOneToMany(targetEntity="Region", mappedBy="Country")
*/
protected $regions;
//...
}
class Region
{
private $id;
private $name;
/**
* @var integer $country
*
* @AssertType(type="*******BundleEntityCountry")
* @ORMManyToOne(targetEntity="Country", inversedBy="regions")
* @ORMJoinColumn(name="country_id", referencedColumnName="id", nullable=false)
*/
private $country;
/**
* @ORMOneToMany(targetEntity="City", mappedBy="Region")
*/
protected $cities;
}
class City
{
private $id;
private $name;
/**
* @var integer $region
*
* @AssertType(type="********BundleEntityRegion")
* @ORMManyToOne(targetEntity="Region", inversedBy="cities")
* @ORMJoinColumn(name="region_id", referencedColumnName="id", nullable=false)
*/
private $region;
/**
* @ORMOneToMany(targetEntity="Company", mappedBy="City")
*/
protected $companys;
//...
}
这是我的城市课程:
namespace ********BundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
class CityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('region');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => '********BundleEntityCity',
));
}
public function getName()
{
return 'city';
}
}
这就形成了一个基本的HTML5表单,其中有一个用于名称的textBox和一个所有区域都可用的SelectBox。
我的问题是,添加第一个SelectBox的最佳方式是什么,它可以让我选择国家,以过滤第二个SelectBox并减少地区选择的数量?
EventListener?事件调度程序组件?
否,EventListener和Event dispatcher用于在服务器上发生的事件,而不是在客户端。您需要使用Javascript。当其中一个选择框发生变化时,这应该触发一个javascript函数,并进行AJAX调用并用该调用的结果填充另一个选择盒,或者使用一些javascript代码来选择在第二个框上显示哪些选项。
在这里查找一些想法
正如Carlos Granados所说,您基本上有两种选择:
-
创建一个单独的Symfony操作,该操作以国家为参数,并以XML或JSON格式返回相关区域的列表。(您可以使用
SymfonyComponentHttpFoundationJsonResponse
发送JSON响应,但是没有相应的XmlResponse
类)。每当用户更改当前选择的项目时,使用jQuery(或任何其他JS库,甚至是纯Javascript)向服务器发送请求。在回调中(当您在Javascript中检索到响应时),您可以更新区域选择框。您可能会发现jQueryAjax的文档很有趣。 -
您可以在HTML代码中存储所有国家及其相关地区的列表(您可以使用JSON或生成本地Javascript数组),每当用户更改国家选择框的值时,您只需替换地区选择框中的地区列表。
第二种方法对表单的初始加载有更大的负担,因为所有国家及其相关地区都必须加载(从数据库或文本文件或存储它们的任何地方),并以JS可以轻松读取的格式呈现。
然而,第一种方法必须在每次用户选择另一个国家时发送请求。此外,您还必须执行另一个操作。
我自己在表单上做这件事。我更改了一个字段(一个产品),可以测量数量的单位也会更新。我正在使用一个带有参数的宏来更容易地调整它。
宏:
{% macro javascript_filter_unit(event, selector) %}
<script>
$(function(){
$('#usersection')
.on('{{ event }}', '{{ selector }}', function(e){
e.preventDefault();
if (!$(this).val()) return;
$.ajax({
$parent: $(this).closest('.child_collection'),
url: $(this).attr('data-url'),
type: "get",
dataType: "json",
data: {'id' : $(this).val(), 'repo': $(this).attr('data-repo'), parameter: $(this).attr('data-parameter')},
success: function (result) {
if (result['success'])
{
var units = result['units'];
this.$parent.find('.unit').eq(0).html(units);
}
}
});
})
});
</script>
{% endmacro %}
ajax返回一个数组:array('success'=>$value,'units'=>$html)。您使用$html代码并将其放在要更改的选择的位置。当然,ajax调用的javascript代码需要进行修改以匹配您的字段。
你可以像往常一样调用宏:
{% import ':Model/Macros:_macros.html.twig' as macros %}
{{ macros.javascript_filter_unit('change', '.unitTrigger') }}
所以我有两个论点:事件,通常是一个选择的改变。以及一个选择器,其更改触发ajax调用。
我希望这能有所帮助。
正如Carlos Granados所说,您必须使用客户端编程:Javascript。我也遇到了和你相同的问题,并提出了一个非常适合我的解决方案,这是代码笔上的片段,你可以从(Cascade Ajax Selects)中获得灵感
//-------------------------------SELECT CASCADING-------------------------//
var currentCities=[];
// This is a demo API key that can only be used for a short period of time, and will be unavailable soon. You should rather request your API key (free) from http://battuta.medunes.net/
var BATTUTA_KEY="00000000000000000000000000000000"
// Populate country select box from battuta API
url="http://battuta.medunes.net/api/country/all/?key="+BATTUTA_KEY+"&callback=?";
$.getJSON(url,function(countries)
{
console.log(countries);
$('#country').material_select();
//loop through countries..
$.each(countries,function(key,country)
{
$("<option></option>")
.attr("value",country.code)
.append(country.name)
.appendTo($("#country"));
});
// trigger "change" to fire the #state section update process
$("#country").material_select('update');
$("#country").trigger("change");
});
$("#country").on("change",function()
{
countryCode=$("#country").val();
// Populate country select box from battuta API
url="http://battuta.medunes.net/api/region/"
+countryCode
+"/all/?key="+BATTUTA_KEY+"&callback=?";
$.getJSON(url,function(regions)
{
$("#region option").remove();
//loop through regions..
$.each(regions,function(key,region)
{
$("<option></option>")
.attr("value",region.region)
.append(region.region)
.appendTo($("#region"));
});
// trigger "change" to fire the #state section update process
$("#region").material_select('update');
$("#region").trigger("change");
});
});
$("#region").on("change",function()
{
// Populate country select box from battuta API
countryCode=$("#country").val();
region=$("#region").val();
url="http://battuta.medunes.net/api/city/"
+countryCode
+"/search/?region="
+region
+"&key="
+BATTUTA_KEY
+"&callback=?";
$.getJSON(url,function(cities)
{
currentCities=cities;
var i=0;
$("#city option").remove();
//loop through regions..
$.each(cities,function(key,city)
{
$("<option></option>")
.attr("value",i++)
.append(city.city)
.appendTo($("#city"));
});
// trigger "change" to fire the #state section update process
$("#city").material_select('update');
$("#city").trigger("change");
});
});
$("#city").on("change",function()
{
currentIndex=$("#city").val();
currentCity=currentCities[currentIndex];
city=currentCity.city;
region=currentCity.region;
country=currentCity.country;
lat=currentCity.latitude;
lng=currentCity.longitude;
$("#location").html('<i class="fa fa-map-marker"></i> <strong> '+city+"/"+region+"</strong>("+lat+","+lng+")");
});
//-------------------------------END OF SELECT CASCADING-------------------------//