Pass a Symfony form from one view to another in ajax - php

I'm trying to make a tabs nav with a central block that contains a Symfony form.
When I click into a link in a tab navs, I reload the block with form and data.
But the issue is how to pass formView object from the first twig to the AJAX twig response view?
My controller
/**
* #Route("/change-tab/{tabId}", name="change_tab")
* #param Request $request
* #return Response
*/
public function changeTab(Request $request, $tabId): Response
{
$firstElement = $this->getDoctrine()->getRepository(Element::class)->findOneBy([
'cart'=>$tabId,
]);
return $this->render('partials/_bloc-cart.html.twig',[
'firstElement '=> $firstElement ,
//'form' => $request->getContent()
]);
}
My twig view
<div class="row p-2">
<div class="col-md-12 px-0" id="bloc-form">
{{ include('partials/_form.html.twig') }}
</div>
</div>
And the ajax JS :
$(document).on('click', '.linkToChange', function () {
$('.linkToChange.active').removeClass('active');
$(this).addClass('active');
let formPlan = $('#bloc-form').data('form');
$.ajax({
type: "POST",
data : formPlan,
url: "/ajax/change-tab/" + $(this).data('cart'),
success : function (datas) {
$('#bloc-form').html(datas);
}
});
});

You have different ways to do it, if you use the Form Component, you can do something like this:
public function changeTab(Request $request, $tabId): Response
{
$firstElement = $this->getDoctrine()->getRepository(Element::class)->findOneBy([
'cart'=>$tabId,
]);
$form = $this->createForm(ElementType::class, $firstElement);
$form->handleRequest($request);
// Save if form is submitted and valid
return $this->render('partials/_bloc-cart.html.twig',[
'firstElement '=> $firstElement ,
'form' => $form->createView()
]);
}

You can try:
/**
* #Route("/change-tab", name="change_tab", method="{POST}")
* #param Request $request
* #return Response
*/
public function changeTab(Request $request)
{
$tabId = $request->get('tab_id');
$firstElement = $this->getDoctrine()->getRepository(Element::class)->findOneBy([
'cart'=>$tabId,
]);
$html = $this->render('partials/_bloc-cart.html.twig',[
'firstElement '=> $firstElement ,
//'form' => $request->getContent()
]);
return $this->json(['html' => $html]);
}
Jquery:
$.ajax({
type: "POST",
data : formPlan,
url: "{{ url('change-tab') }}",
dataType: 'json',
data: {
'tab_id' : $(this).data('cart'),
}
success : function (data) {
$('#bloc-form').remove();
$('#bloc-form').html(data.html);
}
});

Related

Can't pass parameters using AJAX + Symfony 4

