在我的symfony应用程序中,我以表格的形式呈现了许多视图。
我希望通过点击表格单元格,填充新数据并动态保存来编辑这些数据。
我已经使用jEditable探索了jqGrid,DataTable,但没有提供与jquery集成的简单方法。
我最终使用一个表单来构建我自己的视图,该表单在单击表格行时替换了tr行但是超级自定义,现在我需要对其他元素使用相同的行为,我感到有些气馁。
是否有Symfony最佳实践?那是工作还是Angular或React?我该怎么办?
我的梦想解决方案是知道正在编辑哪个实体字段,编辑它并在返回结果之前验证实体。
我的自定义代码:
{% extends ':Template/Backend:backend.html.twig' %}
{% block title_wrapper %}
<div class="row wrapper border-bottom white-bg page-heading">
<div class="col-md-12 col-sm-12">
<h2 class="inline-block">Coûts d'achat</h2>
<span id="header_buttons" class="pull-right">
<a href="{{ path('user_supplier_select') }}" class="btn btn-sm btn-white">Ajouter un fournisseur</a>
</span>
</div>
</div>
{% endblock %}
{% block main %}
<p>Assignez des fournisseurs à vos ingrédients. Vous pourrez ensuite paramétrer le coût d'achat.</p>
<p>L'unité utilisée pour la quantité minimale d'achat est l'unité que vous utilisez pour gérer votre stock. Essayez d'adopter la même que votre fournisseur.</p>
{{ form_start(parameter_cost_form, {'attr' : {'id' : 'parameter_cost_form'}}) }}
{{ form_errors(parameter_cost_form) }}
{{ form_stylesheet(parameter_cost_form) }}
{{ form_javascript(parameter_cost_form) }}
<br>
<br>
<div class="table-responsive">
<table class="DataTable table table-striped table-bordered table-hover" id="editable" >
<colgroup>
<col span="3">
<col span="1" style="min-width: 80px !important">
</colgroup>
<thead>
<tr>
<th>Ingrédient</th>
<th>Marque</th>
<th title="Quantité disponible dans un produit" data-toggle="tooltip" data-placement="top">Packaging</th>
<th title="Quantité payée qui divisera le prix pour obtenir le coût par unité" data-toggle="tooltip" data-placement="top">Quantité</th>
<th title="Unité utilisée pour calculer le coût de revient" data-toggle="tooltip" data-placement="top">Unité</th>
<th title="Montant payé pour la quantité donnée dans l'unité donnée" data-toggle="tooltip" data-placement="top">Montant HT (€)</th>
<th title="Quantité minimale d'achat en nombre de produits - permet d'arrondir les commandes à la quantité minimale servie par votre fournisseur" data-toggle="tooltip" data-placement="top">Q d'achat minimale</th>
<th>Fournisseur</th>
<th title="Référence qui sera indiquée dans les bons de commande" data-toggle="tooltip" data-placement="top">Référence fournisseur</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr id="edit_form" style="display: none;" class="child_collection">
{# Data-repo and data-id are used for updating the unit fields #}
<td class="name" data-repo="AppBundle:FoodAnalytics\UserIngredient"></td>
<td class="brand"></td>
<td class="packaging"></td>
<td class="quantity">
{{ form_widget(parameter_cost_form.quantity) }}
{{ form_errors(parameter_cost_form.quantity) }}
</td>
<td class="unit-control">
{{ form_widget(parameter_cost_form.unit) }}
{{ form_errors(parameter_cost_form.unit) }}
</td>
<td class="numberObjects">
{{ form_widget(parameter_cost_form.numberObjects) }}
{{ form_errors(parameter_cost_form.numberObjects) }}
</td>
<td class="minimumBuyingQuantity">
{{form_widget(parameter_cost_form.minimumBuyingQuantity) }}
{{form_errors(parameter_cost_form.minimumBuyingQuantity) }}
</td>
<td class="userSupplier">
{{ form_widget(parameter_cost_form.userSupplier) }}
{{ form_errors(parameter_cost_form.userSupplier) }}
</td>
<td class="supplierReference">
{{ form_widget(parameter_cost_form.supplierReference) }}
{{ form_errors(parameter_cost_form.supplierReference) }}
</td>
<td class="action buttons">
{{ form_widget(parameter_cost_form.submit) }}
</td>
</tr>
{% set i = 0 %}
{% for ingredient in user_ingredients %}
{% for uis in ingredient.userIngredientSuppliers %}
{% set i = i + 1 %}
<tr id="ingredient_{{ i }}" data-userIngredientId="{{ ingredient.id }}" data-userIngredientSupplierId="{{ uis.id }}" class="table_row">
{# Data-repo and data-id are used for updating the unit fields #}
<td class="name" data-id="{{ ingredient.id }}">{{ ingredient.getProductName }}</td>
<td class="brand">{{ ingredient.getProductBrand }}</td>
<td class="packaging">{{ ingredient.getProductPackaging }}</td>
<td class="quantity">{{ uis.quantity }}</td>
<td class="unit-control" data-value="{{ uis.unit.id }}">{{ uis.unit }}</td>
<td class="numberObjects">{{ uis.getLastNumberObjectValue }}</td>
<td class="minimumBuyingQuantity">{{ uis.minimumBuyingQuantity }}</td>
<td class="userSupplier" data-value="{{ uis.userSupplier.id }}">{{ uis.userSupplier }}</td>
<td class="supplierReference">{{ uis.supplierReference }}</td>
<td class="action buttons">
<a href="{{ path('user_ingredient_supplier_delete', {'userIngredientSupplier' : uis.id }) }}" class="btn btn-xs btn-white fmu_delete_button"><i class="fa fa-times"></i></a>
{#<a href="#/" class="btn btn-xs btn-white add"><i class="fa fa-plus"></i></a>#}
</td>
</tr>
{% else %}
{% set i = i + 1 %}
<tr id="ingredient_{{ i }}" data-userIngredientId="{{ ingredient.id }}" data-userIngredientSupplierId="" class="table_row empty">
{# Data-repo and data-id are used for updating the unit fields #}
<td class="name" data-id="{{ ingredient.id }}">{{ ingredient.getProductName }}</td>
<td class="brand">{{ ingredient.getProductBrand }}</td>
<td class="packaging">{{ ingredient.getProductPackaging }}</td>
<td class="quantity"></td>
<td class="unit-control"></td>
<td class="numberObjects"></td>
<td class="minimumBuyingQuantity"></td>
<td class="userSupplier"></td>
<td class="supplierReference"></td>
<td class="action buttons">
<a href="" class="btn btn-xs btn-white fmu_delete_button" style="display: none;"><i class="fa fa-times"></i></a>
{#<a href="#/" class="btn btn-xs btn-white add"><i class="fa fa-plus"></i></a>#}
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<th>Ingrédient</th>
<th>Marque</th>
<th>Packaging</th>
<th>Quantité</th>
<th>Unité</th>
<th>Montant HT (€)</th>
<th>Q d'achat minimale</th>
<th>Fournisseur</th>
<th>Référence fournisseur</th>
<th>Actions</th>
</tr>
</tfoot>
</table>
</div>
{{ form_end(parameter_cost_form) }}
{% import ':Model/Macros:_macros.html.twig' as macros %}
{{ macros.jqueryui_dialog('.fmu_delete_button', "Confirmation", 'Etes-vous sur de vouloir supprimer les informations de ce produit ?', 'Oui, supprimer', 'Non, annuler') }}
<script>
$(function(){
var $editForm = $('#edit_form');
var $parameterCostForm = $('#parameter_cost_form');
$('#editable').on('click', 'td:not(:last-child)', function(){
var $parent = $(this).parent();
if ($parent.attr('id') == 'edit_form') {
return;
}
$($editForm.data('previous')).show();
$editForm.insertAfter($parent);
$parent.hide();
$editForm.data('previous', '#' + $parent.attr('id'));
$parameterCostForm.data('name', $parent.find('.name').eq(0).html());
$editForm.find('.help-block').remove();
$editForm.find('.form-control').attr('style', 'position:absolute !importante;');
$editForm.show();
$parameterCostForm.attr('action', '{{ path('parameter_cost') }}/' + $parent.attr('data-userIngredientId') + ($parent.attr('data-userIngredientSupplierId') ? '/' + $parent.attr('data-userIngredientSupplierId') : ''));
$editForm.find('.name').eq(0).attr('data-id', $parent.find('.name').eq(0).attr('data-id'));
$editForm.find('.name').eq(0).html($parent.find('.name').eq(0).html());
$editForm.find('.brand').eq(0).html($parent.find('.brand').eq(0).html());
$editForm.find('.packaging').eq(0).html($parent.find('.packaging').eq(0).html());
$editForm.find('.quantity').eq(0).find('.form-control').val($parent.find('.quantity').eq(0).html());
$editForm.find('.unit-control').eq(0).find('.form-control').val($parent.find('.unit-control').eq(0).attr('data-value'));
$editForm.find('.numberObjects').eq(0).find('.form-control').val($parent.find('.numberObjects').eq(0).html());
$editForm.find('.minimumBuyingQuantity').eq(0).find('.form-control').val($parent.find('.minimumBuyingQuantity').eq(0).html());
$editForm.find('.userSupplier').eq(0).find('.form-control').val($parent.find('.userSupplier').eq(0).attr('data-value'));
$editForm.find('.supplierReference').eq(0).find('.form-control').val($parent.find('.supplierReference').eq(0).html());
$editForm.find('.name').eq(0).trigger('initiated');
});
{# Data-repo and data-id are used for updating the unit fields #}
var updateRow = function(){
$editForm.find('.form-control').each(function(){
var $previous = $($editForm.data('previous'));
var thisClass = $(this).closest('td').attr('class');
var $eq = $previous.find('.' + thisClass).eq(0);
$previous.addClass('edited');
if (thisClass == 'unit-control' || thisClass == 'userSupplier') {
$eq.html($(this).find('option:selected').text());
$eq.attr('data-value', $(this).val())
} else {
$eq.html($(this).val());
}
})
};
$editForm.on('change', '.form-control', updateRow);
$('body').on('click', '#parameter_cost_form button[type="submit"]', function(e){
e.preventDefault();
$(this).attr('disabled', 'disabled');
var formData = new FormData($parameterCostForm[0]);
console.log($parameterCostForm.attr('action'));
$editForm.data('sent', $editForm.data('previous'));
$.ajax({
type : 'post',
url : $parameterCostForm.attr('action'),
data : formData,
contentType: false,
cache: false,
processData: false
}).done(function(response){
response = JSON.parse(response);
if ($editForm.data('previous') == $editForm.data('sent')) {
$editForm.find('td:nth-child(n+4)').remove();
var $substitutes = $(response.view).find('td');
$editForm.append($substitutes);
updateRow();
if (response.success) {
var $previous = $($editForm.data('previous'));
if ($previous) {
$previous.attr('data-userIngredientSupplierId', response.id).removeClass('edited');
$parameterCostForm.attr('action', response.action);
console.log(response.action);
var $action = $previous.find('.action a').eq(0);
if ($action.attr('href') == false) {
$action.attr('href', '{{ path('user_ingredient_supplier_delete') }}' + '/' + response.id);
$action.show();
}
}
$.fn.logMessage('success', "L'ingrédient '" + $parameterCostForm.data('name') + "' a été enregistré.");
} else {
$.fn.logMessage('error', "L'ingrédient '" + $parameterCostForm.data('name') + "' n'a pas pu être enregistré. Vérifiez la validité des données renseignées.");
$editForm.find('.form-control').attr('style', 'position: relative !important;');
}
} else {
var $sent = $($editForm.data('sent'));
$('#parameter_cost_form button[type="submit"]').removeAttr('disabled');
if ($sent) {
if (response.success) {
$sent.removeClass('edited');
$.fn.logMessage('success', "L'ingrédient '" + $sent.find('.name').eq(0).html() + "' a été enregistré.");
} else {
$.fn.logMessage('error', "L'ingrédient '" + $sent.find('.name').eq(0).html() + "' n'a pas pu être enregistré. Vérifiez la validité des données renseignées.");
}
}
}
}).fail(function(){
$.fn.logMessage('error', "L'ingrédient '" + $parameterCostForm.data('name') + "' n'a pas pu être enregistré. Vérifiez la validité des données renseignées.");
});
})
});
</script>
{{ macros.javascript_json_prefill('initiated', '#edit_form .name') }}
<style rel="stylesheet">
.unit-control {min-width: 40px;}
#edit_form td:nth-child(n+4) {
padding: 0;
position: relative;
}
#edit_form .form-control, #edit_form .btn {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100%;
width:100%;
border: 0;
}
.edited {
background-color: #fffec9 !important;
}
</style>
{% endblock %}
答案 0 :(得分:2)
几个月前,我有完全相同的要求。我已成功将free jqGrid与Symfony 2.7
整合。
为此,我使用了一个名为thrace datagrid bundle的Symfony包。捆绑包可能有点棘手,但一旦想到它就会让事情变得非常简单。您提到的其他插件在性能(浏览器崩溃等)方面随着行数的增加而恶化。您可以修改用于修改jqGrid的树枝,它将按照您希望它克服 Symfony 限制的方式运行。
我从thrace数据网格包中获取的是它返回的json
数组。我已经能够完全自定义前端,其中包括编辑数据,添加新数据,保存更改等操作。
我已经创建了超过30列的网格,它们工作正常,因此应该回答您关于拥有巨大视图并能够编辑它们的问题。 jqGrid提供了您可以在前端本身提供的所有类型的数据验证。
我建议用Symfony再给jqGrid(捆绑)一个镜头。我绝对可以帮助你整合jqGrid和Symfony。