Skip to content

Latest commit

 

History

History
465 lines (306 loc) · 26.4 KB

FAQ.md

File metadata and controls

465 lines (306 loc) · 26.4 KB

Fullstack Course 5 Frequently Asked Questions

Table of Contents

How to ask and get your question answered: A MUST READ!

There is a simple theme to this answer: Help others help you!

Before I dive into how you should ask your question, let me put things into perspective. You are asking others to spend their time helping you figure out your issue for free. You should do all you can to help them minimize the time they spend helping you upfront. It's the least you can do.

Asking generic questions like "My code doesn't work. Please help!" is basically useless as you are inviting people to spend their time asking you basics like: "Ok, what doesn't work? Where is your code?"

However, even if you do provide some details, they must be the right details so others can help you.

Here is a general guide as to what you should provide:

  1. Post your question to the relevant forum. If your question is about material or assignment of Week 2, post to the Week 2 Discussion Forum. The forum itself will already give context to what you're asking and let others find your question much easier.

  2. Describe your issue with specifics. Don't just say "it doesn't work." Explain what isn't working correctly with steps of what you did when you are experiencing this behavior.

  3. Commit your code with the issue to GitHub and provide 2 links:

    • Link to your repository where people can clone your code and quickly look at it on their own machines.
    • Link to your deployed website (using github.io, i.e., GitHub Pages) so people don't have to spend their time trying to deploy your site just to see for themselves what's going on.
    • Do not take a screenshot of your code. Screenshots may be approapriate when you want to show what you're seeing in terms of layout, but for code - that's very unhelpful.

    Rarely (but it happens), it's appropriate to simply provide a small code snippet that you have a question about. When you do, make sure to use the <> code formatter on Coursera to place your code snippet into the post. Don't just copy/paste it straight into the text of the post. It's very hard to read.*

  4. If you are having issues with installation, GitHub, etc., make sure to describe what you did with enough detail, and if there are errors or logs that you saw, you need to include those in your question. Simply saying "It doesn't let me push my code to the repository" won't do. If you tried to push the code and it didn't work, there were error messages that were displayed. Provide those messages, including the exact command you used.

Last, but certainly not least! I can't recount every scenario that can occur in this short guide. Use common sense. Ask yourself: If I came up to a total stranger in this class and showed them my post, would they have enough information to help me or would they for sure have to ask me something? If they would have to ask you something, you are not done with your post. Yes, of course, there are times when you yourself don't know what you don't know. That's ok. Just put in an honest effort to HELP OTHERS HELP YOU.

Do I have to use Atom?

No. You can use any editor you like

Duplicate error with ng-repeat

In order for angular to know exactly what items have changed, it calculates a hash for each item within the ng-repeat. Otherwise, if you had 5 items with the exact same contents, how would it be able to determine which of those items was modified? When it determines that duplicates exist, it prints out that error.

In your case, to get around this, there's a track by expression that you can add to the end of the ng-repeat expression. This expression lets you define a property that uniquely identifies each item in the array. For arrays with primitive types, one approach is to use track by $index which tracks each item in the array by the objects position within the array.

<div ng-repeat="num in [1, 2, 2, 3] track by $index">
  {{num}}
</div>

By adding the track by $index, you will not get the duplicate error.

What is MVVM? MVC vs MVVM?

Terminology question

You are using the term "ViewModel" for the design pattern of Angular. I hadn't heard "Model - View - ViewModel" before. I frequently >heard "Model - View - Controller" though (Rails etc.).

It seems to me like both is exactly the same, since the "ViewModel" does get called "controller" in the examples.

What motivates the choice of the term "ViewModel" instead of "Controller"? Making it clearer that the controller connects view and model? >Or are there any differences?

MVC and MVVM are indeed different. I will refer you to a great StackOverlow post below, but first let me drop a couple of differences off the top of my head.

  1. MVC does NOT have a Declarative Binder as part of its structure. MVVM does.

  2. Since MVVM has a specific place for storing the STATE of the view, i.e., the ViewModel, it's more suitable to the UI-based development. MVC does NOT store UI (or View) state. It CAN do that, but the pattern does not dictate it.

  3. MVC (without some other way to store the state of your view) will therefore have a harder time differentiating between RAW data (that was pulled from some server call) and actual UI state. Note, UI state data and raw data is NOT one and the same. There is the Model (raw) data and then there is the ViewModel (data that REPRESENTS the VIEW as it is right now: after you clicked something, after you choose something, after you type something, etc.)

Ralin, hopefully, this last point will answer your dislike of mixing data and behavior. In this case, it wouldn't make much sense to separate view data from view behavior.

Although, I am not 100% sure why mixing data and behavior is inherently a bad thing. Yes, you want to separate them sometimes, but not always. Lots of approaches use that, e.g., JavaServer Faces uses backing beans which contain data and behavior. In general, in class-based OOP, it's fairly standard to have data and behavior in one object.

Here is the link I referred to in the beginning of the post:

http://stackoverflow.com/questions/667781/what-is-the-difference-between-mvc-and-mvvm

Look at the answer with the green checkmark.

Hopefully, this clears things up a bit.

To add to Yaakov's response, another differentiating point is that controllers in a "traditional" MVC pattern could potentially return different views. For instance, based on the values inside of a controller, the controller could return view1.html if user is logged in while returning view2.html if the user is unauthenticated. Or an endpoint could return data in json or xml.

ModelView vs Controller clarification

All the theory of week #1 goes that we are going to use Angular, which is MV* framework, as MVVM and explains everything about the ViewModel, never mentions a controller at all. But then when we start the practice with the example application we are now using a controller (not ModelView), and the quiz question suggests that the Controller is actually the ModelView. Ok, I understand MVC and began to understand MVVM, but at the moment I am completely confused. Are we using Angular as MVC or as MVVM? Can someone explain better the differences or relations or concepts between the Controller and ViewModel in Angular?

Hey Ralin,

Excellent question. I'll do my best to not confuse this topic further.

If we set aside angular for a second and strictly look at MVVM, we see that it is a common design pattern. A design pattern gives us, as Yaakov mentioned, a "cookie cutter" approach to solving common development problems. Really as developers, we are constantly fighting complexity. Our goal should be to write software that is clear, concise and maintainable. These patterns strive to bring structure and sanity to our applications so that we can solve them in the cleanest method possible.

Ok, so enough about patterns. Back to MVVM. This pattern can be implemented differently by various frameworks in completely different ways. However, the core fundamental idea behind them would be the same. That is that we would have

  • Model
  • View
  • ViewModel

Ok, so on its own, this may seem rather abstract. Let's bring in angular and add some context. So how does angular implement MVVM? Let's assume we have the following code.

<html ng-app="UserApp">
  
  <!-- View that will be bound to controller -->
  <body ng-controller="UserController">
    <h1>My name is {{fullname}}</h1>
    
    My favorite programming language is <input type="text" ng
        -model="favoriteProgrammingLanguage"/>
    
  </body>
</html>
angular
// Create Module
.module('UserMapp', [])
// Create controller
.controller('UserController', function($scope) {
  
  // Set the name
  $scope.fullname = 'John Flores';
  $scope.favoriteProgrammingLanguage = '';
})

Here we have an application that contains a view, a controller and some data that we want to render. We also included an input field that allows a user to type their favorite programming language. So let's break down this page to show the components used and how they related to MVVM.

Model - represents and holds raw data/ Data/Business Logic

Our data here is 'John Flores' which is being stored in $scope. We also have a variable that is storing a users favorite programming language. This would be another piece of raw data would is associated to the model. We are not concerned with how this data will be rendered. We just care that we are storing it and that we have access to it. You will also notice that the description in the lecture for Model also includes business logic. Well, to illustrate that, suppose we have an object called userService that is responsible for returning to us a users full name. So let's take that the controller we defined above and slightly modify it to include the following:

