Skip to content

Instantly share code, notes, and snippets.

@eanplatter
Last active October 28, 2016 02:16
Show Gist options
  • Save eanplatter/6dac6140f60eaae9806659cd753adcb0 to your computer and use it in GitHub Desktop.
Save eanplatter/6dac6140f60eaae9806659cd753adcb0 to your computer and use it in GitHub Desktop.

Student Questions

Here are a list of the answers to the given questions. I have omitted the questions because some of them are long.

JavaScript

Hello Student!

Here is your original code:

<button id="btn-0">Button 1!</button>
<button id="btn-1">Button 2!</button>
<button id="btn-2">Button 3!</button>

<script type="text/javascript">
  var prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!'];
  for (var btnNum = 0; btnNum < prizes.length; btnNum++) {
      // for each of our buttons, when the user clicks it...
      document.getElementById('btn-' + btnNum).onclick = function() {
          // tell her what she's won!
          alert(prizes[btnNum]);
      };
  }
</script>

With this code, as you've noticed, you have a situation where each of your buttons are saying undefined when clicked.

The first step I would take to figure this problem out, so to break down the pieces of what you're alerting to see what is really going on:

  //
  document.getElementById('btn-' + btnNum).onclick = function() {
    // tell her what she's won!
    console.log(prizes) // ["A Unicorn!", "A Hug!", "Fresh Laundry!"]
    console.log(btnNum) // 3
    alert(prizes[btnNum]);
  };
  //

When we console.log prizes everything looks good, but look at the btnNum! It's 3! As we know, since arrays start at 0 when it comes to counting indexes, prizes[3] does not exist, or in other words it is undefined!

What is going on!?

This tends to be one of the more tricky issues with loops in JavaScript. Essentially, the issue is that loops in JavaScript do not have their own scope, or in other words, the variables you declare while making a loop exist outside the loop.

The loop runs instantly, 0, 1, 2 BAM DONE. Then you go to click the onclick handler for the button, and the button says "Hrmm, ok let me see, I gotta go find the prizes arr, oh there it is, go it! Now I need to find the btnNum variable... hmmm, oh there it is! looks like it is the number 3!" It is 3 because the loop is long gone, and has finished seconds ago.

This is because our onclick handler is using old information.

Ok, so... now what?

To solve this problem, we need a way to essentially take a snapshot of the variable while the loop is happening, that we can use later. The easiest way to do this is by passing the value into our onclick function.

  //
  document.getElementById('btn-' + btnNum).onclick = function(thePrize) {
    // tell her what she's won!
    alert(thePrize);
  }(prizes[btnNum]);
  //

So we changed a couple things, first we added a parameter to our function named thePrize. What this does is introduces a new variable into the scope of the function (thePrize) which we can reference later.

The second thing we added was (prizes[btnNum]) at the end of the definition of our function. This is a strange practice called an Immediately Invoked Function Expression, or IIFE (pronounced If-ee). Essentially we are running the function as we define it. This ensures that we are saving the value of prizes[btnNum]) at the time the loop is running, rather than later.

Now, if you were to try this code you might notice an issue. It automatically alerts all 3 values in a row! Without having us click the button. This is because we are using an IIFE, or in other words, we are immediately running the function as the loop is running.

Functions within Functions

There is one last piece of information we need to make this work. While we were able to use a function's scope to save the right variable, we had to run the function to do it. To solve this we will instead return another function, rather than running the alert. This inner function will allow us to keep the saved value we want, while also deferring its alert until we're ready.

  //
  document.getElementById('btn-' + btnNum).onclick = function(thePrize) {
    // tell her what she's won!
    return function () {
      alert(thePrize)
    }
  }(prizes[btnNum]);
  //

All we did here was return an anonymous function that alerts the prize for us! By using the outer function's variable scope, we were able to get the correct value! Wahoo!

This is a wonderful example of how to use closures in JavaScript. Closures are essentially functions that remember the variables defined around them, even if they're executed a long time after they're defined. They're a powerful feature of JavaScript, and as you can see, useful for times when you can't seem to get the right values.

