I am attempting to use ng-repeat with AngularJS but I am not getting the result of my scope in my DOM. Can anyone see the issue? I have been trying to troubleshoot this for hours and hours now and "players" is always null.
Here is my html:
<body ng-controller="CoachCtrl" >
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
<div class="mdl-tabs__tab-bar">
Starks
Lannisters
Targaryens
</div>
<div class="mdl-tabs__panel is-active" id="coach" >
<p>Number of players {{ players.length }}</p>
<table class="table">
<tr>
<th>Firstname
</th>
<th>Lastname
</th>
<th>Tryout Date
</th>
</tr>
<tr ng-repeat="kid in players" >
<td>{{ kid.firstname }}
</td>
<td>{{ kid.lastname }}
</td>
<td>{{ kid.tryout_date }}
</td>
</tr>
</table>
</div>
</div>
and here is my js:
'use strict';
angular.module('myApp.coach', ['ngRoute', 'firebase'])
// Declared route
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/coach', {
templateUrl: 'coach/coach.html',
controller: 'CoachCtrl'
});
}])
// Home controller
.controller("CoachCtrl", ["$scope", "$firebaseAuth", "$location",
function($scope, $firebaseAuth, $location) {
var ref = new Firebase("https://intense-heat-2545.firebaseio.com");
var authData = ref.getAuth();
if(authData){
console.log("User is "+authData.uid+" and is logged in with "+authData.provider);
var league = new Firebase("https://intense-heat-2545.firebaseio.com/users/"+authData.uid+"/league");
league.on("value", function(snapshot){
console.log("League ID = "+snapshot.val());
var leagueVal = snapshot.val();
var playerlist = new Firebase("https://blahblah.firebaseio.com/"+leagueVal+"/players");
$scope.players = [];
$scope.players.push({firstname:'John', lastname:'B', tryout_date:'2015-11-30'});
$scope.players.push({firstname: 'Marty', lastname: 'B', tryout_date: '2015-12-01'});
playerlist.on("child_added", function(snapshot){
//console.log("players ="+snapshot.val());
var player = snapshot.val();
console.log("Firstname ="+player.firstname);
var first = player.firstname;
var last = player.lastname;
var tyd = player.tryout_date;
console.log('player data ='+first+last+tyd);
$scope.players.push({ firstname: first, lastname: last, tryout_date: tyd });
var len = $scope.players.length;
for (var i = 0; i < len; i+=1){
if (1 === len){
console.log("player name = "+$scope.players[i].firstname);
}
}
console.log("players len ="+$scope.players.length);
}, function(error){
console.log("Error getting player info: "+error.code);
});
console.log("players ="+$scope.players[1].firstname+" len= "+$scope.players.length);
}, function(error){
console.log("Erro ="+error.code);
});
} else {
console.log("User is not logged in.");
$location.path('/signin');
}
}
]);
Three things.
The with the regular Firebase SDK Angular doesn't know when to run $digest.
Use $firebaseArray() rather than manipulating your own.
Use resolve() in the router to inject the user with $firebaseAuth().$waitForAuth().
-
var rootRef = new Firebase("https://<my-firebase-app>.firebaseio.com");
var leagueRef = rootRef.child("users").child(authData.uid).child("league");
// read it one time
leagueRef.once('value', function(snap) {
var leagueVal = snapshot.val();
var playerList = rootRef.child(leagueVal).child("players");
// $firebaseArray() will synchronize child events into an array
// Each update will know how to update $digest as well, which
// will keep the view updated.
$scope.players = $firebaseArray(playerList);
});
Your controller code would be greatly simplified if you use resolve in the router.
.constant('FBURL', '<my-firebase-app>')
.service('RootRef', ['FBURL', Firebase)
.factory('Auth', function($firebaseAuth, RootRef) {
return $firebaseAuth(RootRef);
})
.factory('UserLeague', function(RootRef) {
return function(uid) {
var leagueRef = RootRef.child("user").child(uid).child("league");
var deferred = $q.defer();
leagueRef.once(function(snap) {
deferred.resolve(snap.val());
});
return deferred.promise;
}
})
.config(function($routeProvider) {
$routeProvider.when('/coach', {
templateUrl: 'coach/coach.html',
controller: 'CoachCtrl',
resolve: {
leagueVal: function(UserLeague, Auth) {
var authData = Auth.$getUser();
return UserLeague(authData.uid);
},
authData: function(Auth) {
return Auth.$waitForAuth();
}
}
});
})
.controller("CoachCtrl", function($scope, leagueVal, authData, RootRef) {
// no need to check for a user because authData is injected
// use the resolved leagueVal to create a ref
var playerList = RootRef.child(leagueVal).child("players");
// synchronize the players to an array
$scope.players = $firebaseArray(playerList);
});
Related
I have fetched docs from my database. now what I want to do is based on different doc.statuses, want to display different messages. How to go about it?
<p>
<a target="_blank" style="margin-right:5px" ng-repeat="doc in homeCtrl.getDocs(docType.objectId)" href="{{doc.document.url}}">
<div ng-if="doc.status == 'approved'">Hello</div>
<span class="label label-success"><i class="glyphicon glyphicon-hourglass"></i>{{doc.status}}</span>
<br>{{doc.comment}}<br>
</a>
</p>
so the ng-if in the div is not working. How to refer to the doc used in ng-repeat?
EDIT:
I still can't figure out. My controller looks like this(I'm using a Parse backend)
The controller looks like this
class HomeController {
constructor($scope, $state, itemsService, $location, $ionicLoading) {
'ngInject';
const self = this;
self.UserDocument = Parse.Object.extend('UserDocument');
self.$scope = $scope;
self.$scope.user = {};
self.$scope.objUserDocument = {};
self.$scope.userDocumentTypes = [];
self.loading = $ionicLoading;
// self.$scope.docs = [];
$scope.$on("$ionicView.beforeEnter", function(event, data) {
if (!Parse.User.current()) {
$location.url('/signup');
} else {
// self.$scope.user = window.buddy;
self._getDocumentTypes();
// self.$scope.user.firstName = objUser.get('firstName');
// self.$scope.user.lastName = objUser.get('lastName');
// console.log(objUser.get('docs'));
}
});
window.homeCtrl = this;
}
getDocs(id) {
const self = this;
if (self.$scope.user.docs && self.$scope.user.docs.length) {
var docs = self.$scope.user.docs.filter(d => id == d.docType.objectId);
return docs;
} else {
return [];
}
}
}
export default HomeController;
````
ng-if works fine:
HTML:
<div ng-repeat="item in items">
<span>{{item.id}}</span>
<span ng-if="item.status === 'approved'">approved</span>
<span ng-if="item.status === 'rejected'">rejected</span>
</div>
JS:
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
$scope.items = [
{status: 'approved', id: 1},
{status: 'approved', id: 2},
{status: 'rejected', id: 3},
{status: 'rejected', id: 4}
]
}])
Here is the plnkr: http://plnkr.co/edit/wIX46rZHBNGHdv1S3LKg?p=preview
Try to move the function that retrieves the docs inside you controller and pass the result to ng-repeat instead.
ng-repeat="doc in docs"
and
.controller('YourController', function($scope) {
$scope.docs = getDocs(id);
});
I am trying to route my page to another page once the controller is accessed but its not working. I can route the first two pages but the third one is not working. Can someone help me on this.
This is my routing code:
$routeProvider', function ($routeProvider) {
$routeProvider.
when('/category', {
//templateUrl : 'js/partials/course-list.html',
controller : 'CategoryController'
}).
when('/category/:categoryid', {
templateUrl : 'js/partials/film-list.html',
controller : 'MovieController'
}).
when('/actor/:filmid', {
templateUrl : 'js/partials/actor-list.html',
controller : 'ActorController'
}).
otherwise({
redirectTo : '/'
});
}
Currently my ActorController is not working. Once i click on the movies it should show the actor of the films but in my case its not working
This is my partial html file for the movie-list.html
<section>
<h3>{{movieCount}}</h3>
<table>
<tr data-ng-repeat="movie in movies" data-ng-click="selectFilm($event,movie)" style="cursor: pointer;">
<td>{{movie.title}}</td>
</tr>
<strong>{{successMessage}}</strong>
</table>
And this is my controller code
).controller('ActorController',
[
'$scope',
'dataService',
'$routeParams',
function ($scope, dataService, $routeParams){
$scope.actors = [ ];
$scope.actorCount = 0;
var getActors = function (moviecode) {
dataService.getActors(moviecode).then(
function (response) {
$scope.actorCount = response.rowCount + ' actors';
$scope.actors = response.data;
$scope.showSuccessMessage = true;
$scope.successMessage = "Actor Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
// only if there has been a courseid passed in do we bother trying to get the students
if ($routeParams && $routeParams.filmid) {
console.log($routeParams.filmid);
getActors($routeParams.filmid);
}
}
]
)
This is the selectFilm() code from the MovieController
$scope.selectedFilm = {};
$scope.selectFilm = function ($event,movie) {
$scope.selectedFilm = movie;
$location.path('/actor/' + movie.film_id);
}
This is the movie controller code
).controller('MovieController',
[
'$scope',
'dataService',
'$routeParams',
'$location',
function ($scope, dataService, $routeParams, $location){
$scope.movies = [ ];
$scope.movieCount = 0;
var getMovies = function (moviecode) {
dataService.getMovies(moviecode).then(
function (response) {
$scope.movieCount = response.rowCount + ' movies';
$scope.movies = response.data;
$scope.showSuccessMessage = true;
$scope.successMessage = "Movies Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
$scope.selectedFilm = {};
$scope.selectFilm = function ($event,movie) {
$scope.selectedFilm = movie;
$location.path('/actor/' + movie.film_id);
// $window.location.href = '/actor/' + movie.film_id
console.log(movie.film_id);
}
// only if there has been a courseid passed in do we bother trying to get the students
if ($routeParams && $routeParams.categoryid) {
console.log($routeParams.categoryid);
getMovies($routeParams.categoryid);
}
}
]
)
I solved the problem by myself wher first of all the $location variable was not defined in the function and later on the movie object dont have the film_id so I had to readjust my SQL query to make it work. After changing the SQL query i can route my page now.
I want to increment the data via Button click or scroll.
I have a function which loads the data after button click loadDataQueryDB(param, param, param). With that, I am passing data to MongoDB query.
Well how can I increment my var limit = 50; + 5; after each button click?
Node.js
router.get('/load', function(req, res) {
var skip = 0;
var limit = 50;
var place_val = req.query.place;
var category_val = req.query.category;
var specCategory_val = req.query.specCategory;
if(category_val, specCategory_val, place_val){
Experiences
.find({category : category_val, city:place_val})
.lean()
.skip(skip)
.limit(limit)
.exec(function(err, docs_accommo) {
res.send(docs_accommo);
console.log("First");
});
}
});
Angular.js
app.controller('loadData', ['$scope', '$http', '$window', '$upload', '$rootScope',
function($scope, $http, $window, $upload, $rootScope) {
$scope.loadDataQueryDB = function(place_value, category_value, specCategory_value){
console.log(place_value);
console.log(category_value);
console.log(specCategory_value);
$scope.datafront = [];
var options = {
place : place_value,
category: category_value,
specCategory : specCategory_value
};
$http.get('/load',
{params: options})
.success(function(data) {
$scope.datafront = data;
});
};
});
HTML
<div ng-click="loadDataQueryDB(place, category, specCategory)">
<div ng-repeat="x in datafront | limitTo:? track by x._id" ng-cloak>
{{x}}
</div>
</div>
<button class="btn btn-default" style="float:right; margin-bottom:20px;"/>
Something like the code below, using services and http promises, the data returned form the server its on promise.data.
app.controller('loadData', ['$scope', '$http', '$window', '$upload', '$rootScope','dataService',
function($scope, $http, $window, $upload, $rootScope, dataService) {
$scope.loadDataQueryDB = function(place_value, category_value, specCategory_value){
console.log(place_value);
console.log(category_value);
console.log(specCategory_value);
$scope.datafront = [];
var params = {
place : place_value,
category: category_value,
specCategory : specCategory_value
skip : $scope.skip,
limit : $scope.limit
}
dataService.getData(params).then(function(promise){
$scope.dataFront = promise.data;
//Increment the limit by ten
$scope.limit =+ 10;
})
};
});
app.module('dataService').factory('dataService',['$http',function ($http) {
var service = {};
factory.getData= function (params) {
var promise = $http({
method: 'GET',
url: '/load',
params: params
});
return promise;
}
return service;
}]);
You should have only the gets that return views on the router and the rest of the get and post calls on a service, at least I develop like that and its more confortable.
I'm trying to create a sigle-page app that contains shop list, in every shop card is the link to another view that contains table with products.
A shop looks like:
shop = {
id: 1,
name: "foo",
description: 'bar',
products: [item1, itemn];
};
app.js:
angular
.module('lightpointTestApp', [
'ngCookies',
'ngRoute',
'ui.sortable'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl'
})
.when('/products/:shopID', {
templateUrl: 'views/products.html',
controller: 'ProductsCtrl'
})
.otherwise({
redirectTo: '/'
});
});
Main.html view where are shop list:
<h3>Shop list</h3>
<div class="row shopsContainer" ui-sortable ng-model="shops">
<div class="col-lg-3 shopCard" ng-repeat="shop in shops">
<button class="btn close cardClose" ng-click="removeShop($index)">×</button>
<div class="cardNumber">{{ shops.indexOf(shop) + 1 }}</div>
<div class="cardHeader">{{ shop.name }}</div>
<div class="cardBody">
{{ shop.address }}<br />
{{ shop.hours }}<br />
View {{ shop.products.length }} products
</div>
</div>
</div>
<div class="row">
<input type="text" ng-model="newShop.name" placeholder="Shop name" class="col-lg-3" />
<input type="text" ng-model="newShop.address" placeholder="Shop address" class="col-lg-3" />
<input type="text" ng-model="newShop.hours" placeholder="Shop hours" class="col-lg-3" />
<button class="btn btn-primary col-lg-3" type="button" ng-disabled="!newShop.name || !newShop.address || !newShop.hours" ng-click="addShop()">Add Shop</button>
</div>
</span>
</div>
</div>
products.js - controller for products page
angular.module('lightpointTestApp')
.controller('ProductsCtrl', function ($scope, $routeParams, shops) {
$scope.shopList = shops;
$scope.shop = {};
$scope.getShop = function (id) {
for (var i = 0; i < $scope.shopList.length; i++) {
if ($scope.shopList[i].id === id) {
return $scope.shopList[i];
}
}
return null;
};
var shopID = $routeParams.shopID;
$scope.shop = $scope.getShop(shopID);
})
products.html where is the table with products
<h2>{{ shop.name }}</h2>
<table class="table table-hover">
<tr>
<th>Product Name</th>
<th>Product Description</th>
</tr>
<tr ng-repeat="product in shop.products">
<td> {{ product.name }} </td>
<td> {{ product.description }} </td>
</tr>
</table>
The problem is that products.html doesn't bind with products.js and show something like {{shop.name}} and an empty table.
P.S. I think that products.js isn't correct, but I tried everything to do it well.
Thanks.
You have a parameter shops in ProductsCtrl, but there is nothing that will pass a value for it, so it is going to be null. You set the value of $scope.shopList to it, and then try to iterate over a NULL array, so you get an exception.
You can store the values of shops in a service, and then pass them around your app via injection. You can initialize their values within main.js, or within the service itself, and then the values will be available if you inject them into ProductsCtrl, something like
angular.module('lightpointTestApp')
.controller('ProductsCtrl', ['$scope', '$routeParams', 'shopsService',
function ($scope, $routeParams, shopsService) {
$scope.shopList = shopService;
$scope.shop = {};
$scope.getShop = function (id) {
for (var i = 0; i < $scope.shopList.length; i++) {
if ($scope.shopList[i].id === id) {
return $scope.shopList[i];
}
}
return null;
};
var shopID = $routeParams.shopID;
$scope.shop = $scope.getShop(shopID);
}]);
shopsService could look something like
angular.module('lightpointTestApp')
.service('shopsService', function() {
return [
// add whatever fields you need here from code in main.js
{ name: 'shop1', address: 'addr1' },
{ name: 'shop2', address: 'addr2' }
];
});
Where are your shop objects coming from? You are passing in shop, in products.js but not referencing it in the code. You should also use $q to use promises for async data. Also use the filter() function rather than a for loop to find the shop by shopId.
Are you hitting an API with shops or storing a local json for now?
With angular, you should separate your data logic manipulation in a factory or service as such:
productService.js
angular.module('lightpointTestApp')
.factory('shopService',function($http, $q){
var shops = [];
return {
getShops: function () {
var deferred = $q.defer();
$http.get('<path to product.json or api>').success(function(data){
shops = data;
deferred.resolve(data);
})
return deferred.promise;
},
getShopById: function(shopID) {
var deferred = $q.defer();
deferred.resolve(shops.filter(function(chain){
return chain.id === shopID;
})[0]);
return deferred.promise;
}
}
});
product.js
angular.module('lightpointTestApp')
.controller('ProductsCtrl', function ($scope, $routeParams, $q,shopService) {
$scope.shopList = [];
$scope.shop = {};
var shopID = $routeParams.shopID;
shopService.getShops.then(function(shops){
$scope.shopList = data;
})
$scope.getShopById = function(shopID) {
shopService.getShopById(shopID).then(function(shop){
$scope.shop = shop;
});
}
});
At the moment, my Angular app looks like this:
Factory in app.js
StoreApp.factory("DataService", function () {
// create store
var myStore = new store();
// create shopping cart
var myCart = new shoppingCart("Store");
// return data object with store and cart
return {
store: myStore,
cart: myCart
};
});
controller.js
function storeController($scope, $http, $routeParams, $location, DataService) {
$scope.store = DataService.store;
$scope.cart = DataService.cart;
// use routing to pick the selected product
if ($routeParams.productUrlTitle != null) {
$scope.product = $scope.store.getProduct($routeParams.productUrlTitle) || $scope.store.getHero($routeParams.productUrlTitle);
}
$scope.predicate = '-price';
$scope.store.isCart = $location.path() == "/cart";
}
In store.js (below) is where my issue is — currently this.products[] takes inline assignments. I need this to instead load an external JSON file (also below). I've tried several things from including/passing the promise to var myStore = new store();, to actually including $http.get() paired with .then() inside of store.js — to no avail.
store.js
function store() {
this.products = [
new product("USD", 20, "https://foo.jpg", "Name", "Description"),
new product("USD", 20, "https://bar.jpg", "Name", "Description"),
];
}
store.prototype.getProduct = function (urlTitle) {
for (var i = 0; i < this.products.length; i++) {
if (this.products[i].urlTitle == urlTitle)
return this.products[i];
}
return null;
}
payload.json
[
{
"currency": "usd",
"cost": 1000,
"image_url": "https://whatever.domain/someimage.jpg",
"id": "xyz",
"name": "A title",
"description": "Some details"
},
...
]
For those interested, my project is based on this: A Shopping Cart Application Built with AngularJS.
Many thanks in advance.
Update
I was able to accomplish what I wanted, but I'm not certain it's the best (Read: correct) way to. In short, I added a new factory called "InventoryService" that I pass to my controller.
app.js
// New Factory Added
StoreApp.factory('InventoryService', ['$http', '$rootScope',
function ($http, $rootScope) {
var inventory = [];
return {
getInventory: function () {
return $http.get('http://localhost/ShoppingCart/payload.json').then(function (response) {
inventory = response;
$rootScope.$broadcast('handleInventoryService', inventory);
return inventory;
})
}
};
}
]);
controller.js
function storeController($scope, $http, $routeParams, $location, InventoryService, DataService) {
$scope.name = 'inventory';
(function () {
InventoryService.getInventory().then(function (inventory) {
$scope.inventory = inventory;
for (var i = 0; i < $scope.inventory.data.length; i++) {
if ($scope.inventory.data[i].id == '11ca3ea26f0e431eb996a401f292581f2') {
DataService.store.hero.push(
new product(
$scope.inventory.data[i].id,
$scope.inventory.data[i].image_url,
$scope.inventory.data[i].name,
$scope.inventory.data[i].description,
$scope.inventory.data[i].cost
)
);
} else {
DataService.store.products.push(
new product(
$scope.inventory.data[i].id,
$scope.inventory.data[i].image_url,
$scope.inventory.data[i].name,
$scope.inventory.data[i].description,
$scope.inventory.data[i].cost
)
);
}
}
// get store and cart from service
$scope.store = DataService.store;
$scope.cart = DataService.cart;
...
store.html partial
<div ng-include src="'partials/header.html'"></div>
<div ng-repeat="product in store.hero" class="row-fluid">
<div class="span12">
<div class="span4">
<a href="#/products/{{product.urlTitle}}">
<img class="img-polaroid" ng-src="{{product.image_url}}" title="{{product.name}}" />
</a>
</div>
<div class="span8">
<h1 class="tango-tang weight-100">
{{product.name}}
</h1>
<hr />
<div class="row-fluid">
<div class="span7">
<p>
{{product.description}}
</p>
</div>
<div class="span5">
<div class="well">
<h1 class="weight-300 text-center">
{{product.price | currency}}
</h1>
</div>
<button class="btn btn-success btn-medium btn-block" ng-click="cart.addItem(product.sku, product.image_url, product.name, product.price, 1)">
<i class="icon-plus"></i> Add to Cart
</button>
<a href="#/products/{{product.urlTitle}}" class="btn btn-block">
<i class="icon-list"></i> Details
</a>
</div>
</div>
</div>
</div>
</div>
As I outlined in the comment, the InventoryService isn't necessary in your case, $q and $http.get are enough.
Quoted from comments:
You may try to make products and hero both promises, later when HTTP responded, resolve two deferred objects at once.
Code:
App.factory('DataService', function($http, $q) {
function Store() {
var heroDeferred = $q.defer();
var productsDeferred = $q.defer();
this.hero = heroDeferred.promise;
this.products = productsDeferred.promise;
$http.get('/path/to/payload.json').success(function(data) {
var hero = [];
var products = [];
for (var i = 0, len = data.length; i < len; i++) {
var prod = data[i];
if (prod.id === 'xyz') {
hero.push(prod);
} else {
products.push(prod);
}
}
heroDeferred.resolve(hero);
productsDeferred.resolve(products);
});
}
Store.prototype.getProduct = function(urlTitle) {
return this.products.then(function(products) {
for (var i = 0; i < products.length; i++) { // MUST use products, it's the real value; this.products is a promise
if (products[i].urlTitle == urlTitle)
return products[i];
}
return null;
});
};
...
return {
store: new Store()
...
};
});
http://plnkr.co/edit/qff7HYyJnSdEUngeOWVb