// Do not focus on the controller but just the data that is 
// enclosed in the controller
//.controller('UserController', function($scope, userService) {
  
  // Set the name
  $scope.fullname = userService.getFullname('john', 'flores');
  $scope.favoriteProgrammingLanguage = '';
//})

Note that for the sake of explaining this, I've commented out the controller portion. I would like you to focus on the aspects that deal strictly with the raw data.

Again, we are dealing with data only. The userService.getFullname() method may contain business logic that ensures data is formatted properly or that we are inputting the appropriate content. Or maybe it will fetch data from an external resource using ajax. Again, we are strictly focused on storing and handling the raw data and not worried about where it will be placed in a view.

View - UI/Presentation/ UI declares events

We now arrive at the view. In an abstract sense, the view only displays the data it is given. In our example above, the view is the following:

<body ng-controller="UserController">
    <h1>My name is {{fullname}}</h1>
    
    My favorite programming language is <input type="text" ng
        -model="favoriteProgrammingLanguage"/>
    
  </body>

Recall from the Lecture 3 video that for the view, Yaakov describes the following points regarding the view:

In a web app, it's just the HTML and CSS Only displays data that it is given Never changes data Decoratively broadcasts events So we see that we have HTML (and css if we have stylesheets or inline styles) to render our styling. We see that we have the expression{{fullname}} that is displaying ourfullname from the value that is being stored in the scope. Notice that the view doesn't actually change any data. Yes, we have an ng-model attribute on an input field that updates the favoriteProgrammingLanguage scope value to whatever the user is typing but that is being achieved by the declarative bindings that may broadcast events. In the template, since we've placed , we are telling angular to bind the expression in the ng-model="favoriteProgrammingLanguage" attribute to the scope. So as the user types, the ng-model attribute is performing logic that is updating the favoriteProgrammingLanguage. However, the html template doesn't care how that's happening. All it knows is that it will render whatever value is inside of favoriteProgrammingLanguage.

ModelView - Representation of the state of the view

We've reached the last point. The ModelView. Recall that the ModelView represents the state of a particular view. What does that mean? So angulars implementation of ModelView is achieved with ng-controllers. The previous question above asked,

"Can someone explain better the differences or relations or concepts between the Controller and ViewModel in Angular"

The answer is that angulars concept of a controller is how you implement the ViewModel concept. Let's show the controller code again:

.controller('UserController', function($scope, userService) {
  
  // Set the name
  $scope.fullname = userService.getFullname('john', 'flores');
  $scope.favoriteProgrammingLanguage = '';
})

The controller provides us the mechanism to manipulate data, inject objects that can do "stuff" for us, listen for events that are transmitted from the view (say a click handler, ng-model, or maybe a hover event) or call other functionality for additional business logic processing. In this case, our controller is using the userService object to delegate the responsibility of returning a properly formatted users fullname.

Lastly, how is this magic happening? This is where the Declarative Binder comes into play. This crucial process, which is handled by angular, binds the model (our data in $scope, userService) in our ViewModel (controller) to the view (our html template). As a result, we can easily do things like bind a controller to a view in our templates.

Provider vs Factory and everything in between

Angular module Provider vs Factory

So Yaakov said that .provider is what is actually used by Angular behind the scenes when we use .factory or .service. It is the most >verbose and flexible because we can configure it at the application bootstrapping and not only in the run time.

But I don't actually get why is it any better than the .facotry?

I can in fact set defaults in the factory function definition just like the provider function definition. I just have to provide the defaults in an object or an array and pass it to the returned service either way.

And concerning the .config module method that comes after .provider, I don't get why and when should we use it? Yaakov used it to modify the default value he had set in the provider function, and this is just equal to going to the provider function and simply changing the defaults ourselves.

Since both the provider function definition and the .config module method set the initial values of my service before my application starts, why should I use .config? Moreover, why should I use a .provider instead of a .factory in the first place?

Great questions.

"But I don't actually get why is it any better than the .facotry?

I can in fact set defaults in the factory function definition just like the provider function definition. I just have to provide the defaults in an object or an array and pass it to the returned service either way."