For more reading on how closures work in JavaScript see MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures For more reading on how JavaScript uses blocks, and how they don't have scopes check out this MDN article: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block

This is a pretty great question!

Thanks, Ean

Angular

Hello!

Yes! They all pretty much do the same thing, just in slightly different ways. Here's a quick rundown of them and their differences:

Factory When you make a factory you are creating a JavaScript object and adding properties to it, then you return that object. For example:

app.controller('myController', function($scope, myFactory) {
  $scope.data = myFactory.getData()
})

app.factory('myFactory', function() {
  var _data = {...}
  var service = {
    getData: function() {
      return _data
    }
  }
  return service
})

Factories are one of the easiest ways to fetch data in your angular apps, and make it easy to access closure scope within the factory from the controller.

Service Services might feel a little bit more natural to you if you come from a strong object oriented programming background. Essentially they're very similar to factories but instead of creating an object and returning it your services is the object.

Same code as before but formatted as a service:

app.controller('myController', function($scope, myService) {
  $scope.data = myService.getData()
})

app.service('myService', function() {
  var data = {...}
  this.getData = function() {
    return data
  }
})

The biggest difference is that rather than using a new plain ole JavaScript object, we're using the services context (the this keyword).

The services is consumed the same way in the controller as the factory.

Provider Providers are probably the least used of the three, but they have some magical powers. Check it out:

app.controller('myController', function($scope, myProvider) {
  $scope.data = myService.getData()
  $scope.configData = myProvider.configData
})

app.provider('myProvider', function() {
  this.data = {...}
  this.configData = {...}
  
  this.$get = function() {
    return {
      getData: function() {
        return this.data
      },
      configData: this.configData
    }
  }
})

app.config(function(provider) {
  provider.configData = {...}
})

Providers are magical because we can access them in our app config. This can allow us to alter the information before it is passed to the controller, this is where we can find ourselves making things like middleware.

For more information, I'd recommend my friend Tyler's blog post: https://tylermcginnis.com/angularjs-factory-vs-service-vs-provider-5f426cfe6b8c#.3njp3ass8

It goes way more in depth on the subject

Hope that helps!

Thanks, Ean

Libraries vs Frameworks

Hi student!

Learning Angular is going to be a lot of fun. jQuery is a tool that has a very specific purpose, to manipulate the HTML of a website (DOM); Angular has a much grander purpose, which is to help structure web applications. jQuery can be used to create web apps, but it takes a lot of work to keep the code understandable.

jQuery is often a library because it is a group of methods that all have similar purposes. It's scope is a lot smaller than Angular's and does very specific things. Angular is considered a framework because it has a lot of pieces all wrapped together. It handles routing, and fetching data, and has a templating language, and a module loader (this may all sound like jargon right now and that's ok). Angular is just a lot more powerful than jQuery.

Now using a framework isn't always the best idea, there are drawbacks. Frameworks can make simple tasks more complex, frameworks also require you to learn a lot of things that don't translate outside of the framework. If you become a master of Angular that doesn't necessarily mean you are a master at JavaScript.

The key is to know when to use Angular and when to use something like jQuery. If someone just wanted a static landing page with an email submission form, I'd probably just use jQuery. If someone wanted to make something like Twitter I'd probably lean more towards Angular.

Neither tool is bad, some are just better for some jobs than others.

Thanks, Ean

CSS

Hi Student!

CSS can be so sneaky; sometimes you have to take two steps backwards in order to move 1 step forward. Your code is 99% perfect, the only issue is that you're using float to handle some of your layouts. Your instinct to use float was good, because you had to get those divs in a single row, rather than one on top of the other, the only issue is that float will do unintended things to your elements, as you've experienced :P

As you know, divs by default will stack on top of each other, that's because they have a default css value of display: block. The simplest way to get them to line up is to use that. Here's how you should update your code:

Old Code:

.pricing-tiers .tier {
  width: 260px;
  margin: 20px;
  padding: 10px;
  background-color: white;
  float: left;
}

New Code:

.pricing-tiers .tier {
  width: 260px;
  margin: 20px;
  padding: 10px;
  background-color: white;
  display: inline-block;
}

Thanks, Ean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment