I have a view in which I need after a click to send a javascript variable to a controller which contains a form that I send to the database.
So after the click, I'm using ajax to call my controller and load the html on the same view like this:
$(".month").click(function(){
var click = $(this);
var month = click.val();
var year = $("#years").val();
var url = Routing.generate('av_platform_formulaire');
$.ajax(url,
{
type: 'GET',
data: {"month": month,
"year" : year},
dataType: "html",
success: function (data) {
$('#content').empty();
$('#content').append(data);
},
error : function(jqXHR, textStatus, errorThrown){}
});
});
So far there is no problem, my view containing the form is loading correctly and I receive the data I sent via the ajax request but when I fill my form and try to submit it, the page is refreshing and it's like nothing happens...
Here is my 2 controllers (the second is the problematic one):
public function saisieAction(Request $request){
$thisyear = date("Y");
return $this->render('AvPlatformBundle:Platform:saisie.html.twig',
array(
'year' => $thisyear
));
}
public function formulaireAction(Request $request){
$user = $this->getUser();
$em = $this->getDoctrine()->getManager();
//$repository = $em->getRepository('AvPlatformBundle:NoteDeFrais');
// Create the form
$form = $this->get('form.factory')->createBuilder(FormType::class)
->add('ndf', CollectionType::class, array(
'entry_type' => NoteDeFraisType::class,
'label' => false,
'allow_add' => true,
'allow_delete' => true,
))
->getForm();
if ($request->isXmlHttpRequest()){
$month = $request->get('month');
$year = $request->get('year');
$sub_date = $month . '/' . $year;
}
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
// After debugging, the code inside the if is not executed
$notesDeFrais = $form['ndf']->getData();
foreach ($notesDeFrais as $ndf) {
$ndf->setUser($user);
$ndf->setMois($sub_date);
$em->persist($ndf);
}
$em->flush();
}
return $this->render('AvPlatformBundle:Platform:formulaire.html.twig',
array(
'form' => $form->createView()
));
}
My view containing the form:
<div id="ms_form">
{{ form_start(form) }}
<div id="bloc_saisie" class="fieldset">
<fieldset>
<div id="form_ndf" class="form_ndf" data-prototype="
{% filter escape %}
{{ include('AvPlatformBundle:Platform:prototype.html.twig', { 'form': form.ndf.vars.prototype }) }}
{% endfilter %}">
</div>
<div class="buttons">
<button type="button" class="fas fa-plus" id="add_ndf"></button>
<input type="submit" class="btn btn-primary btn-lg" id="next_button" >
</div>
</fieldset>
</div>
{{ form_row(form._token) }}
{{ form_end(form, {'render_rest': false}) }}
</div>
Related
Everytime I click a category it shows only "Filtering..." and not loading all the post.
Filtering Issue
HTML
<div class="filters"> <span class="filter active" data-set="all"><span>Show all</span></span> <?php $categories = get_categories(array( 'hide_empty' => true, 'exclude' => array(1, 143) )); foreach ($categories as $category) : ?> <span class="filter" data-set="<?= $category->slug ?>"><span><?= $category->name ?></span></span> <?php endforeach; ?> </div>
SCRIPT
`(($) => {
const filters = $("#filterBar .filters");
const articles = $("#all-posts .articles");
filters.children('.filter').click(function(e) {
e.preventDefault();
const button = $(this),
loader = $("#loader"),
data = {
'action': 'filterposts',
'category': $(this).data('set')
};
$.ajax({
url : latest_reads_params.ajaxurl,
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
articles.html("<span class='message'>Filtering...</span>");
loader.remove();
filters.find('.active').removeClass('active');
button.addClass('active');
},
success : function( data ){
if( data ) {
data = JSON.parse(data);
articles.hide().html(data.posts).slideDown(400);
if (data.max_pages > 1) {
articles.after('<div id="loader">'+
'<span class="ibtn secondary large" id="loaderAction" data-category="'+data.category+'" data-maxpages="'+data.max_pages+'">Load more</span>'+
'</div>');
}
} else {
articles.html("<span class='message'>No post found for filter</span>");
}
}
});
});
})(jQuery);`
I wanted to display all category post
Can someone please tell me how to output data from my controller to an HTML form. I want to change the label of an anchor from "Like" to "Liked" if the user has already clicked the link previously.
Here is the HTML.
<section class="row posts">
<div class="col-md-6 col-md-3-offset">
<header><h3>other posts</h3></header>
#foreach($posts as $post)
<article class="post">
<p>{{ $post->content }}</p>
<div class="info">Posted by {{ $post->user->username }} on {{ $post->created_at }}</div>
<div class="interaction">
Like
#if(auth()->user() === $post->user)
|
Edit |
Delete
#endif
</div>
</article>
#endforeach
</div>
<script>
var token = '{{ session()->token() }}';
var urlLike = '{{ route('like') }}';
</script>
</section>
The JavaScript to get the postid from the form:
...
$('.like').on('click', function (event) {
event.preventDefault();
postId = event.target.dataset.postid;
var isLike = event.target.previousElementSibling==null ? true:false;
$.ajax({
method: 'POST',
url: urlLike,
data: {isLike: isLike, postId: postId, _token: token}
})
.done(function () {
//change the page
})
})
...
The route:
Route::post('/like',[
'uses' => 'PostController#postLikePost',
'as' => 'like'
]);
Finally, can someone please tell me how to send the output from the controller to the HTML form?
public function postLikePost(Request $request)
{
$post_id = $request['postId'];
$is_like = $request['isLike'] === 'true' ? true : false;
$post = Post::find($post_id);
if (!$post) {
return null;
}
$user = Auth::user();
$like = $user->likes()->where('post_id', $post_id)->first();
if ($like) { // user already liked the post
$like->delete();
return null; // output to "Like" in the html form here
}
$like = new Like();
$like->post_id = $post->id;
$like->user_id = $user->id;
$like->save(); // output to "Liked" in the html from here
return null;
}
The label of the Like anchor should change from Like to Liked if the user has already like the post.
I'd set up both as POSTs and then in your success block query the result and set to either like or liked. Something like:
success: function (data) {
document.getElementById("something").innerHTML = "Liked";
}
You can do it something like this, change it as per your need though.
public function postLikePost(Request $request)
{
$post = Post::where('id', $request->get('post_id'))->first();
if(!$post){
return response()->json(['status' => 1, 'message' => 'post not found']);
}
if($post->liked == 1)//change it as per your model
{
return response()->json(['status' => 2, 'message' => 'already liked']);
}
$post->status = 1;
$post->save();
return response()->json(['status' => 3, 'message' => 'liked']);
}
and in your ajax success
success: function(response){
if(response.status == 1){
window.alert('Post Not Found')
}
else{
document.querySelector('#likeBtn').innerHTML = 'liked'
}
like button
Like
I have a Symfony form submitted by Ajax request. This form contains 2 fields : a date (datepicker) and a number of days (a choiceType). I can't initialize the number of days when the form is built since it relies on the date selected. Using Javascript, I update the options in select-picker. That works very well. Typically, an option looks like this : <option value="1">1 jours<\option>.
Now, when the form is submitted, I need to add those fields in the choiceType.
I used isXmlHttpRequest method because the form is submitted with an Ajax request, waiting for an answer. If the form is valid, I need to inform the Ajax call (I dont want to render another template).
I do the following in the Controller:
/**
* #param Request $request
* #return Response
* #Route("/ajout_favoris", name="ajoutFavoris")
*/
public function ajoutFavoris(Request $request){
$form = $this->creerFormulaire()->getForm();
$form->handleRequest($request);
if ($request->isXmlHttpRequest()){
if ($form->isValid() ) {
// actions ...
$reponse = ['statut' => 'success'];
}else {
$reponse = ['statut' => 'form-invalid'];
}
$reponse = new Response(json_encode($reponse));
$reponse->headers->set('Content-Type', 'application/json');
return $reponse;
}
return $this->render('#Carte/Reservation/reservation.html.twig', array(
"form" => $form->createView() ));
Here is how I build the form :
public function creerFormulaire(){
$formulaire = $this->createFormBuilder()
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$optionList = $this->getValidChoicesForNbJours($data["nbJours"]);
$form->add('nbJours', 'choice', array(
'attr' => [ 'placeholder' => 'nombre de jours',
'class' => 'selectpicker',
],
'choices' => $optionsList,
));
})
->add('dateDebut', DateTimeType::class, [
'widget' => 'single_text',
'format' => 'dd/MM/yyyy',
'input' => 'datetime',
'label' => 'Date début',
'required' => true,
'attr' => ['class' => 'datepicker input-date',
'type' => 'text',
'placeholder' => 'JJ/MM/AAAA',
'readonly' => true,
'required' => true,]
])
->add('nbJours', 'choice', [
'label' => 'Nombre de jours',
'choices' => [],
'attr' => [ 'placeholder' => 'nombre de jours',
'class' => 'selectpicker',
'title' => 'Nombre de jours',
'readonly' => true,
'required' => false,]
])
->add('ajoutFavoris', SubmitType::class, [
'label' => 'Ajouter aux favoris',
'attr' => [ 'class' => 'btn btn-primary reserver' ]
]);
return $formulaire;
}
And here how I fill the options :
public function getValidChoicesForNbJours($range){
$liste = [];
for ($i = 1; $i <= $range; $i++){
$liste[$i.' jours'] = strval($i);
}
return $liste;
}
and here is the ajax call :
path_to_add_fav = ...
idPlan = ...
$("#form_ajoutFavoris").click(function (e) {
let dateDebut = document.getElementById('form_dateDebut').value;
let nbJours = document.getElementById('form_nbJours').value;
if (dateDebut && nombreJours){
let data = {
poste_id: idPoste,
plan_id: idPlan,
nbJours: nbJours ,
};
$.ajax({
url: path_to_add_fav,
type: "post",
data: JSON.stringify(data),
dataType: "json",
success: function (data) {
console.log(data.statut);
if (data.statut === "success") {
window.parent.$.fancybox.close();
}
}
});
}
});
There is no error in Chrome console. At the fisrt submit, it always display form-invalid, on the following, sometimes display form-invalid, sometines nothing...
I read a lot of "similar post" but haven't been able the achieve mine. This post is related to this one but the question is now different.
Thank you for reading !
EDIT : Add HTML
{{ form_start(form) }}
<div class="card-deck maxheight">
{% include '#Carte/Carte/poste_card.html.twig' with {'poste':poste, 'connected':connected, 'showbtn':false } %}
<div class="card card-form" id="card-reserv">
<div class="card-body">
{{ form_errors(form) }}
<div id="warning-pecheurs" class="alert alert-warning padding alert-dismissible"
role="alert">
<button type="button" class="close float-right" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
warning message
</div>
<table id="table" class="largeur">
<colgroup>
<col span="1" class="col1">
<col span="1" class="col2">
</colgroup>
<tbody>
<tr>
<td>
<div class="input-group">
{{ form_label(form.dateDebut) }}
</div>
</td>
<td class="date">
{{ form_widget(form.dateDebut) }}
</td>
</tr>
<tr>
<td colspan="2">
{{ form_errors(form.dateDebut) }}
</td>
</tr>
<tr>
<td>
<div class="input-group">
{{ form_label(form.nbJours) }}
</div>
</td>
<td>
{{ form_widget(form.nbJours) }}
</td>
</tr>
<tr>
<td colspan="2">
{{ form_errors(form.nbJours) }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row margin0">
<button type="button" class="btn btn-secondary" data-dismiss="modal"
onclick="window.parent.$.fancybox.close()" value="CloseFB">
Annuler
</button>
{{ form_widget(form.ajoutFavoris) }}
</div>
{{ form_end(form) }}
And here how I populate the select-picker in JS:
let new_options = "";
for (i = 0; i < nbOptions; i++, j++)
new_options += "<option value=" + j + ">" + j + " jours</option>";
$("#form_nbJours").html(new_options).selectpicker('refresh');
EDIT : Solution
The Ajax method call is the guilty ! With this call, it works perfectly !
$('form').submit(function (e) {
console.log("ok");
e.preventDefault();
let formSerialize = $(this).serialize();
$.post(path_to_add_fav, formSerialize, function(response) {
console.log(response.statut);
if (response.statut === "success") {
console.log("Réponse correcte !");
window.parent.$.fancybox.close();
}
}, 'JSON');
}
you can disable form validation by calling $builder->get('nbJours')->resetViewTransformers(); after you have added the field
I'm new to using ajax. For example after field title is filled, I want to search in database for specific data and return more fields based on that input. So far I can only receive my title data in /ajax/post page by pressing get data/post data or submit button. How do I receive my title input and data from Route::post while/after filling title? If I remove Form::model and Form::close() I do get my dummy data from Route::post without page refresh by clicking Post data button, but without title value.
I'm aware that checking title field involves some jQuery/js, but I have no idea how to actually bring that title field into my route to do some database searching and return some data with it.
View:
{!! Form::model($project = new \App\Project, ['url' => 'ajax/post', 'method' => 'post']) !!}
<!-- pass through the CSRF (cross-site request forgery) token -->
<meta name="csrf-token" content="<?php echo csrf_token() ?>" />
<!-- some test buttons -->
<button id="get">Get data</button>
<button id="post">Post data</button>
<div class="form-group padding-top-10">
{!! Form::label('title', 'Title') !!}
{!! Form::text('title', null, ['class' => 'form-control', 'placeholder' => 'Title']) !!}
</div>
{!! Form::submit('Submit Button', ['class' => 'btn btn-primary form-control']) !!}
{!! Form::close() !!}
Ajax script:
<script>
$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });
function onGetClick(event)
{
// we're not passing any data with the get route, though you can if you want
$.get('/ajax/get', onSuccess);
}
function onPostClick(event)
{
// we're passing data with the post route, as this is more normal
$.post('/ajax/post', {payload:'hello'}, onSuccess);
}
function onSuccess(data, status, xhr)
{
console.log(data, status, xhr);
// JSON is deserialised into an object
console.log(String(data.value).toUpperCase())
}
$('button#get').on('click', onGetClick);
$('button#post').on('click', onPostClick);
</script>
And in route:
Route::get('/ajax/view', ['as' => 'home', 'uses' => 'AjaxController#view']);
Route::get('/ajax/get', function () {
$data = array('value' => 'some get');
return Response::json($data);
});
Route::post('/ajax/post', function () {
$data = array('value' => 'some data', 'input' => Request::input());
return Response::json($data);
});
What you need is to implement the jquery keypress function.
so here is you js:
$("input.title").keypress(function(){
var title = $(this).val();
// now do the ajax request and send in the title value
$.get({
url: 'url you want to send the request to',
data: {"title": title},
success: function(response){
// here you can grab the response which would probably be
// the extra fields you want to generate and display it
}
});
});
as far as in Laravel you can pretty much treat it the same as a typical request except you will return json:
Route::get('/url-to-handle-request', function({
// lets say what you need to populate is
//authors from the title and return them
$title = Route::get('title'); // we are getting the value we passed in the ajax request
$authors = Author::where('title' ,'=', $title)->get();
return response()->json([
'authors' => $authors->toArray();
]);
}));
Now I would probably use a controller and not just do everything within the route but I think you'll get the basic idea.
I have an entity Lexeme, which contains Language and Phoneme entities.
In my Form Type, I want the Phoneme choices to be different depending on the Language entity chosen for the Lexeme.
The problem is when I dynamically update the form, the correct Phonemes are displayed, but they are not populated from the database: i.e. those Phonemes which belong to the Lexeme in the database do not appear as checked in the dynamically loaded form.
The Lexeme Controller:
public function editAction(Request $request, $id)
{
$lexeme = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->find($id);
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->where('l.id > :thisId')
->setParameter('thisId', $id)
->setMaxResults(1)
->getQuery();
$nextLexeme = $query->getResult();
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->where('l.id < :thisId')
->setParameter('thisId', $id)
->setMaxResults(1)
->getQuery();
$previousLexeme = $query->getResult();
if ( count($nextLexeme) < 1 ) {
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->orderBy('l.id', 'asc')
->setMaxResults(1)
->getQuery();
$nextLexeme = $query->getResult();
}
if ( count($previousLexeme) < 1 ) {
$query = $this->getDoctrine()
->getRepository('Aleph2OmegaTranscriptionBundle:Lexeme')
->createQueryBuilder('l')
->orderBy('l.id', 'desc')
->setMaxResults(1)
->getQuery();
$previousLexeme = $query->getResult();
}
if (!$lexeme) {
$lexeme = new Lexeme();
}
$form = $this->createForm(new EditLexemeType(), $lexeme);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($lexeme);
$em->flush();
return $this->redirect($this->generateUrl('aleph2_omega_transcription_lexemes_edit', array ('id'=>$lexeme->getId())));
}
return $this->render('Aleph2OmegaTranscriptionBundle:Lexeme:edit.html.twig', array(
'form' => $form->createView(),
'previousId' => $previousLexeme[0]->getId(),
'nextId' => $nextLexeme[0]->getId()
));
}
The Lexeme Form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formModifier = function (FormInterface $form, $language) {
$form
->remove('phonemes')
->add('phonemes', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Phoneme',
'property' => 'latin',
'expanded' => true,
'multiple' => true,
'by_reference' => false,
'query_builder' => function(EntityRepository $er) use ($language) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', $language);
},
));
};
$builder
->add('language', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Language',
'property' => 'name',
'expanded' => false,
'multiple' => false,
))
->add('defaultGreek')
->add('defaultHebrew')
->add('save', 'submit')
//->add('id', 'hidden')
->add('words', 'collection', array(
'type' => new WordLimitedProtoType(),
'allow_add' => true,
'allow_delete' => true,
'options' => array('data_class' => 'Aleph2Omega\TranscriptionBundle\Entity\Word'),
'prototype' => true,
'by_reference' => false,
))
->add('phonemes', 'entity', array(
'class' => 'Aleph2OmegaTranscriptionBundle:Phoneme',
'property' => 'latin',
'expanded' => true,
'multiple' => true,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', '1');
},
))
->add('save', 'submit')
->setMethod('POST')
->addEventListener(
FormEvents::POST_SUBMIT,
function ($event) {
$event->stopPropagation();
},
900)
->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
if ($data instanceof Word) {
$formModifier($event->getForm(), $data->getLanguage());
}
}
)
->get('language')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$language = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $language);
});
}
The Lexeme Twig template:
{{ include('Aleph2OmegaTranscriptionBundle::menu.html.twig') }}
{{ form_start(form) }}
{{ form_row(form._token) }}
<h1>< Edit Lexeme ></h1>
{{ form_errors(form) }}
<table>
<tr><th>Language</th><td>{{ form_widget(form.language) }}</td></tr>
<tr><th>Default Greek</th><td>{{ form_widget(form.defaultGreek) }}</td></tr>
<tr><th>Default Hebrew</th><td>{{ form_widget(form.defaultHebrew) }}</td></tr>
</table>
<table>
<tr><th>Phonemes</th></tr>
<tr><td>{{ form_widget(form.phonemes) }}</td></tr>
</table>
<h3>{{ form_widget(form.save) }}</h3>
<table class="words" data-prototype="{% filter escape %}{% include 'Aleph2OmegaTranscriptionBundle:Word:limited_prototype.html.twig' with { 'item': form.words.vars.prototype } %}{% endfilter %}">
{# iterate over each existing word and render its fields #}
<tr><th>transcription</th><th>normalised</th><th>Hebrew</th><th>comment</th></tr>
{% for word in form.words %}
<tr>
<td>{{ form_widget(word.transcription) }}</td>
<td>{{ form_widget(word.normalisedTranscription) }}</td>
<td>{{ form_widget(word.hebrewTranscription) }}</td>
<td>{{ form_widget(word.comment) }}</td>
</tr>
{{ form_rest(word) }}
{% endfor %}
</table>
{{ form_rest(form) }}
{{ form_end(form) }}
The javascript for AJAX:
jQuery(document).ready(function() {
$("[id$=language]").change(function() {
languageChange();
});
function languageChange() {
var $languages = $("[id$=language]");
$($languages).each(function() {
var $form = $(this).closest('form');
var data = {};
data[$(this).attr('name')] = $(this).val();
$.ajax({
url : $form.attr('action'), //Routing.generate('route_id', /* your params */), //
type: $form.attr('method'),
data: data,
success: function(html) {
$phonemes = $("[id$=phonemes]");
$($phonemes).each(function() {
$(this).replaceWith(
$(html).find('#'+this.id)
);
});
}
});
});
}
languageChange();
});
UPDATE: Some screenshots
In private correspondence, some people wanted clarification as to the behaviour I'm looking for.
Here's a page from a different entity (word), which doesn't make use of AJAX to populate the phonemes field. This exhibits the sort of behaviour I would like:
The Lexeme entity (which the code above is for), which is updated via AJAX, correctly displays the right phonemes for each language, it's just that the phonemes recorded in the database as "connected" to the Lexeme are not checked.
E.g., here are the correctly displayed phonemes when "Hebrew" is selected:
And here are the correctly displayed phonemes when "Greek" is selected:
Finally, as far as debugging goes, I've rooted around using Chrome console and network display, and this hints that the problem may be due to CSRF with AJAX. However, what I don't understand is—if CSRF really is the problem, why is the page returned from the server at all?
UPDATE 2: A partial work-around
Ok, so a partial workaround to at least get the initial page-load displaying correctly is to avoid the AJAX call. This doesn't solve the problem of ensuring that AJAX updates display everything correctly, but at least improves functionality (and is, I think, a better-practice way of pre-populating the form anyway than using AJAX).
In the buildForm function, the data to populate the form can be obtained by using:
$lexemeEntity = $builder->getData();
This can then be fed into the initial query populating the phonemes entity type (note, don't forget to "use" this in the declaration of the defined function):
'query_builder' => function(EntityRepository $er) use ($lexemeEntity) {
return $er->createQueryBuilder('u')
->select('p')
->from('Aleph2OmegaTranscriptionBundle:Phoneme', 'p')
->where('p.language = :language')
->setParameter('language', $lexemeEntity->getLanguage());
},
Finally, the default AJAX call on the initial load of every page needs to be turned off in the javascript. I did this by just commenting:
//languageChange();
Now initial page loads are all populated correctly—but further updates to the forms using AJAX are still not loaded with all the data, any help with this still very much welcome.