What differentiates the configuration aspect of a provider from a factory is that a provider can only be configured before any of the service/factory/controller objects are constructed. And these configured items will then be accessible to the underlying service that is associated to the provider.

Let's take a built-in angular service that you all will encounter soon which is the $httpProvider which exposes an $http service. This is a service that lets you perform ajax calls to a remote server. It has methods such as $http.get(), $http.post(), etc.

There are numerous configuration properties on the $httpProvider but I'll focus on headers. Let's say in your app, that with every single http request you make, you need to pass an http header called x-professor. One way of achieving that is to manually set it when you make ajax calls with the $http service. For instance

function ($http) {
  var config = {
    headers: {
      'x-professor' : 'yaakov'
    }
  };
  $http.get('some/path/data.json', config);
}

Ok, cool, that's simple. Now, imagine if your app had 20, 50, 100 http requests sprinkled throughout the app. This would be cumbersome. What if this header name changed? What if you needed to add/remove some?

So one solution is to set some default headers on the $httpProvider so that those changes will then be applied any time you use the $http service. We're essentially setting some global configuration items that will be used by $http. This would only be applied once in the module.config ()

function ($httpProvider) {
  $httpProvider.defaults.headers.common['x-professor'] = 'yaakov';
}

With this, you are free to use the $http without having to explicitly define that header with every remote call. That header will automatically be applied when you use the service. Now, there's no 'magic' in this. The angular team has incorporated the logic to store those default headers and then add them any time you use the $http service methods.

"And concerning the .config module method that comes after .provider, I don't get why and when should we use it? Yaakov used it to modify the default value he had set in the provider function, and this is just equal to going to the provider function and simply changing the defaults ourselves."

Another thing to remember is that it's good to make your services/factories as configurable as possible. Yes, there are times where it makes sense to just put defaults directly in the factory. That is totally fine! Using a provider can come in handy when you've written something that you want to expose as an API for others to use. If you have built a lot of reusable components, others may have different requirements so providing the ability to configure them makes those components much more decoupled. Also, you may expose a service that users can't actually modify so their only way of configuring a service may be through a provider.

Like in the case of the $httpProvider, imagine if the angular team decided to add a default header x-angular-rocks as part of their $http service. This while funny, may not be appropriate for everyone. You'd want the ability to control what defaults get applied when you use the $http service because you don't have direct access to modifying the $http service code.

Is it possible for a service that's generated by a factory to be shared across multiple controllers?

In the example of factory, I see that the service is initialized inside the controller code, I'm wondering if there is another way to initiate the service outside the controller so that the same instance of the service is shared across 2 or more controllers.

Specifically is it possible to code lecture20 example with factory instead of service?

Yes, with a service, you don't return anything in the function constructor. You simply add everything to this like the following:

.service('MyService', function () {
  var service = this;
  
  service.addItem = function (itemName, quantity) {
    // blah
  };
  
  service.getItems = function (){
    // blah
  };  
  
})

To translate this to a factory, you can do the following:

.factory('MyService', function () {
  var service = {};
  
  service.addItem = function (itemName, quantity) {
    // blah
  };
  
  service.getItems = function (){
    // blah
  };  
  
  return service;
  
})

We can create a service object that we add some methods to and then return. This object is what then is injected into your controllers and other services.

Service Provider

Is the service provider a singleton class? If so how will you configure multiple services with diff configurations. In other words, if you take your two shopping cart example, how can you use the provider to achieve that?

Correct, a service provider is a singleton. Using a provider, you can modify configurable properties within a config() block.

angular
  .module('app')
  .config(function (serviceProvider) {
    serviceProvider.config.maxItems = 10;
  });

Remember that this configuration will take place once before all services/factories/controllers/components are instantiated. In this case, we're saying that the maximum number of items that has been set for this provider is 10. This can be looked at as a way to apply some 'global' configuration for this provider.

Now, if you wanted to configure each individual instance of a shopping list, you would need to modify the object that gets returned in the $get() of the provider since this is what gets injected into your controllers.

