I am trying to upload a file from the server but the file is not showing up. The call to the server is initiated from this segment of code:
'use strict';
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: ['Phone',
function PhoneListController(Phone) {
this.phones = Phone.query();
this.orderProp = 'age';
console.log(this.phones);
}
]
});
Which calls this service:
'use strict';
angular.
module('core.phone').
factory('Phone', ['$resource',
function($resource) {
return $resource('phones/:phoneId.json', {}, {
query: {
method: 'GET',
params: {
phoneId: 'phones'
},
isArray: true
}
});
}
]);
The return codes are 200 and subsequently 304 which indicate a successful upload. But the console.log statement in the earlier code segment does not display the phones in the console.
To log the result, use the $promise property of the $resource object:
angular.
module('phoneList').
component('phoneList', {
templateUrl: 'phone-list/phone-list.template.html',
controller: ['Phone',
function PhoneListController(Phone) {
this.phones = Phone.query();
this.orderProp = 'age';
//console.log(this.phones);
//USE $promise property
this.phones.$promise.then(function(phones) {
console.log(phones);
}).catch(function (error) {
console.log("ERROR " + error.status);
});
}
]
});
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
By using the .then method of the $promise property, the $q service delays the console.log until the data has returned from the server.
Related
I'm new to AngularJS and I'm trying to modify this code to wrap my head around the conventions:
https://github.com/flatlogic/angular-material-dashboard/blob/master/src/app/components/services/MessagesService.js
I'm modifying it to use a REST service to fetch messages instead of using the messages array.
Here's the MessageService code:
(function(){
'use strict';
angular.module('app')
.service('messagesService', [
'$scope', '$http',
messagesService
]);
function messagesService($scope,$http){
return {
loadAllItems : function() {
//return $q.when(messages);
return $http({
method: 'GET',
url: 'http://localhost:3000/messages',
})
.then(function(data) {
console.log(data);
return data;
})
}
};
}
})();
But I'm getting an error about the scope:
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- messagesService
As Matthew Cawley said, services cannot access scope. Nor can you return data from loadAllItems() the way you are.
This is probably what you want with a controller example:
(function() {
'use strict';
var app = angular.module('app');
app.service('messagesService', ['$http', messagesService]);
function messagesService($http) {
return {
loadAllItems: function() {
//This will make the get request and return the promise that $http sends back.
return $http({
method: 'GET',
url: 'http://localhost:3000/messages',
});
}
};
};
app.controller('testCtrl', ['$scope', "messagesService", function($scope, messagesService) {
//Onload
messagesService.loadAllItems().then(function(data) {
console.log(data); //Your data from the messageService
}, function(err) {
console.log(err); //Error from the messageService
});
}]);
}());
$http returns a promise that can be accessed in your controller. You can then set your variable in scope and make the data accessible to the view.
Ex:
$scope.items = data; within testCtrl.
Take all of your references to $scope out, you can't inject scope into a service.
I need to mock the method of service in the controller. I know how to mock simple service like service.method, but not like this one. I dont know how to mock "action.user.update". If i tried to spy on it, i received an error 'Cannot read property 'update' of undefined'.
JsFidleDemo
My service:
.service('action', ['$http', '$q', function ($http, $q) {
var service = {};
service.user = {
update: function (data, config) {
return service.run({
name: config.name,
call: $http({
method: "POST",
url: "/user/edit",
data: data
}),
success: config.success,
error: config.error
});
}
};
return service;
}]);
You got half way there
$provide.value('action', action);
where action should be an object you create in unit tests
i.e.
action = {
user: {
update: jasmine.createSpy('action.user.update')
}
}
$provide.value('action', action);
and then in test
scope.saveUser()
expect(action.user.update).toHaveBeenCalled()
updated fiddle
http://jsfiddle.net/3oavnmev/1/
I am new in AngularJS and I have got this problem. I have got defined service citiesService with method addCity:
.service('citiesService', ['$resource', function($resource){
this.addCity = function(city) {
var cityItem = $resource("server/?module=cities&action=add", {}, {save: {method: "POST", isArray:true}});
return cityItem.save({
city: city
});
};
}])
It works fine, the new city was successfully added into DB via the PHP script, but I don't know, how to return server response. Server returning response like:
$output = [];
$output[] = ["success" => "added to database"];
echo json_encode($output);
and then I have got this controller:
.controller('citiesAddCtrl', function($scope, $modalInstance, citiesService) {
// save addCity form (modal)
$scope.saveForm = function() {
if($scope.city.name) {
$scope.a = citiesService.addCity($scope.city);
}
}
})
but I really don't know, how to display server JSON response. When I try something like console.log($scope.a), It shown empty array, but as you can see, the server response is in the right debug menu:
Can you help me to solve this problem please? I read some Stackoverflow topics and tried some edits, which are described here, but nothing works for me.
Since save returns a promise, you could access the response as following (untested):
.controller('citiesAddCtrl', function($scope, $modalInstance, citiesService) {
// save addCity form (modal)
$scope.saveForm = function() {
if($scope.city.name) {
citiesService.addCity($scope.city).$promise.then(function(response) {
$scope.a = response
});
}
}
})
Why don't you use simply $http which has a clear promise structure?
$http({
method: 'POST',
url: "server/?module=cities&action=add",
data: $scope.city
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
See docs at $http
I have a service in angular JS which is defined as follows:
'use strict';
angular.module('Offering.AccountService', [])
.service('Account', function($http, config) {
var account = this;
account.accountDetails = {};
this.getAccountDetails = function(){
var request = $http({
method: 'GET',
url: config.accountURL,
headers: {
'X-Parse-Application-Id': config.applicationID,
'X-Parse-REST-API-Key': config.restAPIKey,
}
})
.success(function(data, status, headers, config) {
console.log('Successfully aqquired account details');
account.accountDetails = data.results[0];
})
.error(function(data, status, headers, config) {
console.warn(status);
});
}
});
This service calls out to an endpoint to retrieve some data, I am calling this method in the run function of the main module as is shown below:
'use strict';
angular.module('Offering', [
'routerRoutes'
])
.run(function(Account){
Account.getAccountDetails();
})
In my controller, I have a variable called balance, in which I want to store the value retrieved from the service, as is shown in the code below:
'use strict';
angular.module('Offering.HomeController', [])
.controller('HomeCtrl', function(Account) {
this.data = {
message: "Account Details",
updateBalance: function() {
Account.getAccountDetails()
},
balance: Account.accountDetails.balance
}
// this.$watch(
// function () { return Account.accountDetails; },
// function (data) {
// $scope.data.balance = data.balance
// },
// true
// );
})
The data from the service is returned after the controller data object has been set up and thus the balance field is not getting populated. If I use the $scope.watch approach that is commented out, then the data object gets populated properly. However, I was under the assumption that this need not be done as there is a two way data binding between the service and the controller. Any help is greatly appreciated. Thanks !
The .run function doesn't stop to wait for the getAccountDetails to return a value. And so, when you assign this.data.balance = Account.accountDetails.balance, you are essentially doing this: this.data.balance = undefined.
As per comment above, the typical way is to have getAccountDetails() to return a promise, like so:
this.getAccountDetails = function(){
return $http({...})
.then(function(response){
console.log("successfully returned balanace", response.data);
return response.data;
});
}
Then, in the controller:
var vm = this;
Account.getAccountDetails()
.then(function(accountDetails){
vm.balance = accountDetails.balance;
});
You can not put a watch on the function which is returning promise, better you could put watch on this.data, Their is different way of put watch while using this keyword inside controller.
CODE
$scope.$watch(angular.bind(this, function () {
return this.data;
}), function (newVal, oldVal) {
// now we will pickup changes to newVal and oldVal
});
Hope this could help you , Thanks.
I'm trying to make a single $http request to get one of my JSON files and use the data across all my controllers.
I saw on egghead.io how to share data across multiple controllers, and I've also read this StackOverflow question: "Sharing a variable between controllers in angular.js".
However, the answers there don't use the $http module. When using $http, the controllers don't have the data to work on, and by the time the response is received it's already too late.
I then found the method $q.defer and this question on StackOverflow: "AngularJS share asynchronous service data between controllers"
The solution posted there works fine, BUT it has two issues:
Each controller will trigger the $http request to obtain the same data already used in another controller; and,
If I try to manipulate the data received I have a then function.
Below you can see my code:
controllers.js
'use strict';
/* Controllers */
function appInstallerListCtrl($scope, Data) {
$scope.apps = Data;
}
function appInstallerDetailCtrl($scope, $routeParams, Data) {
$scope.appId = $routeParams.appId;
$scope.apps = Data;
console.log($scope.apps); // <-- then function
console.log(Data); // <-- then function with $vv data returned but I can't access it
for (var i in $scope.apps) // <--- no way, baby!
console.log(i);
}
app.js
var app = angular.module('appInstaller', []);
app.factory('Data', function($http, $q) {
var defer = $q.defer();
$http.get('apps.json').then(function(result) {
defer.resolve(result.data.versions.version);
});
return defer.promise;
});
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/app', {templateUrl: 'partials/app-list.html', controller: appInstallerListCtrl}).
when('/app/:appId', {templateUrl: 'partials/app-detail.html', controller: appInstallerDetailCtrl}).
otherwise({redirectTo: '/app'});
}]);
What I'd like to have is that when launching the app, the $http request will be performed and the response will be used throughout the app across all controllers.
Thanks
I like to store my data in the service, and return a promise to the controllers, because usually you need to deal with any errors there.
app.factory('Data', function($http, $q) {
var data = [],
lastRequestFailed = true,
promise;
return {
getApps: function() {
if(!promise || lastRequestFailed) {
// $http returns a promise, so we don't need to create one with $q
promise = $http.get('apps.json')
.then(function(res) {
lastRequestFailed = false;
data = res.data;
return data;
}, function(res) {
return $q.reject(res);
});
}
return promise;
}
}
});
.controller('appInstallerListCtrl', ['$scope','Data',
function($scope, Data) {
Data.getApps()
.then(function(data) {
$scope.data = data;
}, function(res) {
if(res.status === 500) {
// server error, alert user somehow
} else {
// probably deal with these errors differently
}
});
}]);
Any callbacks that are registered after a promise has been resolved/rejected will be resolved/rejected immediately with the same result/failure_reason. Once resolved/rejected, a promise can't change (its state). So the first controller to call getApps() will create the promise. Any other controllers that call getApps() will immediately get the promise returned instead.
Since you are using a promise, to access the data returned by promise use the callback syntax
function appInstallerDetailCtrl($scope, $routeParams, Data) {
$scope.appId = $routeParams.appId;
Data.then(function(returnedData) {
$scope.apps=returnedData;
console.log($scope.apps);
for (var i in $scope.apps)
console.log(i)
});
}
Make sure this
defer.resolve(result.data.versions.version);
resolve returns array, for the above code to work. Or else see what is there in data and ajust the controller code.
I found the way not sure weather it is a best approach to do it or not.
In HTML
<body ng-app="myApp">
<div ng-controller="ctrl">{{user.title}}</div>
<hr>
<div ng-controller="ctrl2">{{user.title}}</div>
</body>
In Javascript
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope, $http, userService) {
userService.getUser().then(function(user) {
$scope.user = user;
});
});
app.controller('ctrl2', function($scope, $http, userService) {
userService.getUser().then(function(user) {
$scope.user = user;
});
});
app.factory('userService', function($http, $q) {
var promise;
var deferred = $q.defer();
return {
getUser: function() {
if(!promise){
promise = $http({
method: "GET",
url: "https://jsonplaceholder.typicode.com/posts/1"
}).success(function(res) {
data = res.data;
deferred.resolve(res);
})
.error(function(err, status) {
deferred.reject(err)
});
return deferred.promise;
}
return deferred.promise;
}
}
});
This will exactly make only 1 HTTP request.
My issue was that I didn't want to wait for resolve before loading another controller because it would show a "lag" between controllers if the network is slow. My working solution is passing a promise between controllers via ui-router's params and the data from promise can be loaded asynchronously in the second controller as such:
app.route.js - setting the available params to be passed to SearchController, which shows the search results
.state('search', {
url: '/search',
templateUrl: baseDir + 'search/templates/index.html',
controller: 'SearchController',
params: {
searchPromise: null
}
})
landing.controller.js - controller where the user adds search input and submits
let promise = SearchService.search(form);
$state.go('search', {
searchPromise: promise
});
search.service.js - a service that returns a promise from the user input
function search(params) {
return new Promise(function (resolve, reject) {
$timeout(function() {
resolve([]) // mimic a slow query but illustrates a point
}, 3000)
})
}
search.controller.js - where search controller
let promise = $state.params.searchPromise;
promise.then(r => {
console.log('search result',r);
})