Hi guys im a newbie in AngularJS I have a problem calling multiples http.get. $scope.countries is getting values from cities. What happend?
How can calling multiple http.get?
$scope.getInfo = function(){
$scope.refreshing=true;
//cities
$http.get(baseUrl+'cities/GET_INFO/ALL').success(function(data) {
$scope.cities = data[0];
$scope.cities.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
alert('city');
});
//countries
$http.get(baseUrl+'countries/GET_INFO/ALL').success(function(data) {
$scope.countries = data[0];
$scope.countries.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
// alert('countries');
});
}
Also I tried with:
$scope.getInfo2 = function(){
$scope.refreshing=true;
alert ('start');
$scope.urlcities = $http.get(baseUrl+'cities/GET_INFO/ALL');
$scope.urlcountries = $http.get(baseUrl+'cities/GET_INFO/ALL');
$q.all([$scope.urlcities, $scope.urlcountries]).then(function(values) {
alert('finish');
$scope.refreshing=false;
});
}
But this code get an error.. Thanks so much for your help !
Carlos,
You may have a race condition with the AJAX calls. Try chaining them together using promises:
$scope.getInfo = function(){
$scope.refreshing=true;
//cities
$http.get(baseUrl+'cities/GET_INFO/ALL').then(function(data) {
$scope.cities = data[0];
$scope.cities.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
alert('city');
return $http.get(baseUrl+'countries/GET_INFO/ALL');
}).then(function(data) {
// countries
$scope.countries = data[0];
$scope.countries.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
// alert('countries');
});
};
To learn more, watch the screencast:
https://egghead.io/lessons/angularjs-chained-promises
You can also learn more about promises here:
https://docs.angularjs.org/api/ng/service/$q
NOTE:
It is a best practice to move your data preparation, business logic and calculations out of the controller and into a service. Consider revising your code to encapsulate your AJAX request (using the $http service) into a service and then inject that service into the controller that is being used to present the data to the view.
You need to make Syncronous calls.
And in Angular world it is achieved using $q or promise.
Good article on that http://haroldrv.com/2015/02/understanding-angularjs-q-service-and-promises/
Hope it helps..
Related
Iam trying to create a custom filter to filter matching array of values in angularjs. Array Structure below
["tag1","tag2"]
Now I need to filter all objs having tags matching id1,id2.. Below is the filter I have tried
var autoFilter = angular.module("autoFilters",[]);
autoFilter.filter('arrayData', function (){
return function(){
return ["id1","id2"];
}
//$scope.arrayValues = ["id1","id2"];
});
and UI code below
<li style="cursor:pointer" ng-cloak class="list-group-item" ng-repeat="values in suggestionResults | arrayData">{{values.id}} -- {{values.title}}</li>
But Data is not showing up. Can you help me out where Iam doing wrong. Plunker Code available below
plunker here
see the code below :) This is not the best approach in my opinion and will definitely have some performance issue with larger lists, but it does the work (now I used indexOf(2) but there you can pass any truthy/falsy argument)
var autoFilter = angular.module("autoFilters",[]);
autoFilter.controller("filterController",['$scope','$http', function ($scope,$http) {
$scope.searchSuggest = function(){
//$http({method: 'GET', url: 'json/searchSuggestions.json'}).success(function(data) {
$http.get("assets.json").then(function(response) {
//var str = JSON.stringify(response);
//var arr = JSON.parse(str);
$scope.suggestionResult = response.data;
console.log($scope.suggestionResult);
//$scope.arrayData = ["asset_types:document/data_sheet","asset_types:document/brochure"];
}).catch(function activateError(error) {
alert('An error happened');
});
}
$scope.showProduct = function(){
}
}]);
autoFilter.filter('arrayData', function (){
return function(data){
// if you are using jQuery you can simply return $.grep(data, function(d){return d.id.indexOf('2') >-1 });
return data.filter(function(entry){
return entry.id.indexOf('2') > -1
})
}
});
Having experienced working with large lists I would, however, suggest you to avoid using a separate filter for this and rather manipulate it in the .js code. You could easily filter the data when you query it with your $http.get like:
$scope.suggestionResult = response.data.filter(function(){
return /* condition comes here */
}
This way you are not overloading the DOM and help the browser handling AngularJS's sometimes slow digest cycle.
If you need it to be dynamic (e.g. the filtering conditions can be changed by the user) then add an ng-change or $watch or ng-click to the modifiable information and on that action re-filter $scope.suggestionResult from the original response.data
I am using three Angular controllers:
**Controller1**
var fetchStudentDetails = function(){
var sDetails = myService.getList(//url-1 here);
sDetails.then(function (data) {
$scope.studentData = data.list;
var studentId = $scope.studentData[0].id;
});
}
fetchStudentDetails();
$scope.loadSecondLevel = function(){
$state.go('secondLevel');
}
**Controller2**
var fetchClassDetails = function(){
var sDetails = myService.getList(//url-2 here);
sDetails.then(function (data) {
$scope.classData = data.list;
var className = $scope.classData[0].name;
});
}
fetchClassDetails();
$scope.loadThirdLevel = function(){
$state.go('thirdLevel');
}
**Controller3**
$scope.putStudentDetails = function(){
// Here I need studentId,className for updateResource
var sDetails = myService.updateResource(//url-3 here);
sDetails.then(function (data) {
});
}
Where I have to pass studentId (in Controller1), className (in Controller2) into a function which in Controller3. I tried with $rootScope, it is working but when refresh the page $rootScope values become empty. Does anyone know how to do this?
Your question could be split into two aspects:
1. How to share data between controllers
The best practice to share data in Angular 1.x is using factory, store the shared data in a factory service, and expose access methods to controllers:
factory('DetailData', function(myService, $q){
var _details;
function __getDetailData(){
return details
}
function __setDetailData(){
return myService.getList().then(function(data){
_details = data;
})
}
return {
getDetailData: __getDetailData,
setDetailData: __setDetailData
}
})
controller('myContrller', function(DetailData, $scope){
$scope.data = DetailData.getDetailData();
})
2. How to persist data when page refreshed,
you can use localStorage to keep data persistent during page reloading, many tools & libraries can achieve this, for example ngStorage, or you could reset the data from server every time your angular application started:
//this would register work which would be performed
//when app finish loading and ready to start.
angular.module('app').run(function(DetailData){
DetailData.setDetailData();
})
Depending on what problem you are solving.
There are three options:
Is to save data to $rootScope
Is to use $scope.$emit & $scope.$on functions.
Use a custom Service to store the data
And if you need to save data, so it was available after full page reload - localStorage.
Hey this question are responded in Passing data between controllers in Angular JS?
But the simple response is in the services.
So Im having a hard time trying to get my head wrapped around promises in angularJs. I have mixed around my code to try to do some brute force/reverse engineering understanding of it but nothing is coming out to any viable conclusion.
My Code:
Is is making a call back to get a list of repositories that I manage. These are just stored in the database as basic objects with an id and url.
Here is my view. It allows to me delete, view, and clear metadata in my database about these repos.
<div class="container" ng-controller="adminCtrl as vm">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<label class="control-label" >Repos:</label>
<div ng-repeat="repo in vm.repos">
<div class="clearfix">{{repo.URL}}<br>
<button class="btn btn-primary" ng-click='vm.listFiles(repo.URL)'>View Files</button>
<button class="btn btn-primary" ng-click='vm.clearFiles(repo.URL)'>Clear Files</button>
<button class="btn btn-primary" ng-click='vm.clearRepo(repo.URL)'>Delete Repo</button>
</div>
<br>
</div>
<label class="control-label" >Files:</label>
<div ng-repeat="file in vm.files">
<li>{{file.FullPath}}</li>
</div>
</div>
</div>
<!-- /.row -->
</div>
Here is my controller with some basic functions
(function (angular) {
'use strict';
var ngModule = angular.module('myApp.adminCtrl', []);
ngModule.controller('adminCtrl', function ($scope, $resource) {
//"Global Variables"
var File = $resource("/api/file/:repoUrl");
var Repo_del = $resource("/api/repo/:repoUrl");
var Repo = $resource("/api/repo");
var vm = this;
vm.files = [];
vm.repos = [];
vm.clearFiles = clearFiles;
vm.listFiles = listFiles;
vm.clearRepo = clearRepo;
init();
//Anything that needs to be instantiated on page load goes in the init
function init() {
listRepos();
}
function listRepos() {
vm.repos = Repo.query();
}
//Lists all files
function listFiles(url) {
vm.files = File.query({repoUrl: url});
}
function clearRepo(url) {
Repo_del.remove({repoUrl: url});
}
function clearFiles(url) {
File.remove({repoUrl: url});
}
});
}(window.angular));
Now this works fine and dandy. It brings back the repos and list them. I can delete, view, and remove with all the functions.
My issue came up with when I was trying to make a list item disappear on delete (instead of needing a page load). To do this I needed to find the index of the item being deleted in the array and remove it. I was gonna use some lodash to do this.Seemed simple enough. My problem is, my vm.repos array is not available within the controller.
For example. When I try to print out vm.repos with a console log within the listRepos function like so
function listRepos() {
vm.repos = Repo.query();
console.log(vm.repos);
}
I get nothing back from console.log. So this is telling me its not being assigned. Which to me is peculiar because the list is showing up in the ng-repeat on the view using vm.repos.
I have also ran into a problem when I am able to print out the array. It has TONS of promise information in it. For example if I put the console.log in the init() function I will get an array back that is jammed packed with information under a Resource object.
Im not sure how to go about and parse this down to be a manageable object. Looking at basic guides I have found some examples but nothing I can translate into my situation.
How do I properly handle api/resource promises?
Another problem im having is being able to mock out all of the api responses in my tests. This is my next feat. I do not care if it gets addressed here but I feel its stemming from the same problem.
Here is my only test I was able to write for this controller.
'use strict';
describe('adminCtrl', function () {
beforeEach(function () {
module('myApp.adminCtrl');
module('myApp');
});
describe('listRepos()', function () {
it('should return a json object representing a repository',
inject(function (_$httpBackend_, $rootScope, $controller) {
var scope = $rootScope.$new();
var mockBackend = _$httpBackend_;
var expectedResponse = {id: 12345, url: "https://github.com/myuser/myrepo.git"};
mockBackend.expectGET('/api/repo').respond([expectedResponse]);
var ctrl = $controller('adminCtrl', {$scope: scope});
mockBackend.flush();
expect(ctrl.repos.length).toEqual(1);
console.log(ctrl.repos[0]);
expect((angular.equals(ctrl.repos[0], expectedResponse)));
}));
});
});
Sorry if this is alot. Hopefully this isnt a repeated question.
EDIT to show what im trying now.
function clearRepo(url) {
$http.delete('/api/repo/', {params: {repoUrl: url}}).then(function (){
//DO THINGS
});
Express:
app.delete('/api/repo/:repoUrl', repoCtrl.clear);
repoCtrl.clear
module.exports.clear = function (req, res) {
var repoURL = req.params.repoUrl;
//console.log(repoURL);
Repo.remove({URL: repoURL}, function(err, results) {
if (err) {
console.log("ERR: " + err);
} else {
console.log('\n' + repoURL + ' repo deleted... \n');
}
});
Error im getting:
DELETE http://localhost:3000/api/repo/?repoUrl=https:%2F%2Fgithub.com%2Fuw34%2Fmyrepo.git 404 (Not Found)
First, the promise:
Used by $http
Allow chaining async request
Works like this :
var promise = $http.get('/api/values');
promise.then(function(response) {
$scope.displayData = response.data;
});
It is the new way to avoid simple callback (why avoid callback ?? check this CallbackHell :))
Nevertheless, callback can be complicated, hard to follow for debug and everyone prefer write sync code.
To simplify, Angular allow you to code something which look like sync code (but internally, it is async). To do it, $resource encapsulate a promise.
// this code return an empty array, then after received server respond, it will populate the empty array with data.
var values = VALUES.query();
// A simple version of it can be code like this
function simpleQuery() {
var arrayReference = [];
$http.get('api/values').then(function(response) {
// populate array reference with data received from server
angular.forEach(response.data, function(value) {
arrayReference.push(value);
});
// after the return, angular run a $digest
// which will display all newly received data thank to biding on your view
});
return arrayReference ;
}
By doing this, I return an empty array which will be populate on server response.
It is possible to get the promise from a $resource if you prefer :
var promise = Repo.query().$promise;
promise.then(function(response) {
$scope.displayData = response.data;
});
In 2020, you will probably use Async/Await instead $resource ;)
If you want more information, don't hesitate.
I have an Angular service that goes away to retrieve a pretty big JSON file (nearly 10,000 lines).
The problem i am facing, is that it is taking some time to bind the data to the front-end (as expected).
Sample controller:
$scope.dataLoaded = false;
serviceReport.getData( function (data) {
$scope.data1 = data.data1;
$scope.data2 = data.data2;
$scope.data3 = data.data3;
$scope.data4 = data.data4;
$scope.data5 = data.data5;
$scope.data6 = data.data6;
$scope.data7 = data.data7;
$scope.data8 = data.data8;
$scope.data9 = data.data9;
$scope.data10 = data.data10;
$scope.data11 = data.data11;
$scope.data12 = data.data12;
$scope.data13 = data.data13;
$scope.data14 = data.data14;
$scope.data15 = data.data15;
$scope.data16 = data.data16;
$scope.data17 = data.data17;
$scope.dataLoaded = true;
});
Service:
app.factory('serviceReport', function($http) {
return {
getData: function(value,done) {
$http.get('data.json', {
})
.success(function(data) {
done(data);
})
.error(function(error) {
alert('An error occured');
});
}
}
});
I have ng-cloak on my HTML element, when dataLoaded = true, this is removed as it indicates the data is available to be displayed.
How can i improve the service call/data bind? Would splitting the call help?
Server-side solution would be to reduce the size of the response and make more requests with smaller responses. Do you actually need the whole response at start? You have to be aware that binding the whole response will generate many watchers, which will slow down all subsequent digests.
Client-side solution would be to bind the response part by part in a loop as a callback parameter for $scope.$apply() or even $timeout().
I have a rather simple question. I have a simple controller and its $scope.coords = []; renders JSON in HTML:
[24.43359375, 54.6611237221]
[25.2905273438, 54.6738309659]
[25.3344726562, 54.6102549816]
[25.2685546875, 54.6801830971]
[25.2960205078, 54.6611237221]
How can I render that JSON not in html, but in my controller itself ? The code looks like that. Please see the comment in code:
propertyModule.controller('propertyController', ['$scope', 'Property', function ($scope, Property) {
// Query returns an array of objects, MyModel.objects.all() by default
$scope.properties = Property.query();
// Getting a single object
$scope.property = Property.get({pk: 1});
$scope.coords = [];
$scope.properties = Property.query({}, function(data){
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
$scope.positions = //$Resource('realestate.property').items();
[
[54.6833, 25.2833], [54.67833, 25.3033] // those coordinates are hardcoded now, I want them to be rendered here by $scope.coords
];
}]);
First off, you're showing us a bunch of arrays, not a JSON document. But since your code seems to be working, I'll assume you do have a valid JSON to work with.
You need to consider the fact that you are making an asynchronous request here :
$scope.properties = Property.query({}, function(data) {
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
This means you won't be able to fetch data from $scope.coords before anything has arrived.
There are several ways to solve that :
You could simply fetch data while you're still in the loop :
angular.forEach(data , function(value) {
$scope.coords.push(value.coordinates);
if('your condition') {
$scope.positions.push(value.coordinates);
}
});
You could use a promise, see the angular doc.
Or you could watch over $scope.coords with $scope.$watch.