Cannot Get Upvoting to Work from Thinkster MEAN Stack Tutorial - javascript

I have been going through the this MEAN Stack tutorial at following along however I have changed my code to use Controller as 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:
index.html
<html>
<head>
<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>
</head>
<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 }}
</div>
<form ng-submit="main.addPost()">
<input type="text" ng-model="main.title"></input>
<button type="submit">Add Post</button>
</form>
</body>
</html>
app.js
/*global angular*/
/*jslint white:true*/
angular
.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;
};
});

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.

Just add main.incrementUpvotes(post) instead of incrementUpvotes(post).
angular
.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;
};
});
<html>
<head>
<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>
</head>
<body ng-app="flapperNews" ng-controller="MainCtrl as main">
<div ng-repeat="post in main.posts | orderBy: '-upvotes'">
<span ng-click="main.incrementUpvotes(post)">^</span>
{{ post.title }} - upvotes: {{ post.upvotes }}
</div>
<form ng-submit="main.addPost()">
<input type="text" ng-model="main.title"></input>
<button type="submit">Add Post</button>
</form>
</body>
</html>

Related

How to index variables in html using angularjs

I am new to angularjs and still learning the language.
I created a select box in an html and I want to populate it with a variable in my controller.
I am able to get the variable in the html using {{variablename}}, but I am not able to get the sub objects within it.
Please see my code here.
You can see that it displays "repeatSelect" in the html but if i try to index an object within it, it doesn't show.(getID is always empty)
Controller has a $scope variable as follows
controller('ExampleController', ['$scope', function($scope) {
$scope.repeatSelect = null;
$scope.data = {
availableOptions: [
{id: '1', name: 'Option A'},
{id: '2', name: 'Option B'},
{id: '3', name: 'Option C'}
],
};
}]);
In the html code,
<tt>repeatSelect = {{repeatSelect}}</tt><br/>
<tt>getID = {{repeatSelect.id}}</tt><br/>
repeatSelect works fine, but repeatSelect.id doesn't.
Please guide
Use ng-options built in directive instead https://docs.angularjs.org/api/ng/directive/ngOptions.
It is designed specifically to work with HTML select lists and has plenty of powerful options.
<div ng-controller="ExampleController">
<label> Repeat select: </label>
<select ng-options="option as option.name for option in data.availableOptions track by option.id" ng-model="repeatSelect"></select>
<hr>
<tt>repeatSelect = {{repeatSelect}}</tt><br/>
<tt>getID = {{repeatSelect.id}}</tt><br/>
</div>
Please see plunkr here: http://plnkr.co/edit/kIcH5fF7suhN0rhVDez8?p=preview

Issues with AngularJS Service

I´m currently starting to learn Angular.JS and worked with a few tutorials like this this one. I followed the Steps but tryed to improve the code by saving single parts like controllers or services in seperate .js files because I heared this is a good habit. That was no problem and all worked fine. But when I came up with the Service which provides my posts I also tried to write some sort of API in the Service because i learned in another tutorial to do so.
There comes the Problem: The API to get my list of posts is working fine but if I try to send data due to an addPost function to the API it doesn´t work at all.
So can you maybe help me to find out what the problem is because I want to implement a Backend to the post-Service later on and want all $http requests at one place.
EDIT
The code-sample below is running now and you can see the problem if you try to add a post. The code stops after/during the addPost() function in the MainCtrl because the "clearing" of the HTML-form isn´t happening.
here you can find my code:
var app = angular.module('flapperNews', []);
app.controller('MainCtrl', function($scope, postService){
$scope.test = "Hello, World!";
$scope.posts = postService.getPosts();
$scope.addPost = function(){
if(!$scope.title || $scope.title === '') { return; }
postService.addPost({title: $scope.title, link: $scope.link, upvotes: 0});
//This code above was my try ith the API in posts.js
// $scope.posts.push({
// title: $scope.title,
// link: $scope.link, // this whole part is from the tutorial and works fine
// upvotes: 0
//});
$scope.title = '';
$scope.link = '';
};
$scope.incrementUpvotes = function(post) {
post.upvotes += 1;
};
});
app.factory('postService', function() {
var srv = {};
srv._posts = [
{title: 'post 1', link: '', upvotes: 5},
{title: 'post 2', link: '', upvotes: 2},
{title: 'post 3', link: '', upvotes: 15},
{title: 'post 4', link: '', upvotes: 9},
{title: 'post 5', link: '', upvotes: 4}
];
srv.getPosts = function() {
return angular.copy(srv._posts);
};
srv.addPost = function(post) { //function to put the new Post in the Array
srv._posts.push(post);
};
return {
getPosts: function() {
return srv.getPosts();
},
addPost: function(post) { //the public API to put the post in the Array
srv.addPost(post);
}
};
});
<!DOCTYPE html>
<html>
<head>
<meta charset="Utf-8">
<title>FlapperNews</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<style> .glyphicon-thumbs-up { cursor:pointer } </style>
</head>
<body ng-app="flapperNews" ng-controller="MainCtrl">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="page-header">
<h1>Flapper News</h1>
</div>
<div ng-repeat="post in posts | orderBy:'-upvotes'">
<span class="glyphicon glyphicon-thumbs-up"
ng-click="incrementUpvotes(post)"></span>
{{post.upvotes}}
<span style="font-size:20px; margin-left:10px;">
<a ng-show="post.link" href="{{post.link}}">
{{post.title}}
</a>
<span ng-hide="post.link">
{{post.title}}
</span>
</span>
</div>
<form ng-submit="addPost()"
style="margin-top:30px;">
<h3>Add a new post</h3>
<div class="form-group">
<input type="text"
class="form-control"
placeholder="Title"
ng-model="title"></input>
</div>
<div class="form-group">
<input type="text"
class="form-control"
placeholder="Link"
ng-model="link"></input>
</div>
<button type="submit" class="btn btn-primary">Post</button>
</form>
</div>
</div>
<!-- Scripts -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src=scripts/app.js></script>
<script src=scripts/controller/main.js></script>
<script src=scripts/service/posts.js></script>
</body>
</html>
Once you push the data to the service you should update $scope.posts
$scope.addPost = function(){
if(!$scope.title || $scope.title === '') { return; }
postService.addPost({title: $scope.title, link: scope.link, upvotes: 0});
$scope.posts = postService.getPosts();
// or edit postService.addPost so you can make
/* $scope.posts = postService.addPost({title: $scope.title, link: scope.link, upvotes: 0}); */
$scope.title = '';
$scope.link = '';
};

Can I dynamically filter an array of names with Angular with a user input character?

I am presently playing around with some Angular examples that I found over here - https://code.angularjs.org/1.3.10/docs/guide/filter
I am using the built in filterFilter filter (he he) and I have read that I may need to create my own filter (so I can pass params) however seems like I should not have to if the filter will just re-run. The idea I am working on is to try to let the user define the character that is used to filter the array.
My present code looks like so:
var myApp = angular.module('FilterInControllerModule', [])
.controller('FilterController', ['filterFilter', function(filterFilter) {
this.filterChar = 'a';
this.array = [
{name: 'Tobias'},
{name: 'John'},
{name: 'Jack'},
{name: 'Frank'},
{name: 'Desmond'},
{name: 'Allan'},
{name: 'Margie'}
];
this.filteredArray = filterFilter(this.array, this.filterChar);
}]);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Filters Extended Example 1</title>
</head>
<body ng-app="FilterInControllerModule">
<div ng-controller="FilterController as ctrl">
Filter by character: <input ng-model="ctrl.filterChar" type="text" maxlength="1"><br><br>
<div>
All entries:
<div ng-repeat="entry in ctrl.array">{{entry.name}}</div>
</div><br>
<div>
Entries that contain an "{{ctrl.filterChar}}":
<div ng-repeat="entry in ctrl.filteredArray">{{entry.name}}</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
If you run the code you will find that the two-way binding between the model and view is working between the controller and the expression {{ctrl.filterChar}} however the controller does not seem to re-evaluate the actual filtering. Why might this be?
The problem occurs because your function filterFilter is only evaluated once. To re-evaluate it and get the filtered array on every digest cycle, you can do this:
In your controller, use a function:
var that = this;
this.filterMyArray = function() {
return filterFilter(that.array, that.filterChar);
}
...and in your html
<div ng-repeat="entry in ctrl.filterMyArray()">{{entry.name}}</div>
You can use Array.filter() in your controller:
var myApp = angular.module('FilterInControllerModule', [])
.controller('FilterController', ['filterFilter', function(filterFilter) {
this.filterChar = 'a';
this.array = [
{name: 'Tobias'},
{name: 'John'},
{name: 'Jack'},
{name: 'Frank'},
{name: 'Desmond'},
{name: 'Allan'},
{name: 'Margie'}
];
this.filterFilter = function() {
return this.array.filter(function(element, index, sourceArray) {
return element.name.indexOf(this.filterChar) !== -1;
}.bind(this));
}
}]);

Remove object from array when attribute 0 or below - Angular

I have a controller that lets a user edit an object. Part of this decrements an attribute of the object – the quantity of my model. When the model's quantity reaches 0 or below, ideally I'd like to delete the whole object.
HTML
<div ng-app='basket'>
<div ng-controller='BasketController as basket'>
<div class='product' ng-repeat='product in cart.products'>{{ product.name }} Count: {{ product.quantity }} <a ng-click='product.quantity = product.quantity - 1'>Remove</a></div>
</div>
</div>
JS
(function(){
var app = angular.module('basket', []);
var cart;
app.controller('BasketController', function($scope, $http){
$scope.getTimes=function(n){
return new Array(n);
};
$scope.cart = {};
$scope.cart.products = [{
'name':'item 1',
'quantity':3
},{
'name':'item 2',
'quantity':3
},{
'name':'item 3',
'quantity':3
}];
});
})();
Live demo
http://codepen.io/EightArmsHQ/pen/bNBmXm
So for instance, in the above if you click 'remove' again and again on the first object, when you get to 0 I'd like to just have an array like the following:
$scope.cart.products = [{
'name':'item 2',
'quantity':3
},{
'name':'item 3',
'quantity':3
}];
You could just write a remove method to check for quantity and remove the item from the list.
In your controller:-
$scope.remove = function(product){
var products = $scope.cart.products;
product.quantity -= 1;
if(!product.quantity){
/*Splice the object from the array based on the index*/
products.splice(products.indexOf(product), 1);
}
}
and on click just call it as:
<a ng-click='remove(product)'>Remove</a>
Demo
Change your link to:
<a ng-click='remove(product)'>Remove</a>
And add the following method to the controller:
$scope.remove = remove;
function remove(product) {
$scope.cart.products.splice($scope.cart.products.indexOf(product), 1);
}

Beginners issue with scope in Angularjs

I am working with angular at frontend and django as backend, writing smll stuff to learn angular.I am trying to display variable i defined in scope.I am loading the html as partial onclick of anchor tag.
Here is my app.js which have my router settings:
var blogApp = angular.module('blogApp',['ngRoute'])
blogApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
});
blogApp.config(['$routeProvider',function($routeProvider){
$routeProvider.
when('/login', {
templateUrl: 'static/partials/login.html',
}).
when('/register',{
templateUrl:'static/partials/register.html',
}).
when('/shoppingcart', {
templateUrl:'static/partials/shopping_cart.html',
controller:'CartController'
}).
otherwise({
redirectTo: '/'
});
}]);
Here is my CartController defined:
blogApp.controller('CartController', function($scope){
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
});
i have included the js which contains my CartController and the one that has defined my routing.My partial (called shopping_cart.html) is like this:
<div ng-controller='CartController'>
<h3>Your Cart</h3>
<div ng-repeat="item in items">
<span>{% item.title %}</span>
<input ng-model="item.quantity">
<span> {% item.price|currency %} </span>
<span> {% item.price * item.quantity | currency %} </span>
<button ng-click="remove($index)">Remove</button>
</div>
</div>
Intriguing problem is that item.quantity is displayed correctly but item.title, item.price etc is displayed as it is in my output.Point is if it displays item.quantity this implies that item variable is containing the variable defined, even if i put item.title in input in place of item.quantity it displays the variable, But {% item.title %} on its own never gets displayed.Plese point me to my mistake, and kindly explain why is this happening.
It looks like a typo.
In your config you override the start and end symbol for interpolation to '{$' and '$}', but in your template you use '{%' and '%}' instead. Replace the % with $ and it should work.

Categories