Angular-ui-router causing infinite loop - angularjs

I'm trying to learn angular with this tutorial https://thinkster.io/tutorials/mean-stack/wiring-everything-up and I've hit a snag where opening the application runs into an infinite loop of calling the /posts url. I've checked my routes with postman and they are working as intended so I'm not sure why this isn't resolving correctly. Below is the 'posts' service, 'mainCtrl' controller, and the config. Could someone take a look and tell me if they see my error or if I need provide more of the code to help. Thank you.
Service
app.factory('posts', ['$http', function($http){
var o = {
posts:[]
}
o.getAll = function() {
return $http.get('/posts').success(function(data){
angular.copy(data, o.posts);
});
};
return o;
}])
Controller
app.controller('MainCtrl', ['$scope', 'posts', function($scope, posts){
$scope.test = 'Hello world!';
$scope.posts = posts.posts;
$scope.addPost = function(){
if(!$scope.title || $scope.title === '') { return; }
$scope.posts.push({
title: $scope.title,
link: $scope.link,
upvotes: 0,
comments: [
{author: 'Joe', body: 'Cool post!', upvotes: 0},
{author: 'Bob', body: 'Great idea but everything is wrong!', upvotes: 0}
]
});
$scope.title='';
$scope.link='';
}
$scope.incrementUpvotes = function(post) {
post.upvotes += 1;
};
}]);
Config
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url:'/home',
templateUrl:'/home.html',
controller:'MainCtrl',
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
})
.state('posts', {
url:'/posts/{id}',
templateUrl:'/posts.html',
controller:'PostsCtrl'
})
$urlRouterProvider.otherwise('home');
}])

I'm going to close this question with a solution that partially has worked for me. I say partially because I'm no longer experience an infinite loop but if someone comes across this question doing the tutorial they will eventually run into a new problem where clicking submit does create a new post but the data is not there. Upon a refresh you will get the result you are looking for, but I have not solved this new issue.
The solution to the infinite loop is to use this in the service function
return $http.get('/posts').then(function(data){
angular.copy(data.data, o.posts);
});
The success syntax the the tutorial calls for does not work.

Related

AngularJS: HTML partials ng-if not updating when $scope.loggedInUser is updated

I am new to the AngularJS community and was hoping someone could help me with the following issue.
I have created a light CMS system based on an incomplete tutorial and have filled in some of the pieces myself, but i cannot get the HTML partials to update when the $scope changes;
HTML partial (admin-login.html)
<div ng-if="loggedInUser">
Welcome {{loggedInUser}} | My Admin | Logout
</div>
my directive (directives.js)
directive('adminLogin', [
function() {
return {
controller: function($scope, $cookies) {
var user = $cookies.get('loggedInUser', {path: "/"});
$scope.loggedInUser = user;
},
templateUrl: 'partials/directives/admin-login.html'
};
}
])
my controller (controllers.js)
controller('AdminLoginCtrl', ['$scope', '$location', '$cookies', 'AuthService','$log','flashMessageService',
function($scope, $location, $cookies, AuthService, $log, flashMessageService) {
$scope.credentials = {
username: '',
password: ''
};
$scope.login = function(credentials) {
AuthService.login(credentials).then(
function(res, err) {
$cookies.put('loggedInUser', res.data);
$location.path('/admin/pages');
},
function(err) {
flashMessageService.setMessage(err.data);
$log.log(err);
});
};
}
])
The scope updates but i have to refresh the page to show or hide the admin-login.html.
Any help is greatly appreciated, thank you in advance.
$scope.loggedInUser won't be updated once you update cookie, you should watch for cookie change in directive and update $scope manually.
directive('adminLogin', [
function() {
return {
controller: function($scope, $cookies) {
$scope.$watch(function () {
return $cookies.get('loggedInUser', {path: "/"});
}, function (value) {
$scope.loggedInUser = value;
});
},
templateUrl: 'partials/directives/admin-login.html'
};
}
])

how does is it possible that post is accessible in my controller?

