Tythyrn Tythyrn - 1 year ago 55
Javascript Question

Cannot Get Upvoting to Work from Thinkster MEAN Stack Tutorial

I have been going through the MEAN Stack tutorial at https://thinkster.io/mean-stack-tutorial#jumping-in-with-angular following along however I have changed my code to use controllerAs rather than having $scope like they do in their code.

I am stuck on the enabling upvotes portion. When I click it does not increase the number of upvotes and I am not sure why this happening.

Can anyone help resolve this? Here is my code:


<title>My Angular App!</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<script src="app.js"></script>
<body ng-app="flapperNews" ng-controller="MainCtrl as main">
<div ng-repeat="post in main.posts | orderBy: '-upvotes'">
<span ng-click="incrementUpvotes(post)">^</span>
{{ post.title }} - upvotes: {{ post.upvotes }}
<form ng-submit="main.addPost()">
<input type="text" ng-model="main.title"></input>
<button type="submit">Add Post</button>


/*global angular*/
/*jslint white:true*/
.module('flapperNews', [])
.controller('MainCtrl', function(){
'use strict';
var main = this;

main.posts = [
{title: 'post 1', upvotes: 5},
{title: 'post 2', upvotes: 2},
{title: 'post 3', upvotes: 15},
{title: 'post 4', upvotes: 9},
{title: 'post 5', upvotes: 4}

main.addPost = function(){
if(!main.title || main.title === '') {return;}
main.posts.push({title: main.title, upvotes: 0});
main.title = '';

main.incrementUpvotes = function(post) {
post.upvotes += 1;

Answer Source

The problem you're having is with ng-repeat. You need to change your code to $parent.incrementUpvotes(post) to make this work.

Here's why: ng-repeat creates a new child scope for each iteration, but you don't have full access to everything you might need. This is due to how angular copies properties into the child scope. In order to access the scope that actually contains a definition for incrementUpvotes (the controller scope), you need to move up into the parent scope first. Alternatively you could probably do main.incrementUpvotes(post) to accomplish the same thing since you're aliasing the controller.

You can see a more detailed explanation of what happens when angular creates a child scope, and why certain properties are not inherited here https://github.com/angular/angular.js/wiki/Understanding-Scopes

What happens is that the child scope gets its own property that hides/shadows the parent property of the same name. This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works. New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view and ng-include all create new child scopes, so the problem often shows up when these directives are involved.