I'm using Angular in an application. After getting a specific object (a movie in my case), I'm assigning the object to $scope ($scope.movie = response), so that I can use it in the view. The problem is that my view seems not to display anything I use in $scope. I've tried deleting everything and doing a dummy test like $scope=name="whatever" and when I use something like {{name}} in the view nothing is rendered. Have anyone faced this problem ? I've already searched for this error, and it seems like it would be a good idea to use $apply(). I've tried that and it didn't work. The function that fetches the data is below:
var app = angular.module('movies');
app.factory('Films', ['$resource',function($resource){
return $resource('/films.json', {},{
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
}]);
app.factory('Film', ['$resource', function($resource){
return $resource('films/:id.json', {}, {
show: {method: 'GET' },
update: { method: 'PUT', params: {id: '#id'} },
delete: { method: 'DELETE', params: {id: '#id'} }
});
}]);
app.controller('MoviesController', ['$scope', '$http', '$location', '$resource', '$routeParams', 'Films', 'Film', function($scope, $http, $location, $resource, $routeParams, Films, Film){
$scope.movies = Films.query();
$scope.user = document.getElementById('name').innerHTML; // Find a better way to interact with devise via angular
$scope.createMovie = function() {
$scope.movies = Films.query();
$http.get(
'/categories.json'
).success(function(data,status,headers,config){
$scope.categories = data;
}).error(function(data, status, headers, config){
alert("There was an error while fetching the categories on the database. Error " + status);
});
$location.path("/" + 'new').replace();
};
$scope.listMovies = function() {
$location.path("/").replace();
};
$scope.save = function(){
if($scope.form.$valid){
Films.create({film: $scope.movie}, function(){
$scope.form.$setPristine();
}, function(error){
alert("Movie not created");
});
}
};
$scope.deleteMovie = function(movie){
Film.delete(movie);
$scope.movies = Films.query();
};
$scope.viewDetails = function(movie){
$scope.name="ola";
alert(movie.id);
$location.path("/" + movie.id);
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
$scope.movie = Movie.get({filmId: movie.id});
$scope.movie.$promise.then(
function(response){
$scope.$apply();
$scope.movie = response;
console.log("filme e: " + response.name);
},
function(error){
console.log("request failed");
}
);
};
}]);
I had a look at your repository and I think where your problem is. You are trying to reuse the MoviesController in all of your routes. But AngularJS will create a new instance for every route and therefore you can't access your previous data because it will be destroyed.
So I would start by creating a separated controller for each view, so you can move the code of your viewDetails method to a new MovieDetailController. To have access to the movie id in this controller, you need to use the $routeParams service.
angular.module('movies').controller('MovieDetailController', MovieDetailController);
function MovieDetailController($scope, $resource, $routeParams) {
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
Movie.get({filmId: $routeParams.id}).then(
function(response) {
$scope.movie = response;
},
function(error){
console.log('request failed');
}
);
}
Change your route definition to use the new controller.
.when('/movies/:id', {
controller: 'MovieDetailController',
templateUrl: 'movie_details.html'
})
And now your viewDetails method in the MoviesController just need to redirect to the movie detail url.
$scope.viewDetails = function(movie) {
$location.path('/movies/' + movie.id);
}
I hope it works for you. Let me know when you try!
Related
I'm hoping this is an easy question to answer...
I'm trying to create a table that loads my json, then be able to click a row and load more details that pertain to the json object. When you click a row it should load additional details at the top of the page. The row clicking part is working fine. What I'm having trouble with is loading the initial object by default.
Below is an example of what I'm referring to:
var myItemsApp = angular.module('myItemsApp', [ ]);
myItemsApp.factory('itemsFactory', ['$http', function($http){
var itemsFactory = {
itemDetails: function () {
return $http({
url: "fake-phi.json",
method: "GET",
}).then(function (response) {
return response.data;
});
}
};
return itemsFactory;
}]);
myItemsApp.controller('ItemsController', ['$scope', 'itemsFactory',
function($scope, itemsFactory){
var promise = itemsFactory.itemDetails();
promise.then(function (data) {
$scope.itemDetails = data;
console.log(data);
});
$scope.select = function (item) {
$scope.selected = item;
}
}]);
http://embed.plnkr.co/6LfAsaamCPPbe7JNdww1/
I tried adding this after $scope.select, but got an error:
$scope.selected = item[0];
How do I get the first object in my json to load by default?
thanks in advance
Inside your promise resolve function assign the first item of the array, as a selected value:
promise.then(function (data) {
$scope.itemDetails = data;
$scope.selected = data[0];
console.log(data);
});
var myItemsApp = angular.module('myItemsApp', [ ]);
myItemsApp.factory('itemsFactory', ['$http', function($http){
var itemsFactory = {
itemDetails: function () {
return $http({
url: "fake-phi.json",
method: "GET",
}).then(function (response) {
return response.data;
});
}
};
return itemsFactory;
}]);
myItemsApp.controller('ItemsController', ['$scope', 'itemsFactory',
function($scope, itemsFactory){
var promise = itemsFactory.itemDetails();
promise.then(function (data) {
$scope.itemDetails = data;
$scope.selected = data[0];
console.log($scope.itemDetails);
console.log($scope.selected);
});
}]);
I am trying to set value in html page from angularjs controller.
I am getting value from web api in service but I have issue that I am always getting error:
TypeError: Cannot set property 'messageFromServer' of undefined
But I can't figure what am I doing wrong here. What am I missing?
On the html part I have:
<div ng-app="myApp" ng-controller="AngularController">
<p>{{messageFromServer}}</p>
</div>
In the controller I have:
var app = angular.module('myApp', []);
app.controller('AngularController', ['$scope', 'messageService', function ($scope, messageService) {
$scope.messageFromServer = "When I set it here it works!"
messageService.getMessage();
}]);
app.service('messageService', ['$http', function ($http) {
this.getMessage = function ($scope) {
return $http({
method: "GET",
url: "api/GetMessage",
headers: { 'Content-Type': 'application/json' }
}).success(function (data) {
$scope.messageFromServer = data;
console.log(data);
}).error(function (data) {
console.log(data);
})
};
}]);
Basically the problem is, you missed to $scope object to the service getMessage method. But this is not a good approach to go with. As service is singleton object, it shouldn't manipulate scope directly by passing $scope to it. Rather than make it as generic as possible and do return data from there.
Instead return promise/data from a service and then assign data to the scope from the controller .then function.
app.service('messageService', ['$http', function ($http) {
this.getMessage = function () {
return $http({
method: "GET",
url: "api/GetMessage",
headers: { 'Content-Type': 'application/json' }
}).then(function (response) {
//you could have do some data validation here
//on the basis of that data could be returned to the consumer method
//consumer method will have access only to the data of the request
//other information about request is hidden to consumer method like headers, status, etc.
console.log(response.data);
return response.data;
}, function (error) {
return error;
})
};
}]);
Controller
app.controller('AngularController', ['$scope', 'messageService',
function ($scope, messageService) {
$scope.messageFromServer = "When I set it here it works!"
messageService.getMessage().then(function(data){
$scope.messageFromServer = data;
});
}
]);
Don't use $scope in your service, just return the promise from $http.
var app = angular.module('myApp', []);
app.service('messageService', ['$http', function ($http) {
this.getMessage = function () {
return $http({
method: "GET",
url: "api/GetMessage",
headers: { 'Content-Type': 'application/json' }
});
};
}]);
app.controller('AngularController', ['$scope', 'messageService', function ($scope, messageService) {
messageService.getMessage().then(function(data) {
$scope.messageFromServer = data;
});
}]);
In this example you can unwrap the promise in your controller, or even better you can use the router to resolve the promise and have it injected into your controller.
app.config(function($routeProvider) {
$routeProvider.when('/',{
controller: 'AngularController',
templateUrl: 'views/view.html',
resolve: {
message: function(messageService) {
return messageService.getMessage();
}
}
});
});
Then in your AngularController, you'll have an unwrapped promise:
app.controller('AngularController', ['$scope', 'message', function ($scope, message) {
$scope.messageFromServer = message;
}]);
I want multiple controllers to be able to update a single view attached to one controller using a factory with $http.
My list view:
<div class="list" ng-repeat="image in images" ng-controller="controller1">
<div lass="item"><img src="{{image.url}}" /></div>
</div>
Service:
.factory("imageService", function($http) {
return {
getImages: function() {
return $http({
method: "get",
url: "http://example.com/images",
params: { user: window.localStorage['user_id'] }
})
}
}
});
Controller 1:
.controller('controller1', function($scope, imageService) {
window.localStorage['user_id'] = '101';
var handleSuccess = function(data, status) {
$scope.images = data;
};
imageService.getImages().success(handleSuccess);
})
This all works. When the app is loaded, the list immediately is populated with a list of images for user '101'.
In another controller, I want to be able to switch users and automatically re-poupulate the image list in the view from controller 1 with new images.
Controller 2:
.controller('controller2', function($scope, imageService) {
window.localStorage['user_id'] = '202';
imageService.getImages();
})
So controller 2 will run getImages() and I can see the $http request working via chrome dev tools / XHR. And I know why the list view attached to controller1 is not populating, but I dont know how to make it populate. I have tried moving the success callback into the service and setting a 'images' property on the service and a $scope.images in controller1, but no luck there.
How can I force the new list of images into the view attached to controller 1?
You should just manage a list into your service that you will bind to your controller1 :
.factory("imageService", function($http) {
var service = {};
service.images = {};
service.images.list = [];
service.getImages = function(userId) {
window.localStorage['user_id'] = userId;
return $http({
method: "get",
url: "http://example.com/images",
params: { user: userId }
}).success(function(data){
service.images.list = data
});
}
//at the initialization of the service, you launch the getImages once with the localStorage value.
service.getImages(window.localStorage['user_id']);
return service;
});
Then you can bind it like this in your controllers :
.controller('controller1', function($scope, imageService) {
$scope.images = imageService.images;
//then access it in the view with images.list
imageService.getImages(101);
})
.controller('controller2', function($scope, imageService) {
$scope.images = imageService.images;
//then access it in the view with images.list
imageService.getImages(202);
})
Note that using a sub object (images.list instead of images) is important.
If you want some more precise informations about why this sub object is needed you can read this answer on this subject
Hope it helped.
I think you can just use one controller. At some time point, say, a user clicks some button, reload the image lists and re-render the view to show the list.
For example, make the factory like:
.factory("imageService", function($http) {
return {
getImages: function(userId) {
return $http({
method: "get",
url: "http://example.com/images",
params: { user: userId }
});
}
};
});
And in the controller:
.controller('imageController', function($scope, imageService) {
function refreshImages (userId) {
imageService.getImages(userId).success(function(data) {
$scope.$apply(function () {
$scope.images = data;
});
});
}
refreshImages('101');
$scope.loadImages = function (userId) {
refreshImages(userId);
};
});
I am a Angular noob and having problems with binding a variable from one of my services to one of my controllers. I have read at least a dozen posts on the subject and nothing seems to be working for me.
Here is the controller:
app.controller('TeamController', ['$scope', '$modal', 'teamService', function ($scope, $modal, teamService) {
$scope.teamService = teamService;
$scope.selectedTeam = null;
$scope.selectTeam = function(teamId){
$scope.selectedTeam = teamService.getTeam(teamId, $scope.login.loginId);
};
}]);
Here is the service:
angular.module('teamService', [])
.service('teamService', function($http, $q){
this.selectedTeam = {teamId:-1, teamName:"Select a team", teamLocationName:"", teamDescription:"", teamManaged:false};
this.userTeams = [];
this.getTeam = function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
var promise = $http({
url: "/url-for-getting-team",
method: "POST",
data: postData
});
promise.success(function (data) {
if (data.status === "success") {
this.selectedTeam = data.response;
return data.response;
}
});
promise.error(function () { //TODO handle getTeam errors
return {};
});
};
this.getSelectedTeam = function(){
return this.selectedTeam;
};
});
And here is the template:
<div class="jumbotron main-jumbo" ng-controller="TeamController">
<h1>{{selectedTeam.teamName}}</h1>
</div>
I have tried binding to the getSelectedTeam function and the service variable itself. Do I need to set up a $watch function in the controller? Any assistance would be greatly appreciated.
EDIT:
I tried turning my service into a factory, which still did not help me, so then I looked at a provider that was properly working that I had already written in the application. I converted my "teamService" into a provider and finally worked like a charm. Thanks for the contributions guys.
Code from my new provider:
angular.module('teamService', [])
.provider('teamService', function () {
var errorState = 'error',
logoutState = 'home';
this.$get = function ($rootScope, $http, $q, $state) {
/**
* Low-level, private functions.
*/
/**
* High level, public methods
*/
var wrappedService = {
/**
* Public properties
*/
selectedTeam: {teamName:"Select a team"},
userTeams : null,
createTeam: function(loginId, name, description, locationName, managed){
var postData = {loginId:loginId, teamName:name, teamDescription:description, teamLocationName:locationName, teamManaged:managed};
var promise = $http({
url: "/create-team-url",
method: "POST",
data: postData
});
return promise;
},
getTeam: function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
var promise = $http({
url: "/get-team-url",
method: "POST",
data: postData
});
promise.success(function (data) {
if (data.status === "success") {
wrappedService.selectedTeam = data.response;
}
});
promise.error(function () { //TODO handle getTeam errors
wrappedService.selectedTeam = {};
});
},
getUserTeams: function(loginId) {
var postData = {loginId: loginId};
var promise = $http({
url: "/team-list-url",
method: "POST",
data: postData
});
return promise;
},
joinTeam: function(teamId, loginId){
var postData = {teamId:teamId, loginId:loginId};
var promise =$http({
url: "/join-team-url",
method: "POST",
data: postData
});
return promise;
},
getSelectedTeam: function(){
return wrappedService.selectedTeam;
}
};
return wrappedService;
};
});
As seen in my edit. I converted my service into a provider and all the changes seem to propagate to the view with no issues. I need to further analyze the difference between the factory, service, and provider in order to gain a higher understanding of what is going on here.
The main issue with the code is the way that promises are used. You can either correct that within the service, or handle it in the controller. As an example of the latter, you can re-write the above as:
Controller Code:
app.controller('TeamController', ['$scope', '$modal', 'teamService', function ($scope, $modal, teamService) {
$scope.teamService = teamService;
$scope.selectedTeam = null;
$scope.selectTeam = function(teamId){
teamService.getTeam(teamId, $scope.login.loginId).then(
function(result){
$scope.selectedTeam = result.data;
},
function(error){
console.log(error);
}
)
};
}]);
Service code:
angular.module('teamService', [])
.service('teamService', function($http, $q){
this.selectedTeam = {teamId:-1, teamName:"Select a team", teamLocationName:"", teamDescription:"", teamManaged:false};
this.userTeams = [];
this.getTeam = function(teamId, loginId) {
var postData = {teamId: teamId, loginId: loginId};
return $http({
url: "/url-for-getting-team",
method: "POST",
data: postData
});
};
this.getSelectedTeam = function(){
return this.selectedTeam;
};
});
You can also handle this in the service itself, but it requires a little more code. The key thing is that the getTeam call is asynchronous and needs to be handled using proper promise constructs.
var foo = angular.module('foo', []);
foo.factory('apiService', function($http){
'use strict';
return {
getItems: function() {
return $http.get('api/get-items')
.then(function(result) {
return result;
});
}
};
});
OutlastApp.controller('ItemListCtrl', function ($scope, apiService) {
'use strict';
apiService.getItems().then(function(result) {
$scope.items = result.data;
});
});
OutlastApp.controller('AddItemCtrl', function ($scope, $http, $rootScope, apiService) {
'use strict';
...
$scope.saveItem = function(item) {
$http({
url: 'api/add-item',
method: 'GET',
params: {
name: item.name,
description: item.description,
...
}
}).success(function(data, status, headers, config) {
// TODO: What to do?
});
};
});
As you can seem, I have setup $scope.items to get its contents from an AJAX call.
What I am trying to accomplish is that I want to try to update the $scope.items so that it will reflect the newly added item.
How do I do this? Is this the correct way to do some sort a two-way binding between API and scope variables?
Given what I can see here you need to probably share the "items" collection between the two controllers. In order to do this you can use a factory to store the items collection (approximately):
app.factory('SharedDataService', function () {
return {
items: []
};
});
After you have created this you can use inject it into both controllers and the "items" object will be shared between them. Then you can just push the new item after it is saved:
$http({
// run your call here
}).success(function(data, status, headers, config) {
// Here is where you push the new/saved item into the shared data:
SharedDataService.items.push(item);
});
That should really do it. Best of luck!
Video Tutorial - In case you haven't seen this concept before - as of now - John Lindquist has an excellent video tutorial here.