how is it possible that i can do $scope.post = post; and that i did not have to do $scope.post = posts.post;
app.js
angular.module('flapperNews', ['templates','ui.router'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
})
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts/_posts.html',
controller: 'PostsCtrl',
resolve: {
post: ['$stateParams', 'posts', function($stateParams, posts) {
return posts.get($stateParams.id);
}]
}
});
$urlRouterProvider.otherwise('home');
}])
postCtrl.js
angular.module('flapperNews')
.controller('PostsCtrl', [
'$scope',
'posts',
'post',
function($scope, posts, post){
$scope.post = post;
$scope.addComment = function(){
if($scope.body === '') { return; }
$scope.post.comments.push({
body: $scope.body,
author: 'user',
upvotes: 0
});
$scope.body = '';
};
}]);
That's pretty simple.
You wrote in declaration of your posts state:
resolve: {
post: ['$stateParams', 'posts', function($stateParams, posts) {
return posts.get($stateParams.id);
}]
}
You inject in this function your posts service and make a call (to some API, whatever). This function returns promise. Property resolve in state declaration at first will wait for resolving all promises which has been written in there. And after that it will inject the results of all resolved promises into the controller.
Lets take a look on your controller:
angular.module('flapperNews')
.controller('PostsCtrl', [
'$scope',
'posts',
'post',
function($scope, posts, post){
$scope.post = post;
...
};
}]);
On injection section we can see posts - injected service. Also post - which will be a resolved promises which came from resolve section of your state.
It means that you don't have to call your posts service again, because it was already called before instantiating the controller. You probably don't need to inject this service at all.
I hope I clarified that and it will help you to understand.

AngularJS factory method not found in Rails project

I'm currently working on this AngularJS Tutorial: Learn to Build Modern Web Apps with Angular and Rails https://thinkster.io/angular-rails/ which I think is a great resource showing how to build Rails Web Apps with AngularJS.
So far, I've completed over two thirds of the tutorial successfully, but now I'm facing some issues with factory services. When I hit the post button to create new post, I get the following error:
Error message
angular.js?body=1:11608 TypeError: undefined is not a function
at Scope.$scope.addPost (http://0.0.0.0:3000/assets/home/mainCtrl.js?body=1:23:24)
It points to the code below in mainCtrl.js file:
posts.create({
title: $scope.title,
link: $scope.link,
});
the entire mainCtrl.js file:
angular.module('flapperNews')
.controller('MainCtrl', ['$scope', 'posts', function ($scope, posts) {
$scope.test = 'Hello world!';
$scope.posts = posts.posts;
$scope.posts.push({
title: $scope.title,
link: $scope.link,
upvotes: 0,
comments: [
{ author: 'Joe', body: 'Cool post!', upvotes: 0 },
{ author: 'Bob', body: 'Great idea but everything is wrong!', upvotes: 0 }
]
});
$scope.addPost = function () {
if (!$scope.title || $scope.title === '') { return; }
posts.create({
title: $scope.title,
link: $scope.link,
});
$scope.title = '';
$scope.link = '';
};
$scope.incrementUpvotes = function(post) {
posts.upvote(post);
};
}]);
In the above controller, if the addPost function is replaced with the one that was used in the tutorial before the factory create method was introduced, then it works fine.
Working code:
$scope.addPost = function(){
if(!$scope.title || $scope.title === '') { return; }
$scope.posts.push({
title: $scope.title,
link: $scope.link,
upvotes: 0
});
$scope.title = '';
$scope.link = '';
};
So somehow the factory's posts.create method is causing the issue (although posts.posts is accesible).
Below is post.js file which o.create method is causing the current issue
angular.module('flapperNews').factory('posts', ['$http',
function($http){
var o = {
posts: []
};
return o;
o.getAll = function() {
return $http.get('/posts.json').success(function(data){
angular.copy(data, o.posts);
});
};
o.create = function(post) {
console.log("o.create");
return $http.post('/posts.json', post).success(function(data){
o.posts.push(data);
});
};
o.upvote = function(post) {
return $http.put('/posts/' + post.id + '/upvote.json')
.success(function(data){
post.upvotes += 1;
});
};
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
}
]);
app.js file
angular.module('flapperNews', ['ui.router', 'templates'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl'
})
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts/_posts.html',
controller: 'PostsCtrl'
})
$urlRouterProvider.otherwise('home')
}]);
If someone knows what the underlying issue here, then please give advice. Many Thanks :-)
My rails version 4.0.2 and I'm using Linux Ubuntu 12.04
Some thoughts, if I can't get the factory methods working, then I might put those methods directly in controllers to resolve the issue, hopefully :-)
You returned the object too soon, the other functions didn't register (since they are function expressions, and not function declarations, they didn't get hoisted). Move your return o; to the end and it will work:
app.factory('posts', ['$http',
function($http){
var o = {
posts: []
};
o.getAll = function() {
return $http.get('/posts.json').success(function(data){
angular.copy(data, o.posts);
});
};
o.create = function(post) {
alert('In create!');
console.log("o.create");
return $http.post('/posts.json', post).success(function(data){
o.posts.push(data);
});
};
o.upvote = function(post) {
return $http.put('/posts/' + post.id + '/upvote.json')
.success(function(data){
post.upvotes += 1;
});
};
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
return o;
}
])
See this working Fiddle.
Your resolve statement should go in the home state in your app.js. As the tutorial says, "By using the resolve property in this way, we are ensuring that anytime our home state is entered, we will automatically query all posts from our backend before the state actually finishes loading."
App.js:
angular.module('flapperNews', ['ui.router', 'templates'])
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
resolve: {
postPromise: ['posts', function(posts){
return posts.getAll();
}]
}
})
.state('posts', {
url: '/posts/{id}',
templateUrl: 'posts/_posts.html',
controller: 'PostsCtrl'
})
$urlRouterProvider.otherwise('home');
}]);

angular ui-router nested resolves are not resolving

I have nested ui-views which are both waiting on data from http request. In the following code i have simulated this with timeouts. If i set the timeouts any longer than 10 ms then my plunker wont load at all.
var myapp = angular.module('myapp', ["ui.router"])
myapp.config(function($stateProvider, $urlRouterProvider){
// For any unmatched url, send to /route1
$urlRouterProvider.otherwise("/route1")
try{
$stateProvider
.state('contacts', {
templateUrl: 'contacts.html',
controller: function($scope, service1){
$scope.title = service1.getData()
}
,resolve:{
titlePromise:function(service1){
return service1.myPromise
}}
})
.state('contacts.list', {
templateUrl: 'contacts.list.html',
controller: function($scope, service2){
$scope.contacts = service2.getData();
},
resolve:{
contactPromise:function(service2){return service2.myPromise}
}
});
}catch(e){
alert.log(e);
}
});
The services are defined as follows.
myapp.factory('service1',['$q', function($q){
var title = 'Not Yet';
var _promise = $q.defer();
setTimeout(function(){
title='My Contacts';
_promise.resolve(true);
},100);
return {
getData:function(){return title},
myPromise: _promise.promise
}
}]);
myapp.factory('service2',['$q','service1', function($q, service1){
var data = [];
var _promise = $q.defer();
setTimeout(function(){
service1.myPromise.then(function(){
data=[{ name: 'Alice' }, { name: 'Bob' }];
_promise.resolve(true);
})
},100);
return{
getData:function(){return data},
myPromise:_promise
}
}]);
I need service2 to wait until service 1 returns its data in order to fulfill its request.The way I have it set up does not seem to work. What have I done wrong? If there is a better way to set up my app any suggestions are appreciated. I have modified the ui-view nested view demo plunker her: plnkr
Have a read of how hierarchical resolves work:
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#wiki-what-do-child-states-inherit-from-parent-states
You don't need to wait for service 1 to complete inside service 2, but rather inject the results from the parent resolve into the child resolve function.

$http service executing multiple times in controller

I am using $http service to call server that return json data.howerver each time i request for the json data , the $http service is executing multiple times.dont know what went wrong.plz help. thanks in advance.below is my code.
var app = angular.module('Demo',[]);
app.config(function ($routeProvider) {
$routeProvider
.when('/users',
{
templateUrl: "users.html",
controller: "users"
}
).when('/users/new',
{
templateUrl: 'new.html',
controller : 'newuser'
}).when('/users/:id/edit',
{
templateUrl: 'edit.html',
controller: 'edit'
})
});
app.controller('users',function($scope,$http){
$scope.list_of_users = [];
$http.get('http://testing.com:3000 /users.json').success(function(data,status,header,config){
angular.copy(data,$scope.list_of_users)
})
});
app.controller('newuser',function($scope,$http,$location){
$scope.done = function(){
var data = {user: {name: $scope.name}};
$http.post("http://testing.com:3000/users.json",data).success(function(data,status,header,config){
$location.path('/users');
}).error(function(data,stauts,header,confi){
});
};
});
app.controller('edit',function($scope,$http,$routeParams,$location){
$scope.name="";
$http.get("http://testing.com/users/"+$routeParams.id +".json").success(function(data,status,header,config){
console.log(data);
$scope.name = data['user']['name'];
});
$scope.update = function(){
var data = {user: {name: $scope.name}};
$http.put('http://localhost:3000/users/$routeParams.id',data).success(function(data,status,header,config){
$location.path("/users");
}).error(function(data,status,header,config){
});
}
});
Most likly you defined your controller in both the routeProvider and in the actuall template ( ng-controller ). This makes it run multiple times. Remove one of them and try again.

Resources