I'm fairly new to Angular but am trying to abstract a RESTful call from $http to a factory/resource but I cant seem to pass any params to it. I've read though SO but cannot find an example of this.
My factory code (services.js):
myApp.factory("PropertyDb", function($resource, $log) {
return {
getProperties: function(onSuccess) {
var properties = $resource("http://myurl.com/get_properties.php?", {
callback: 'JSON_CALLBACK',
postcode: 'AA11AA',
minimum_beds: '3',
minimum_price: '97500'
},
{
fetch : {method:'JSONP'},
params: {postcode: 'BB11BB'} // This doesn't override the above or work without the above postcode
});
properties.fetch(
function success(response) {
console.log(response);
onSuccess(response.listing);
},
function error(response) {
console.log(response);
console.log("error");
}
);
},
My Controller code:
myControllers.controller('PropertyListCtrl', ['$scope', 'PropertyDb',
function($scope, PropertyDb) {
$scope.properties = {};
// Adding the postcode below doesnt work...
PropertyDb.getProperties({postcode : 'CC11CC'}, function(responce) {
$scope.properties = responce;
});
}]);
I want to be able to use my factory in my controllers and pass it different params like postcode etc and override the defaults set in the factory. No matter what I try I cannot seem to do this and the docs aren't very easy to follow.
From your example you passed 2 parameters to PropertyDb.getProperties:
postcode Object: {postcode : 'CC11CC'}
callback: function(responce) {$scope.properties = responce;}
The one thing is to use 1st parameter in factory:
myApp.factory("PropertyDb", function($resource, $log) {
return {
getProperties: function(parameter, onSuccess) {
// ^param^ , ^callback^
/* ... */
}
So fixed version of service should be:
myApp.factory("PropertyDb", function($resource, $log) {
return {
getProperties: function(parameter, onSuccess) {
var properties = $resource("http://myurl.com/get_properties.php?", {
callback: 'JSON_CALLBACK',
postcode: parameter.postcode,
minimum_beds: '3',
minimum_price: '97500'
},
{
fetch : {method:'JSONP'},
params: parameter
});
properties.fetch(
function success(response) {
console.log(response);
onSuccess(response.listing);
},
function error(response) {
console.log(response);
console.log("error");
}
);
},
/*...*/
}
});
Try:
myApp.factory("PropertyDb", function($resource, $log) {
return {
getProperties: function(data,onSuccess) { //add 1 more parameter
var properties = $resource("http://myurl.com/get_properties.php?", {
callback: 'JSON_CALLBACK',
postcode: 'AA11AA',
minimum_beds: '3',
minimum_price: '97500'
},
{ //fix your code here
fetch : {
params: data || {postcode: 'BB11BB'},
method:'JSONP'
}
});
properties.fetch(
function success(response) {
console.log(response);
onSuccess(response.listing);
},
function error(response) {
console.log(response);
console.log("error");
}
);
},
But I think a better solution is we only define the $resource once:
myApp.factory("PropertyDb", function($resource, $log) {
//define only once here so we don't need to redefine it whenever we run the method.
var properties = $resource("http://myurl.com/get_properties.php?", {
callback: 'JSON_CALLBACK',
postcode: 'AA11AA',
minimum_beds: '3',
minimum_price: '97500'
},
{ //fix your code here
fetch : {
params: {postcode: 'BB11BB'},
method:'JSONP'
}
});
return {
getProperties: function(data,onSuccess) { //add 1 more parameter
properties.fetch(
data, //send the data.
function success(response) {
console.log(response);
onSuccess(response.listing);
},
function error(response) {
console.log(response);
console.log("error");
}
);
},
I got it, you can use app.factory() as a separate js file to read a file, say get_data.js.
The parameter arg is a file path(now is a web file, you can change it to a relative file path, like js/abc.txt).
var app = angular.module('app', []);
// this part can separate from others as a single file - get_data.js
app.factory('metdata', ['$http', function ($http) {
var load_data = {}; // This is like a new class in factory
load_data.getDataPath = function (arg) { // This is like a method of class
console.log(arg);
load_data.path = arg; // This is like a attribute of class
return $http.get(load_data.path);
};
console.log('print 1 ' + load_data.data);
return load_data; // Call the class, and then call getDataPath function
}]);
app.controller('MainCtrl', ['$scope', 'metdata', function($scope, metdata) {
$scope.loadData = function () {
var dataPath = 'https://raw.githubusercontent.com/OnlyBelter/learnGit/master/readme.txt';
metdata.getDataPath(dataPath).success(function (data) {
console.log(data);
});
};
}]);
<!--this is html file-->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<body ng-app="app" ng-controller="MainCtrl">
<br>
<div>
<p>Load data:
<button ng-click="loadData()">Load</button>
</p>
</div>
</body>
Related
I have been trying to get data from a json file through service in AngularJS(1.5.3). I am unable to retrieve the data from json and display it in the view. I get blank values instead. Below is the code:
//Service
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
var promise = $http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return "Invalid data";
})
return promise;
}
};
}]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
Update:
Below is the json data that I have.
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}];
Could anyone help me out.
I think the problem is in your first line!
you should pass all the services as string:
angularStoreApp.factory('dataService', ['$http', '$q' function ($http, $q) {
In order to get the code snippet working, you need to
initialize the module in the js: var angularStoreApp = angular.module('storeapp', []);
add ng-app in the view
add ng-controller in the view (or use routing)
Forgetting to add $q to the dependencies is indeed a mistake, but it doesn't prevent your app from working, as long as you don't use $q.
Below is an adjusted, working code snippet. I simulated the http call by returning json wrapped in a promise. If it still doesn't work, the problem is the HTTP call, i.e. the part I commented out. Execute the call in the browser and verify that the correct JSON is returned.
var angularStoreApp = angular.module('storeapp', []);
//Service
angularStoreApp.factory('dataService', ['$http', '$q',
function($http, $q) {
return {
getProducts: function() {
//simulate promise with json:
return $q.when([{
'Name': 'name 1',
'Price': 1.23
}, {
'Name': 'name 2',
'Price': 4.56
}]);
//var promise = $http.get('/api/json/products.json')
// .then(function successCallback(response) {
// if (typeof response.data === 'object') {
// return response.data;
// } else {
// invalid response
// return "Invalid data";
// }
// }, function errorCallback(response) {
// return "Invalid data";
// })
// return promise;
}
};
}
]);
//Controller
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService',
function($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="storeapp" ng-controller="SearchCtrl">
<blockquote>
<h4 style="color: salmon">Welcome to the New Store
<br />
Please select the products you want and add them to your shopping cart.
<br />
When you are done, click the shopping cart icon to review your order and checkout.
<br />
</h4>
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.Name}}, {{ProductItem.Price}}
</div>
</div>
</div>
I've replicated your code in jsbin and it is working fine through I've changed the code for brevity.
The issue might be with below code as it looks for products.json in root folder not in your project folder.
$http.get('/api/json/products.json')
Try with removing '/' in the URL.
$http.get('api/json/products.json')
I found the answer after reading the following links
http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/
http://bguiz.github.io/js-standards/angularjs/resolving-promises-for-a-controller/
The following is how I modified my code to get it working:
In the config routing section I used the resolve property for that route
var angularStoreApp = angular.module('AngularStore', ['ngRoute']);
angularStoreApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/products', {
templateUrl: '/Views/Store.html',
controller: 'SearchCtrl',
resolve: {
resolvedJSON: function (dataService) {
return dataService.getProducts();
}
}
})
.when('/product/:productName', {
templateUrl: '/Views/product.html',
controller: 'ProductCtrl'
})
.otherwise({
redirectTo: '/products'
});
}]);
Now I injected the resolvedJSON in my controller as follows
/// <reference path="SearchController.js" />
angularStoreApp.controller('SearchCtrl', ['$scope', 'resolvedJSON', function ($scope, resolvedJSON) {
$scope.ProductItems = [];
$scope.ProductItems = resolvedJSON;
}]);
And my service code as follows:
angularStoreApp.factory('dataService', ['$http', function ($http, $q) {
return {
getProducts: function () {
return $http.get('/api/json/products.json')
.then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
return "Invalid data";
})
}
};
following is my json data
[{
"itemName": "Notepad",
"itemPrice": 12,
"itemQuantity": 0
},
{
"itemName": "Pen",
"itemPrice": 8,
"itemQuantity": 0
},
{
"itemName": "Pencil",
"itemPrice": 5,
"itemQuantity": 0
}]
My view(Store.html) is something like this:
<div class="row">
<div class="col-sm-12" ng-repeat="ProductItem in ProductItems track by $index">
{{ProductItem.itemName}}, {{ProductItem.itemPrice}}
</div>
</div>
Hope this helps anyone facing a similar issue as mine. Thank you everyone who steered me to a better and simple answer.
u forgot the '$q' for minification
u should use the var deferred = $q.defer();// (I will add a full code)
example jsfiddle
angular.module('app', [])
.controller('ctrl', function(dataService, $scope) {
var vm = this;
$scope.ProductItems = [];
dataService.getProducts()
.then(function(data) {
$scope.ProductItems = data;
});
})
.factory('dataService', ['$http', '$q', function($http, $q) {
function getProducts() {
var deferred = $q.defer();
$http.get('https://httpbin.org/get')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
// let say we get list of Products
deferred.resolve([{
id: 1,
name: 'Product1'
}, {
id: 2,
name: 'Product2'
}, {
id: 3,
name: 'Product3'
}]);
} else {
// invalid response
return "Invalid data";
}
}, function errorCallback(response) {
return deferred.reject("Invalid data");
})
return deferred.promise;
}
return {
getProducts: getProducts
};
}]);
I think one of the problems you have is already mentioned #Ahmad Mobaraki ,and another one is in your dataService, the promise object returned by $http.get('/api/json/products.json') is already consumed by then() function, so what you need to do is to create a new promise and return it.
Code example:
//service
angularStoreApp.factory('dataService', ['$http', '$q', function ($http, $q) {
return {
getProducts: function () {
var defered = $q.defer();
$http.get('/api/json/products.json')
.then(function successCallback(response) {
if (typeof response.data === 'object') {
defered.resolve(response.data);
} else {
// invalid response
defered.reject("Invalid data");
}
}, function errorCallback(response) {
defered.reject("Invalid data");
});
return defered.promise;
}
};
}]);
//Controller
angularStoreApp.controller('SearchCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.ProductItems = [];
dataService.getProducts()
.then(function (data) {
$scope.ProductItems = data;
});
}]);
hope this can help you :)
I have a template in which I output the values (movie titles) from my database,
%div{"ng-repeat" => "movie in movies"}
{{ movie.title }}
And a template in which users can input a movie title,
%div{"ng-controller" => "searchCtrl", :id => "container_search"}
#addMovie{"ng-controller" => "addMovieCtrl"}
%div{"ng-click" => "addMovie()"}
%input{:type => "text", "ng-model" => "title"}
addMovie action.
When a user types in a movie title in the inputfield and clicks the div it gets saved into the database, and when I refresh the page I can see the result. But I want this to happen asynchronously (at the same time, right?).
This is the controller,
angular.module('addMovieseat')
.controller('movieOverviewCtrl', [
'$scope', 'movieService', function($scope, movieService) {
movieService.success(function(data) {
$scope.movies = data;
});
}
]);
And this is the service,
angular.module('addMovieseat')
.factory('movieService', ['$http', function($http) {
return $http.get('movies.json')
.success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}])
.factory('movies', ['$http', function($http){
var o = {
movies: []
};
o.create = function(movie){
return $http.post('/movies.json', movie).success(function(data){
o.movies.push(data);
});
};
return o;
}])
Your service should not do an HTTP request as soon as it's instanciated, and then always return the same result. Instead, it should provide a method that allows getting the movies.
Once that is done, you can simply call the service again right after saving a new movie:
angular.module('addMovieseat')
.factory('movieService', ['$http', function($http) {
return {
loadMovies: function() {
return $http.get('movies.json');
}
};
}])
.factory('movies', ['$http', function($http){
return {
create: function(movie) {
return $http.post('/movies.json', movie);
}
};
}]);
and your controller can now simply do
angular.module('addMovieseat')
.controller('movieOverviewCtrl', [
'$scope', 'movieService', 'movies', function($scope, movieService, movies) {
var init = function() {
movieService.loadMovies().then(function(response) {
$scope.movies = response.data;
});
};
init();
$scope.save = function() {
movies.create({title: $scope.title}).then(init);
};
}]);
Note that you're making your own life more complex than it should by defining two services instead of just one that would have a loadMovies() and a create() functions.
I am starting to learn angularjs, so far i can create update delete withoud using services. I am trying to take it to the next level: Ive created a service page that looks like this:
app.factory('MainService', function($http) {
var getFeaturesFromServer = function() {
return $http.get('restfullfeatures/features');
}
var deleteFeature = function(id) {
return $http.post('restfullfeatures/delete', {'id': id});
}
var createFeature = function(feature) {
return $http.post('restfullfeatures/create', {'title': feature.title, 'description': feature.description});
}
return {
getHello : getHello,
getFeatures: getFeaturesFromServer,
deleteFeature: deleteFeature,
createFeature: createFeature
}
});
and my add function in controller looks like this:
$scope.add = function(){
MainService.createFeature($scope.formFeature)
.then(function(response) {
console.log('feature created',response.data);
$scope.features.push($scope.formFeature);
$scope.formFeature = {};
}, function(error) {
alert('error',error);
});
};
And this is my postCreate function:
public function postCreate()
{
\App\Feature::create(
['title' => \Input::get('title'),
'description' => \Input::get('description')
]);
return ['success' => true];
}
I have a table in my database called features, so basically what i am trying to do is add a new feature to my table using angularjs, my controller doesnt seem to recognize formFeature all i get is 'undefined' then i get the error: Cannot read property of type undefined, but I am using it in my delete function and it works perfectly, what did i miss here??
Factory
So when creating a factory for CRUD, try to lay out your factory like the example below. The example is some code I wrote for a project each call willl do a different thing, the idea is that you add a method that you can instantiate when you add the factory to your controller. (note: don't use $rootScope for session)
.factory('Chats', function ($http, $rootScope, $stateParams) {
return {
all: function () {
return $http.get('http://your_ip/chats', { params: { user_id: $rootScope.session } })
},
get: function () {
return $http.get('http://your_ip/chat', { params: { user_id: $rootScope.session, chat_id: $stateParams.idchat } })
},
add: function (id) {
return $http.post('http://your_ip/newChat', { params: {idfriends:id}})
}
};
});
Controller
when you instantiate this in your controller your looking at something like this
.controller('ChatsCtrl', function ($scope, Chats) {
Chats.all().success(function (response) {
$scope.chats = response;
});
$scope.getChat = function (id) {
Chats.get().success(function (response) { })
};
$scope.addChat = function (id) {
Chats.add(id).success(function (response) { })
};
})
$scope.remove and $scope.addChat and linked to buttons that will execute on click and $scope.chats is bound to the page via an ng-repeat.
Conclusion
Clean up your factories to look like this and you can easily write a series of reusable methods that are easy to instantiate in your controller.
How would i change the following code form $http.get to a $resource
//The created resource (not using it for now)
hq.factory('LogsOfUser', function ($resource) {
return $resource('/HQ/Graph/GetLoggedinTimes?userName=:userName', {
userName: '#userName'
})
});
//The Controller
var ModalViewLogActionsCtrl = function ($scope, $http, $log, LogsOfUser, $modal) {
$scope.openLogs = function (userName) {
$http.get("/HQ/Graph/GetLoggedinTimes?userName=" + userName).success(function (data) {
var modalInstance = $modal.open({
templateUrl: 'LogView.html',
controller: 'ModalLogViewInstance',
resolve: {
items: function () {
//$scope.items = data;
$log.log(data);
$scope.items = data;
return $scope.items; //return data;
},
userName: function () {
return userName;
}
}
});
}).error(function () {
alert("eror :(");
});;
};
};
You've already done most of the work. All you need now is to call the service inside the controller :
LogsOfUser.query({
userName: userName
}, function success(data) {
//your code
}, function err() {
alert("Error")
});
Use query to get an array of data, and get to get a single document.
Here is a example how to call a resource from a controller:
app.controller('MainCtrl', function($scope, $resource) {
var userName = 'Bob';
var LoggedinTimes = $resource('/HQ/Graph/GetLoggedinTimes');
var data = LoggedinTimes.get({userName : userName}, function () {
console.log(data);
});
});
First, you would want to move data-related logic behind a Service, so your controller doesn't know about server-specifics. More importantly, your Service becomes reusable as all services in AngularJS are global singletons. your controller stays small, as it should be.
Next, your controller would call getLoggedIntimes() and work with the outcome as if the data is there. The result of a $resource.get() or similar functions return an empty object or array which fills itself when the REST call returns with data.
In your service you would do the actual $resource.get().
something along the lines of the following pseudo code:
//The Controller
var ModalViewLogActionsCtrl = function ($scope, MyService, $log, LogsOfUser, $modal) {
$scope.openLogs = function (userName) {
var items = MyService.getLoggedInTimes(userName);
var modalInstance = $modal.open({
templateUrl: 'LogView.html',
controller: 'ModalLogViewInstance',
resolve: {
items: function () {
$scope.items = items;
return $scope.items;
},
userName: function () {
return userName;
}
}
});
};
};
app.service('MyService', function ($resource) {
var loggedInResource = $resource('/HQ/Graph/GetLoggedinTimes/:userName');
return {
getLoggedInTimes: functio(username) {
return loggedInResource.get({
username: username
});
}
};
});
I am new to angular js. I am trying to call factory service method 'getScoreData' from ng-change of select, but not able to get it done. please help.
Html code:
<select ng-model="Score" ng-change="getScoreData(Score)" ng-options="c.name for c in Scores"></select>
Angularjs code:
var app = angular.module('audiapp', []);
app.controller('audiLayoutCtrl', function ($scope, ScoreDataService) {
ScoreDataService.getScoreData($scope.Score, function (data) {
$scope.ScoreData = data;
});
});
app.factory('ScoreDataService', function ($http) {
return {
getScoreData: function (Score, callback) {
var params = {
questionCode: Score.code
}
return $http({
url: 'Home/GetAvgData',
method: 'GET',
params: params
}).success(callback);
}
};
});
above is the service factory method and its instantiate from controller. I tried instantiating from ng-change of select but its neither giving error nor its getting called.
You have at least two issues in your code:
ng-change="getScoreData(Score)
Angular doesn't see getScoreData method that refers to defined service
getScoreData: function (Score, callback)
We don't need to use callback since GET returns promise. Use then instead.
Here is a working example (I used random address only for simulation):
HTML
<select ng-model="score"
ng-change="getScoreData(score)"
ng-options="score as score.name for score in scores"></select>
<pre>{{ScoreData|json}}</pre>
JS
var fessmodule = angular.module('myModule', ['ngResource']);
fessmodule.controller('fessCntrl', function($scope, ScoreDataService) {
$scope.scores = [{
name: 'Bukit Batok Street 1',
URL: 'http://maps.googleapis.com/maps/api/geocode/json?address=Singapore, SG, Singapore, 153 Bukit Batok Street 1&sensor=true'
}, {
name: 'London 8',
URL: 'http://maps.googleapis.com/maps/api/geocode/json?address=Singapore, SG, Singapore, London 8&sensor=true'
}];
$scope.getScoreData = function(score) {
ScoreDataService.getScoreData(score).then(function(result) {
$scope.ScoreData = result;
}, function(result) {
alert("Error: No data returned");
});
};
});
fessmodule.$inject = ['$scope', 'ScoreDataService'];
fessmodule.factory('ScoreDataService', ['$http', '$q', function($http) {
var factory = {
getScoreData: function(score) {
console.log(score);
var data = $http({
method: 'GET',
url: score.URL
});
return data;
}
}
return factory;
}]);
Demo Fiddle