If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOne of the crazy things about JavaScript... is that there's not one good way to loop over a collection! In PHP, we have foreach
, and it works perfectly. But in JavaScript, you need to create an ugly custom for
loop. Well actually, there is a .forEach()
function, but it only works on arrays, not other loopable things, like the Set object we'll talk about later. And, with .forEach()
, there is no break
if you want to exit the loop early.
That's why we've been using jQuery's $.each()
. But guess what? ES2015 fixes this, finally. Introducing, the for of
loop!
It looks like this: for (let repLog of data.items)
:
... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
... lines 4 - 37 | |
loadRepLogs() { | |
$.ajax({ | |
... line 40 | |
}).then(data => { | |
for (let repLog of data.items) { | |
this._addRow(repLog); | |
} | |
}) | |
} | |
... lines 47 - 230 | |
window.RepLogApp = RepLogApp; | |
... lines 232 - 233 |
And it's pretty easy to follow: repLog
is the new variable inside the loop, and data.items
is the thing we want to loop over. We're no longer passing this an anonymous function, so we can get rid of everything else. That's it. Say hello to your new best friend: the for of
loop.
Let's look for the other $.each()
spots and update those too! Instead, say for let fieldData of $form.serializeArray()
:
... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
class RepLogApp { | |
... lines 6 - 94 | |
handleNewFormSubmit(e) { | |
... lines 96 - 100 | |
for (let fieldData of $form.serializeArray()) { | |
formData[fieldData.name] = fieldData.value | |
} | |
... lines 104 - 111 | |
} | |
... lines 113 - 212 | |
} | |
... lines 214 - 231 | |
})(window, jQuery, Routing, swal); |
Before, the anonymous function received a key and then the fieldData
. But, we didn't actually need the key: the $.each()
function just forced us to add it. Now, things are cleaner!
Tip
Since we are in a for
loop now, we need to also update the return
statement to be continue
.
Make this same change in two more places: for $element of $form.find(':input')
. Ah, don't forget your let
or var
:
... lines 1 - 4 | |
class RepLogApp { | |
... lines 6 - 136 | |
_mapErrorsToForm(errorData) { | |
... lines 138 - 140 | |
for (let element of $form.find(':input')) { | |
... lines 142 - 152 | |
} | |
} | |
... lines 155 - 178 | |
} | |
... lines 180 - 233 |
Then, one more below: for let $element of $elements
:
... lines 1 - 2 | |
(function(window, $, Routing, swal) { | |
... lines 4 - 183 | |
class Helper { | |
... lines 185 - 204 | |
static _calculateWeights($elements) { | |
... line 206 | |
for (let element of $elements) { | |
... line 208 | |
} | |
... lines 210 - 211 | |
} | |
} | |
... lines 214 - 231 | |
})(window, jQuery, Routing, swal); |
Oh, and PhpStorm is warning me because I forgot to remove one of my closing parentheses! And, we don't need that semicolon! Yay!
So, use the for of
loop for everything! Well actually, that's not 100% true. for of
is perfect when you want to loop over a collection of items. But, if you want to loop over an associative array... or object, and you need to know the key for each item, then you'll use for in
.
Tip
Actually, you can use for of
with an object, with a clever combination of
Object.entries()
and array destructuring!
let pets = {
beagle: 'Bark Twain',
poodle: 'Snuffles'
};
for (let [petKey, petName] of Object.entries(pets)) {
console.log(petKey, petName);
}
BUT, the Object.entries()
method is still experimental, and may be
included in ES2017.
This is the one limitation of for of
: it gives you the value of the item you're looping over, but not its key, or index. In fact, if try to use for of
with an object, you'll get an error.
Hey plashenkov!
Ah, you're totally right! It's not something I had even thought of - const isn't allowed for a traditional "for", but definitely is for "for of". Thanks for the note!
Cheers!
In _mapErrorsToForm the `return` should be `continue`. With `return` the loop will stop iterating after first valid form input as the method executions is finished.
Hey @Marcin!
Ah, you’re totally right! I’ll have is update that so it doesn’t mess anyone up.
Thanks for reporting this!
Hey @Marcin
Actually, in order to stop the loop you have to specifically return false
otherwise the return statement will work just as a continue
statement
From jQuery docs: "You can stop the loop from within the callback function by returning false."
Hey MolloKhan!
Yes..... but unfortunately, in this case, we're not using a jQuery loop :). This is a native, JS for
loop. So, the return will exit from the _mapErrorsToForm function. But, I know why you got confused! This function did originally use a jQuery loop (https://symfonycasts.com/screencast/javascript/js-validation-error-handling#codeblock-f4b4af62c2). At some point, we refactored it to a native for. And that is when we needed to also change that return statement.
Cheers!
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.2.*", // v3.2.14
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.1
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
"symfony/swiftmailer-bundle": "^2.3", // v2.4.2
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.3.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.19
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.2
"symfony/phpunit-bridge": "^3.0" // v3.2.2
}
}
You can absolutely use const instead of let in "for of" loop.