If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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.
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!
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...
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!
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.
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!
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
Yes, several chapters of the first course should be released at the end of this month or in Jan 2017 :)
Cheers!
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.
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!
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.
Hey Kuba,
Fair point! That's another reason to avoid setting URL in href attribute - avoid such 404 errors :)
Cheers!
// 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
}
}
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.