angular
  .module('app')
  .provider('shoppingService', function () {
    var provider = this;
    provider.config.maxItems = 10;
    
    // This is what gets injected into controllers, services, etc
    provider.$get = function () {
      return function (name) {
        return new ShoppingListService(provider.config.maxItems, 
            name)
      }
    };
    
  });

With that change, when we inject shoppingService into a controller,

controller('MyShoppingController', function (shoppingService) {
  var service = shoppingService('Favorite List');
})

Notice, we pass in Favorite List. This is the name parameter in the function() that's being return within the $get. So here, we can configure the name that gets passed into each instance of a ShoppingListService while also using the global configuration of maxItems that was applied in the config() block.

Service factory - two methods

The service factory has two ways to generate a service - a factory function or an object literal. Why are there two different ways? When do we use one over the other?

Hey Kollivakkam!

Great question. So let's take a step back and look at angular factories from a high level view. In angular.factory(), you're able to return anything you want. That's one core differentiation from using angular.service().

for instance, the following are examples of factories

angular
.module('factoryExamples')
// I'm a boring factory
.factory('johnFactory', function () {
   return "john";  
})
.factory('schoolService', function () {
  var service = {};
  
  service.getSchools = function () {
    return ['School 1', 'School 2'];
  };
  
  return service;
})
.factory('userService', function () {
  
  return {
    users: ['User 1', 'User 2'],
    adminUsers: ['Admin 1', 'Admin 2']
  };
  
})
.factory('userGenerator', function (User) {
  
  return function (username) {
    return User(username);
  };  
})

Yaakov was just explaining the different ways that you could go about creating a factory that returns a new configured service. Whether you return a function() or an object literal that contains a function that creates new objects is entirely up to you. It just depends on how much functionality you want to expose with the factory.

For instance, let's say the built-in $log service was not good enough and you created a new logger called SuperLogger (this is made up. Just assume it has info(), debug() etc methods). Now let's assume you wanted to instantiate one with a different name every time you used it. So you can create a factory (SuperLoggerFactory) that will instantiate new instances of SuperLogger every time the return function is called.

So one way Yaakov proposed is the following:

.factory('SuperLoggerFactory', function (SuperLogger) {
  
  // Return a function that must be called with a logger name
  return function (loggerName) {
    return new SuperLogger(loggerName);
  };
  
})

Now, let's say we wanted to use it in a controller

.controller('UserController', function (SuperLoggerFactory) {
  
  // Here SuperLoggerFactory is pointing to the function above
  var logger = SuperLoggerFactory("UserLogger");
  
  logger.info('Hello world');
  
})

Notice here how SuperLoggerFactory points to a function that accepts a parameter loggerName that returns a new instance of SuperLogger Everytime SuperLoggerFactory(loggerName) is called, it will return a new instance of SuperLogger.

Now, you asked, well, what about object literals? So, let's say, you still wanted the new logger but maybe you wanted to track how many times a SuperLogger has been created. Let's modify our code so that we have an object literal that contains two methods, one to create a new SuperLogger instance and increment the count, and then another method that simply returns that count.

.factory('SuperLoggerFactory', function (SuperLogger) {
  // Tracks how many times a SuperLogger has been instantiated
  var count = 0;
  
  return {
    // creates new instances of SuperLogger and increments the
    // number of times it's been instantiated.
    create: function(loggerName) {
      count++;
      return new SuperLogger(loggerName);
    },
    
    // Returns how many times it's been instantiated
    createCount: function() {
      return count;
    }
  };
  
})

Now to use this, you can do the following:

.controller('UserController', function (SuperLoggerFactory) {
  var logger = SuperLoggerFactory.create("UserLogger")
  logger.info('Hello world 2');
  
  // I can also print out how many times it's been instantiatd.
  console.log(logger.createCount());  
})

Notice how with both methods, we're still able to create a new configured SuperLogger instance. In the second instance, we returned an object so that we could attach additional behavior. So it's just up to you how much you want to expose.

Cross Origin Error

This occurs because you are not running your application from a web-server. Simply opening the file from a web-browser will usually not work. Run the application using browser-sync.