I'm using AJAX to request client names from my DB using autocomplete() inside an input #descricao. It requests a route that I created inside Symfony 4 (/acao).
The problem is that I'm trying to set a parameter (/acao?parametro=clientname) but I'm get an error:
Could not resolve argument $parametro of
"App\Controller\DefaultController::filter()", maybe you forgot to
register the controller as a service or missed tagging it with the
"controller.service_arguments"?
I tried to change my routes.yaml:
acao:
path: /acao
controller: App\Controller\DefaultController::filter
methods: GET
but it didn't work.
script.js:
$( "#descricao" ).autocomplete({
source: function( parametro, response ) {
$.ajax({
url: '/acao',
dataType: "json",
data: {
parametro: $('#descricao').val()
},
success: function(data) {
response(data);
}
});
}
});
DefaultController:
/**
* #param string $parametro
* #return JsonResponse
* #Route("/acao", name="acao", methods="GET")
*/
public function filter(string $parametro){
$em = $this->getDoctrine()->getManager()->getRepository(Clients::class)
->createQueryBuilder('c')
->andWhere('c.name_fantasy ilike :parametro')
->setParameter('parametro','%'.$parametro.'%')
->getQuery()
->getArrayResult();
return new JsonResponse($em);
}
What am I doing wrong?
ANSWER:
I managed to make it work using POST and changing table name c.name_fantasy to values:
Controller:
/**
* #param Request $request
* #return JsonResponse
* #Route("/acao", name="acao", methods="POST")
*/
public function filter(Request $request){
$q = strtoupper(trim($request->request->get('parametro')));
$em = $this->getDoctrine()->getManager()->getRepository(Clients::class)
->createQueryBuilder('c')->select('c.name_fantasy AS value')
->andWhere('c.name_fantasy like :parametro')
->setParameter('parametro', '%'.$q.'%')
->getQuery()
->getArrayResult();
return new JsonResponse($em);
}
AJAX:
$( "#descricao" ).autocomplete({
source: function( parametro, response ) {
$.ajax({
url: '/acao',
dataType: 'json',
method: 'POST',
data: {
parametro: $('#descricao').val()
},
success: function(data) {
if (data.length > 0) {
response(data);
}
else {
data = '';
response(data)
}
},
});
}
});
Firstly, you dont need use routes.yaml for routing, if you use the Route Component:
Symfony\Component\Routing\Annotation\Route
So just delete that stuff from routes.yaml.
EDITED:
/**
* #param Request $request
* #return JsonResponse
* #Route("/acao", name="acao", methods="GET", options={"expose"=true})
*/
public function filter(Request $request)
{
//you should strip and trim the parameter, like (just basic):
$clientName = strip_tags(
trim($request->query->get('parametro'))
);
// ...
}
Where Request is Symfony\Component\HttpFoundation\Request <-- you need add to the use statements!
If this not working for you, with your original ajax (what in your question), try on this way:
// ...
let formData = new FormData();
formData.append("parametro", $('#descricao').val());
$.ajax({
url: '/acao',
// ...
data : formData,
// ...
JUST A TIP:
I recommend, use the symfony/bundles/FOSJsRoutingBundle. You can link your routes in js like this:
import Routing from '../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router';
import Routes from '../../public/assets/js/fos_js_routes.json';
Routing.setRoutingData(Routes);
// ...
$.ajax({
//acao is your route name and you route in this case:
url: Routing.generate("acao"),
Dump the latest added routes, with this command:
php bin/console fos:js-routing:dump --format=json --target=public/assets/js/fos_js_routes.json
...

Symfony/PHP7, passing parameters with AJAX

I am creating eisenhover matrix and I want a button to refresh a data in grid, but I have no idea how to pass parameters/variables with ajax.
/**
* #Route("/articles/list", name="articles_list")
*/
public function showArticlesAction(bool $important = false, bool $urgent = false){
$articles = $this->returnAllArticles();
return $this->render(
'articles/list.html.twig',
array('articles' => $articles,
'important' => $important,
'urgent' => $urgent)
);
}
and script
function refreshFunctionName()
{
$.ajax({
type: "POST",
url: "{{ path('articles_list') }}",
data: {'important': true, 'urgent': true},
cache: false,
success: function(data){
$('#list').html(data);
},
error: function(){},
complete: function(){}
});
}
the showArticlesAction always takes default data if I try to use it with ajax, works fine with:
{{ render(controller(
'AppBundle:Articles:showArticles',
{ 'important': true,
'urgent': true}
)) }}
You need to define those parameters in the Route definition:
/**
* #Route("/articles/list/{important}/{urgent}", name="articles_list")
*/
and you GET method in AJAX call or get those parameters from POST table
/**
* #Route("/articles/list", name="articles_list")
*/
public function showArticlesAction(Request $request){
$articles = $this->returnAllArticles();
$important = $request->request->get('important');
$urgent = $request->request->get('urgent');
return $this->render(
'articles/list.html.twig',
array('articles' => $articles,
'important' => $important,
'urgent' => $urgent)
);
}

Laravel 5.4 - MethodNotAllowedHttpException in RouteCollection.php line 233

In my laravel 5.4 app, i have a crud module. for the edit bit, the data is rendered in a bootstrap modal. the data is pulled successfully but on hitting the update button, the error shown is returned.
here's my blade form that's rendered in the modal:
<form id="formEdit" class="form-horizontal" role="form" method="put">
//form elements
</form>
here's the ajax that handles it:
//edit_id is defined globally
$('#updateClass').click(function(){
var name = $("#formEdit #name").val();
var status = ($('#formEdit #status').prop("checked") == true) ? 1 : 0;
var token = $('meta[name="csrf-token"]').attr('content');
var id = edit_id;
var url = "classes/update/"+id;
var formdata = {
'name' : name,
'status' : status,
'_token' : token
};
$.ajax({
method: "PUT",
url: url,
data: formdata,
dataType: "json"
})
.done(function(data) {
//
})
.fail(function(data) {
//
});
});
});
updateClass is the button that displays the edit form on the modal..
here's my routes in web.php:
Route::resource('classes', 'ClassesController');
Route::group(['prefix' => 'admin'], function () {
Route::get('classes', 'ClassesController#index');
Route::get('classes/edit/{id}', 'ClassesController#edit');
Route::put('classes/update/{id}', 'ClassesController#update');
});
and finally, my update function in the classes controller:
public function update(Request $request, $id)
{
$validator = $this->validator($request->all());
$errors = $validator->errors();
$errors = json_decode($errors);
if ($validator->passes())
{
Dl_class::find($id)->update($request->all());
return response()->json();
}
else
{
return response()->json($errors, 422);
}
}
what i'm i missing?
To send PUT request from form you need to send a hiddne input element named as method. Something like this:
<input type='hidden' name='_method' value='PUT'>
there is a helper method to do the same like:
{{ method_field('PUT') }}
Only then Laravel can recognize your PUT request.
Or alternatively, you can enclose this value to your ajax call also Something like:
var formdata = {
'name' : name,
'status' : status,
'_token' : token,
'_method' : 'PUT'
};

Request conflict symfony action

I have an Ajax call that calls an action in the controller.
The Ajax call looks like this
$(document).on('click', '.editQuestionButton', function() {
var question_id = $(this).data('question');
console.log(question_id);
$.ajax({
type: "POST",
url: "/dashboard/form/AjaxEditQuestionForm/" + question_id + "",
success: function(data) {
$('#form-modal').html(data);
}
});
});
$(document).on('click', '.modal .fn-submit', function() {
$(this).closest('.modal').find('form').submit();
});
And this is the action.
/**
* #Route("/AjaxEditQuestionForm/{question}")
* #Template
* #ParamConverter("question", class="AppBundle:Question")
*/
public function ajaxEditQuestionFormAction(Request $request, $question)
{
$edit_question_form = $this->createForm(new AddQuestionType(), $question);
$edit_question_form->handleRequest($request);
if ($edit_question_form->isValid()) {
$em->flush();
return $this->redirectToRoute('app_form_create');
}
else{
die();
}
return array(
'question' => $question,
'editAjaxQuestionForm' => $edit_question_form->createView(),
);
}
The problem is that the action never returns the form but goes straight into checking if the form is valid.
I figure this has something to do with the $request but I'm not sure how to change this.
The action should first get the data from the Ajax call, return the form and if the form is submitted, check if the form is valid and flush the Question entity.
Any idea on how I should do this?

Laravel 5 AJAX Sort Order data (jQuery Sortable) with no HTML form

I'm to trying to store a sort order to each article within a help centre for my new site using Laravel 5 and having a bit of trouble getting it to work. I'm using jQuery UI's .sortable for arranging the elements on the page, and since there are going to be multiple sections throughout the site where areas are sortable, my jQuery script is built in a way for a 'one script for all' purposes. Hence the use of data-* attributes and route name references.
Here is the code I've got so far:
routes.php
Route::post('admin/help-centre/category/{category_id}/section/{section_id}/article/sort-order', 'AdminHelpCentreArticleController#sortOrder');
AdminHelpCentreArticleController.php
public function sortOrder($category_id, $section_id)
{
/* Return ------------------------------------- */
return [
'category_id' => $category_id,
'section_id' => $section_id
];
}
show.blade.php (Admin Article Listing)
<ul id="help-center-articles-sort" class="sortable">
#foreach ($helpCentreArticles as $helpCentreArticle)
<li class="sortable-element" data-sortable-element-id="{{ $helpCentreArticle->id }}">
{{ $helpCentreArticle->title }}
</li>
#endforeach
</ul>
Save Order
scripts.js (includes CSRF Token _token)
var csrfToken = $('meta[name="csrf-token"]').attr('content');
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
if (options.type.toLowerCase() === 'post')
{
options.data += options.data?'&':''; // add leading ampersand if `data` is non-empty
options.data += '_token=' + csrfToken; // add _token entry
}
});
$(document).ready(function() {
$('.sortable').sortable();
$('.sortable-save').on('click', function(e) {
e.preventDefault();
var route = $(this).attr('href'),
sortableID = $(this).attr('data-sortable-id');
var data = $('#' + sortableID + ' .sortable-element').map(function() {
return $(this).attr('data-sortable-element-id');
}).get();
$.ajax({
type: 'POST',
url: route,
dataType: 'json',
data: { id_array: data },
success: function(data) {
console.log(data);
}, error: function(data) {
console.log(data);
},
});
});
});
Everything so far is working in terms of the return response in the console, which is Object {category_id: "1", section_id: "1"}. But no matter what I try, I cannot seem to pass through the data map to the controller to use it.
I've tried a bunch of guesswork since I cannot find a single decent tutorial on AJAX in Laravel 5 anywhere, and I've tried things such as adding a $data parameter to the sortOrder() method, I've tried Input::all() and Request::all but it all returns errors (I'm guessing cause it's not an actual form?).
Once I've got the data to be passed through to the controller I'll be able to save the sort order to the database easily enough. But I can't quite get to that stage, any ideas?
EDIT
I should probably note that I do have a HelpCentreArticle model and a HelpCentreArticleRequest request too, here's some of the code from each file in case they are also needed:
HelpCentreArticle.php
class HelpCentreArticle extends Model {
protected $fillable = [
'category_id',
'section_id',
'title',
'content',
'excerpt',
'is_visible',
'sort_order',
'created_by',
'updated_by',
];
}
HelpCentreArticleRequest.php
class HelpCentreArticleRequest extends Request {
/* Authorization ------------------------------ */
public function authorize()
{
return true;
}
/* Validation rules --------------------------- */
public function rules()
{
$rules = [
'title' => 'required|min:3',
'content' => 'required|min:10',
];
return $rules;
}
}
I wasn't sure if I needed to add HelpCentreSectionRequest $request as the last parameter of the sortOrder() method, so I could use $request->all() but it just returns a 422 (Unprocessable Entity) in the console log.
So it appears that the correct way was to use Input::get('id_array'); instead of $_POST['id_array'];, which I tried, but when I originally tried this I wasn't including use Input; at the top of my controller, as I thought this was already accessible, but it wasn't.
Adding use Input;, and using Input::get(); is now working as expected.
Here is the updated code:
AdminHelpCentreArticleController.php
public function sortOrder($category_id, $section_id)
{
/* Query Select ------------------------------- */
$helpCentreCategory = HelpCentreCategory::findOrFail($category_id);
$helpCentreSection = HelpCentreSection::findOrFail($section_id);
/* Variables ---------------------------------- */
$id_array = Input::get('id_array');
$sort_order = 1;
/* Query Update ------------------------------- */
foreach($id_array as $id) {
$helpCentreArticle = HelpCentreArticle::where('id', $id)->first();
$helpCentreArticle->sort_order = $sort_order;
$helpCentreArticle->save();
$sort_order++;
}
/* Return ------------------------------------- */
return ['success' => true];
}
Then you can obviously access success for an if else statement in your jQuery to manipulate the page.
My implementation of UI sortable with Laravel
index.blade.php
...
#foreach($photos as $photo)
<tr data-sortable="{{ $photo->pivot->position }}" data-id="{{ $restaurant->id }}" data-photo-id="{{ $photo->pivot->photo_id }}">
<td>
<i class="fa fa-sort" aria-hidden="true"></i>
</td>
...
</tr>
#endforeach
<script type="text/javascript">
$("#sortable-ui tbody").sortable({
helper: fixHelper,
update: function(event, ui) {
$("#sortable-ui tbody tr").each(function(index){
console.log($(this).data('id')+', '+(index+1));
$.ajax({
url: '{{ route('owner.photo.update.position') }}',
type: 'POST',
data: 'restaurant_id='+$(this).data('id')+'&photo_id='+$(this).data('photo-id')+'&position='+(index+1)
})
.done(function (response) {
console.log(response);
})
.fail(function (jqXhr) {
console.log(jqXhr);
});
});
}
}).disableSelection();
</script>
scripts.js
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
AjaxController.php
public function updatePhotoPosition(Request $request)
{
$restaurant = $this->restaurantRepository->getById($request->get('restaurant_id'));
$photoId = $request->get('photo_id');
$photo = $restaurant
->photos()
->wherePivot('photo_id', $photoId)
->first();
$photo->pivot->position = $request->get('position');
$photo->pivot->save();
}

Categories