Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Hooking up the Scientist Removal JavaScript

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Endpoint, done! Let's call this bad boy from JavaScript. Back in the template, each delete link will have a different URL to the endpoint. Add a new attribute called data-url set to path('genus_scientist_remove') and pass it genusId set to genus.id and userId set to genusScientist.id. Remember, that's a User object:

... lines 1 - 4
{% block body %}
<h2 class="genus-name">{{ genus.name }}</h2>
<div class="sea-creature-container">
<div class="genus-photo"></div>
<div class="genus-details">
<dl class="genus-details-list">
... lines 12 - 21
<dd>
<ul class="list-group">
{% for genusScientist in genus.genusScientists %}
<li class="list-group-item js-scientist-item">
... lines 26 - 31
<a href="#"
class="btn btn-link btn-xs pull-right js-remove-scientist-user"
data-url="{{ path('genus_scientists_remove', {
genusId: genus.id,
userId: genusScientist.id
}) }}"
>
<span class="fa fa-close"></span>
</a>
</li>
{% endfor %}
</ul>
</dd>
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 50 - 91

Oh, and do one more thing: give the li above its own class: js-scientist-item:

... lines 1 - 4
{% block body %}
<h2 class="genus-name">{{ genus.name }}</h2>
<div class="sea-creature-container">
<div class="genus-photo"></div>
<div class="genus-details">
<dl class="genus-details-list">
... lines 12 - 21
<dd>
<ul class="list-group">
{% for genusScientist in genus.genusScientists %}
<li class="list-group-item js-scientist-item">
... lines 26 - 40
</li>
{% endfor %}
</ul>
</dd>
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 50 - 91

That'll also help in JavaScript.

Making the AJAX Call

Scroll back to the javascripts block. I'll paste a few lines of code here to get us started:

... lines 1 - 50
{% block javascripts %}
... lines 52 - 66
<script>
jQuery(document).ready(function() {
$('.js-remove-scientist-user').on('click', function(e) {
e.preventDefault();
var $el = $(this).closest('.js-scientist-item');
$(this).find('.fa-close')
.removeClass('fa-close')
.addClass('fa-spinner')
.addClass('fa-spin');
... lines 78 - 86
});
});
</script>
{% endblock %}

Ok, no big deal: the first line uses $(this), which is the link that was just clicked, and finds the .js-scientist-item li that is around it. We'll use that in a minute. The second chunk changes the fa-close icon into a loading spinner... ya know... because we deserve fancy things.

The real work - the AJAX call - is up to us. I'll use $.ajax(). Set the url key to $(this).data('url') to read the attribute we just set. And then, set method to DELETE:

... lines 1 - 50
{% block javascripts %}
... lines 52 - 66
<script>
jQuery(document).ready(function() {
$('.js-remove-scientist-user').on('click', function(e) {
e.preventDefault();
var $el = $(this).closest('.js-scientist-item');
$(this).find('.fa-close')
.removeClass('fa-close')
.addClass('fa-spinner')
.addClass('fa-spin');
$.ajax({
url: $(this).data('url'),
method: 'DELETE'
... lines 82 - 83
});
... lines 85 - 86
});
});
</script>
{% endblock %}

To add a little bit more fancy, add a .done(). After the AJAX call finishes, call $el.fadeOut() so that the item disappears in dramatic fashion:

... lines 1 - 50
{% block javascripts %}
... lines 52 - 66
<script>
jQuery(document).ready(function() {
$('.js-remove-scientist-user').on('click', function(e) {
... lines 70 - 78
$.ajax({
url: $(this).data('url'),
method: 'DELETE'
}).done(function() {
$el.fadeOut();
});
... lines 85 - 86
});
});
</script>
{% endblock %}

Testing time! Refresh.

Cute close icon, check! Click it! It faded away in dramatic fashion! Yes!

Checking the Delete Query

Check out the web debug toolbar's AJAX icon. Mixed in with AJAX call for notes is our DELETE call. Click the little sha, then go to the Doctrine tab. Ooh, look at this:

DELETE FROM genus_scientist WHERE genus_id = 11 AND user_id = 11

Gosh darn it that's nice. To prove it, refresh: the scientist is gone. ManyToMany? Yea, it's as simple as adding and removing objects from an array.

Well, ok, it will get a bit harder soon...

Leave a comment!

10
Login or Register to join the conversation
Farshad Avatar
Farshad Avatar Farshad | posted 2 years ago | edited

Off topic: I want to upload an undefined amount of input fields on the database. Its an undefined amount, because the amount of input fields depend on the amount of 'Meetstaten', which is different on every product. But I think it's uploading a string instead of a float.


foreach ($amountOfMeetstaten as $index => $item) {
      $measurementForm->setMeetstaatId($id->getMeetstaatId());
      $amountOfMeetstaten[$index];
      $measurementForm->setMeasurement($amountOfMeetstaten[$index]);
      $request->request->get($index);
      $entityManager->persist($measurementForm);
}
$entityManager->flush();
$this->addFlash('success', 'Measurement added.');
Reply

Hey Farry7,

Sorry, I really don't understand your question here. If you think something is a string instead of float - try type casting, i.e.:


$floatValue = (float) $stringValue; // converting number as a string into float 

But to be sure - try to debug the value with "dd($value);".

Btw, if the number comes from form - it might be so that it's come as a string, as everything you send via form is a string actually and you need manually to typecast it. Symfony Form component probably can do it behind the scene for you, but you need to use a proper form type for the field, e.g. NumberType I think.

I hope this helps!

Cheers!

Reply
Default user avatar
Default user avatar Connect James | posted 5 years ago

Hi Ryan, looking at this course I was thinking it would be really cool to have a short course on a few jQuery methods that you use a lot in relation with the Symfony framework.

Reply

Hey Connect,

jQuery is pretty simple and has a good docs with ton of examples: http://api.jquery.com/ - but you're right... And we're working on it right now: https://knpuniversity.com/s... ! It will cover some most useful jQuery methods, which we use a lot in our screencasts and much more! So you can subscribe to it and we'll notify you when this course is released.

Cheers!

1 Reply

That's awesome!!! Would definitely LOVE LOVE LOVE to get my hands on the React.js course. I know that that and Angular are really popular right now. :)

Any idea on a time frame for any of the planned JS courses? I notice the first one already seems to have some kind of lesson plan. :D

Reply

Yes, several chapters of the first course should be released at the end of this month or in Jan 2017 :)

Cheers!

Reply
Default user avatar
Default user avatar Richard Perez | posted 5 years ago

Probably no the best place to ask, but why to use a `data-url` instead of using the `href` attribute? You are still using the e.preventDefault() so nothing will happen. I saw it in another of your tutorials and now I'm curious.

Reply

Hey Richard,

Setting URL in href probably requires more work, since if user has disabled JS in they browser - they will follow this link on click. But with data-url nothing happens. So you can set it in href, but then probably you have to handle this case, e.g. return JSON response if AJAX request was sent and do redirecting if user follows this link without JS. And one more thing: your website most probably is scanned by different robots like google crawler, etc. and most of them do not support JS, that's why they will follow this link to, but probably it's not something you want for JS links, but it depends. So it's up to you what to use, but data-url is more mainstream now, so you'll see using it more by us :)

Cheers!

1 Reply

Remember that you've set method to DELETE, while following link without JS will fire GET method, so user will probably get 404 and nothing will happen.

Reply

Hey Kuba,

Fair point! That's another reason to avoid setting URL in href attribute - avoid such 404 errors :)

Cheers!

2 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is built on Symfony 3, but most of the concepts apply just fine to newer versions of Symfony.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "symfony/symfony": "3.4.*", // v3.4.49
        "doctrine/orm": "^2.5", // 2.7.5
        "doctrine/doctrine-bundle": "^1.6", // 1.12.13
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.4.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.23.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.9.0
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2" // v1.3.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.47
        "nelmio/alice": "^2.1", // v2.3.6
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice