angular.module('testJsonLoadApp')
.factory('LoadJsonFactory', function($http) {
var jsonLoad = {
fetch: function() {
return $http.get('../../test.json').then(function(data) {
return data;
});
},
};
return jsonLoad;
})
.controller('MainCtrl', function ($scope, LoadJsonFactory) {
var a = [];
LoadJsonFactory.fetch().then(function(items) {
a = items;
});
$scope.data = a;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div>
<ul>
<li ng-repeat="datum in data">{{datum.id}}</li>
</ul>
</div>
Here is the Json file content
[{"id": "0","Name": "Items 0", "Description": "Description 0"},
{"id": "1","Name": "Items 1", "Description": "Description 1"},
{"id": "2","Name": "Items 2", "Description": "Description 2"}]
The variable 'a' in the controller is not getting updated with the array generated from the json file. However if I use the $scope variable to bind the array directly, then it works.
LoadJsonFactory.fetch().then(function(items) {
$scope.data = items;
});
The above code works.
Is there a way I can assign it to a controller variable and then bind it to the scope variable?
This call is Async
LoadJsonFactory.fetch().then(function(items) {
a = items;
});
This executes before the call has finished
$scope.data = a;
So No you cannot do it this way. a will always be empty upon assigning to $scope.data
You can do this however if it suits you, which the same with what you have working, just a little clearer.
LoadJsonFactory.fetch().then(onFetch);
function onFetch(data) {
$scope.data = data;
}
The problem with your code is that LoadJsonFactory's $http call is asynchronous. Your $scope.data = a is run immediately after LoadJsonFactory is called.
I've made a quick plunkr that illustrates this—open console to see what I mean. (I've also cleaned up the LoadJsonFactory a bit so it more clearly returns a promise.)
https://plnkr.co/edit/Ns1iQdvhKT6DXrfjrOjm
Assigning to $scope.data within success function of the promise works because at that point the promise is resolved and the assignment triggers a digest that updates the two-way binding.
You can assign your service response LoadJsonFactory to a variable within your service, instead of in your controller, and then just call it from any controller.
For example:
.factory('LoadJsonFactory', function($http) {
var jsonResponse = [];
return {
fetch: function() {
return $http.get('../../test.json').then(function(data) {
jsonResponse = data;
});
},
getResponseResult: function() {
return jsonResponse;
}
}
})
Related
I have a JSON object that looks like:
[
{
"empName": "John",
"ID": 1
},
{
"empName": "Sam",
"ID": 2
},
{
"empName": "Ben",
"ID": 3
}
]
In the view I want a dropdown where the user selects one of the names. I'm using ng-options to achieve this:
<select ng-options="item as item.empName for item in employees track by item.ID" ng-model="emp.selected">
</select>
If I hard-code the JSON into the variable employees in my controller the select renders. However if I use:
$.getJSON("path to JSON file",function(json){
$scope.employees = json;
});
The select is not populated. I've tried adding in $scope.$apply() to no avail. Any suggestions?
Update 1
Taking on board the answer from Iso I'm still left with the select not binding. For example If my javascript is:
app.controller('Ctrl', ['$scope', '$http', function($scope, $http) {
$scope.employees = [
{
"empName": "John",
"ID": 1
},
];
$http.get(" --PATH TO JSON-- ").then(function (res) {
$scope.employees = res.data;
console.log($scope.employees);
});
}]);
The select is still only populated with the first name 'John' despite the fact that the console.log returns the full object with all three names.
You need to either call $scope.$evalAsync(), or use $http instead of jQuery here (which I would recommend):
$http.get("path to JSON file").then(function (res) {
$scope.employees = res.data;
});
The reason for this is that $http is aware of AngularJS' digest cycle, whereas jQuery is not.
Refrain from using $scope.$apply() (which can fail if a digest cycle is in progress), and $timeout (which is heavy since it provokes a digest cycle of all your scopes).
Wrap your code inside $timeout:
$.getJSON("path to JSON file", function (json) {
$timeout(function () {
$scope.employees = json;
})
});
The call to $apply may fail when the digest cycle is already in progress.
But consider using the $http instead of using jQuery to pull data.
You should use promises with the $q library in Angular.
var getEmployees = function () {
var deferred = $q.defer();
$.getJSON("path to JSON file", function (json) {
deferred.resolve(json);
});
return deferred.promise;
}
getEmployees().then(res){
$scope.employees = res.data;
}
EDIT
If you use $timeout is not really as correct a solution, as it doesn't give you control over the synchronicity. However, using $http to make your calls comes with promises built in, I believe.
I am facing issue while displaying data in table.
I am using a customservice to fetch data from a json file and then inserting that data into $rootScope of an object.
But when I run the view,it comes blank with no errors.In the view,i am using below line of code in the view to iterate the array of objects "books"
Please guide me.
controller.js
Controllers.controller('BookListCtrl_Student', ['$scope','$rootScope','$http','$location','BookData',
function ($scope, $rootScope, $http, $location, BookData) {
$rootScope.books=[];
$rootScope.books.push(BookData.getData());
$scope.bookLists = ['All Books', 'Available Books'];
$scope.selection = $scope.bookLists[0];
$scope.backToLogin = function() {
$location.path("/main");
}
}
]);
customservice.js
Controllers.factory('BookData',['$http',function(http){
return {
getData: function() {
return http.get('data/books.json').then(function(result){
return result.data;
});
}
};
}
]);
The problem is when you do $rootScope.books.push(BookData.getData()) it calls your getData(), but it never executes the promise. To fix it you would need to handle the promise within the controller and assign the data then.
customservice.js
return http.get('data/books.json');
controller.js
BookData.getData().then(function(resp){
$rootScope.books.push(resp.data);
})
Heres a plunker with a quick example - https://plnkr.co/edit/ivdLd9wilmWW8oUnrMsh?p=preview
i´m trying to create a factory with angular.js and y have this json structure from the github api:
[
{
"login": "mojombo",
"id": 1,
"avatar_url": "https://avatars.githubusercontent.com/u/1?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/mojombo",
"html_url": "https://github.com/mojombo",
"followers_url": "https://api.github.com/users/mojombo/followers",
"following_url": "https://api.github.com/users/mojombo/following{/other_user}",
"gists_url": "https://api.github.com/users/mojombo/gists{/gist_id}",
"starred_url": "https://api.github.com/users/mojombo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/mojombo/subscriptions",
"organizations_url": "https://api.github.com/users/mojombo/orgs",
"repos_url": "https://api.github.com/users/mojombo/repos",
"events_url": "https://api.github.com/users/mojombo/events{/privacy}",
"received_events_url": "https://api.github.com/users/mojombo/received_events",
"type": "User",
"site_admin": false
}]
i want to get the data from the url property that contain the info of the user and the idea is get that data from all the user.
this the my code so far from the factory:
angular
.module('app')
.factory('userFactory', function($http){
function getData(){
return $http.get('https://api.github.com/users');
}
function userData(){
getData().success(function(data){
return $http.get('https://api.github.com/users'+ data.url)
}).error()
}
return{
getData : getData
}
});
and this is the controller:
angular
.module('app')
.controller('listUserController', function($scope, userFactory){
$scope.users;
userFactory.getData().success(function(data){
$scope.users = data;
}).error(function(error){
console.log(error);
})
});
but i can get the data, coul you help please.......
Angular encapsulate the api response in an object, try
userFactory.getData().success(function(response){ // response instead of data
$scope.users = response.data; // response.data instead of data
}).error(function(error){
console.log(error);
})
you can try the working plnkr here.
It does:
$resource(user.url)
To request for individual user for its data
result.push(userData)
Push to result and return to controller
userFactory.userData().then(function(data){
vm.userData = data;
});
We're using $resource here because we want to keep the reference to the data in the result array. Then after response come from server, our result array will reflect the changes and show in the view.
I have simple RESTful server with Flask and I like to make a simple client with AngularJS using ngResource. The idea is make a GET to the server, and obtain a json file.
This is my services.js
var IpZapServices = angular.module('IpZapServices', ['ngResource']);
IpZapServices.factory('Plug', ['$resource', function($resource) {
return $resource('http://localhost:8003/api/plugs/:id',
{id : "#id"}, {
query: {method: 'GET', params: {}, isArray: false}
});
}]);
And the controllers.js
var IpZapControllers = angular.module('IpZapControllers', []);
IpZapControllers.controller('PlugListCtrl', ['$scope', 'Plug', function($scope, Plug) {
$scope.plugs = Plug.query();
console.log($scope.plugs);
}]);
But, I don't get the json file, get this:
Object { $promise: Object, $resolved: false }
Why? What's I do wrong? Thanks!
EDIT:
This is the raw response that I receipt from the server.
{"plugs": [
{"alarm": [],
"id": 0,
"name": "Plug 0",
"state": false},
.
.
.
{"alarm": [],
"id": 3,
"name": "Plug 3",
"state": false}
]}
EDIT 2: Solution
The problem is in the server. Simply add to the server Flask-CORS and work!
from flask.ext.cors import CORS
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
The solution is from this question
You need to resolve the promise you have created with Plug.query()
Try this:
Plug.query().$promise.then(function (response) {
console.log(response)
$scope.plugs = response;
});
More information on this can be found in the docs on angularjs.org:
https://docs.angularjs.org/api/ngResource/service/$resource
Try this:
var IpZapControllers = angular.module('IpZapControllers', []);
IpZapControllers.controller('PlugListCtrl', ['$scope', 'Plug', function($scope, Plug) {
$scope.plugs = Plug.query();
$scope.plugs.$promise.then(function (result) {
console.log(result);
$scope.plugs = result;
});
}]);
Explanation:
Your call to query will not return the data immediately. It instead returns a promise object that will eventually get your data once the HTTP request is complete (notice that the console message said resolved: false).
Read More:
Promises in AngularJS, Explained as a Cartoon
It seems that you have a promise there.
Don't assign the return value of the function, you need to wait till the promise resolves or rejects
Try
Plug.query().$promise
.then(function (response) {
// success code here
})
.catch(function (err) {})
Whwn connecting to a webapi from the controller, use the success and error promise from the method call.
Ctrl.$inject = ["$scope", "Service"];
function Ctrl($scope, Service) {
Service.getJson().success(function (data) {
$scope.json = data;
})
.error(function (data) {
// data should show the error
});
}
So this is my service that I use to fetch user details.
angular.module('app')
.factory('userDetailService', function($http) {
var userData = {};
function getUserDetails(userId) {
if (userId) {
return $http.get("/users/" + userId).success(function(data) {
angular.copy(data[0], userData);
});
}
}
return {
userData: userData,
getUserDetails: getUserDetails
}
})
Now in Controller 1 that uses this service, I have this bit of code which works fine as I get the relevant data.
$scope.getUserId = function(userId) {
if (userId) {
$scope.userData = userDetailService.userData;
userDetailService.getUserDetails(userId).success(function() {
console.log($scope.userData); //Prints valid user data
});
}
};
After this function executes in Controller 1, I try to do the following in Controller 2:
$scope.userData = userDetailService.userData;
console.log($scope.userData); //Prints null
But $scope.userData is null. Isn't the whole purpose of using a service to share data between controllers? Since I have already set the value of userData in Controller 1, shouldn't I be able to access it in Controller 2?
Weirdly enough, the modal dialog which is the template for Controller 2 is able to access data in the form of {{userData.first_name}} or {{userData.last_name}}. If this works, why is $scope.userData null? What am I missing?
Edit:
Template 1:
<div id="myModal" ng-controller="Controller 1">
<modal-configure-user></modal-configure-user>
<a data-toggle="modal" data-target="#configureUserModal" href="#" ng-click="getUserId(user.id)" data-id="user.id">{{user.first_name + ' ' +user.last_name}}</a>
</div>
Template 2:
<div ng-controller="Controller 2" id="configureUserModal">
</div>
Both are modal dialog windows.
Your approach is not very reliable, since you can't be 100% sure that data has already loaded when you try to access it in the second controller. Instead of assigning user data to variable always invoke getUserDetails method, which returns a promise. Then you just need to cache loaded data to avoid duplicated requests.
angular.module('app')
.factory('userDetailService', function($q, $http) {
var userData;
function getUserDetails(userId) {
if (userId) {
return userData ? $q.when(userData) : $http.get("/users/" + userId).success(function(data) {
userData = data;
return userData;
});
}
}
return {
getUserDetails: getUserDetails
}
});
Wrapping userData into $q.when creates a promise object, which resolves immediately. This is what you need, because service API is now consistent - you always deal with promises.
The usage in both controller then would be:
userDetailService.getUserDetails(userId).then(function(data) {
$scope.userData = data;
});