Angular loading data from server json - javascript

Can someone tell me whats the best solution to use jsonObjects in ng repeat?
My code is as follows:
My PHP response is this:
die(json_encode(array('sts'=>'success', 'title'=>'*****', 'msg'=>'*******', 'data'=>$result)));
My angular.js the service is like this:
LeadApp.service('LeadsService', function ($http) {
var AllLeads = {
async: function() {
var promise = $http({
url: '../app/ServerData.php',
method: "POST",
params: { req_id: 'leads' }
}).then(function (response) {
return response.data;
});
return promise;
}
};
return AllLeads;
});
Then in my controller i call the service and set a lead var to use in my view with ng repeat: My Data is being loaded i assured already. I attached a pic of the console log from below. But somehow the ng repeat just wont work like expected... What am i doing wrong? There is no error!
....
LeadsService.async().then(function (d) {
this.leads = d.data;
console.log(this.leads);
this.list_all = this.leads;
Here is the main ng repeat part in the view (custom ng repeat with "dir-paginate"):
...
<div class="widget-content leads_list" ng-controller="leadsController as leads">
<tr dir-paginate="lead in leads.list_all | orderBy:sortKey:reverse | filter:search | itemsPerPage:15" pagination-id="leads.list_all">
<td scope="row">{{lead.id}}</td>
...

You need to bind the this context outside then method.
Since you are using ng-controller="leadsController as leads", it would be wise to bind to the variable leads.
var leads = this;
LeadsService.async().then(function (d) {
leads.list_all = d.data;
console.log(leads.list_all);
});
The this context inside the then method is not the same as the this context outside the then method. Also binding to the same name that you use in your template helps to avoid confusion.

this is not the controller in that callback function context. So you need to assign this to a variable in the controller.
var ctrl = this;
LeadsService.async().then(function (d) {
ctrl.leads = d.data;
console.log(ctrl.leads);

Related

How to make filters work with array in angularjs

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

AngularJS + Firebase - Scope not updating when getting remote data

I've been working on the problem for about a week now with no progress. I have an array of data that's available to my $scope. I iterate through it like this:
<div ng-repeat="device in myData">
<label>{{processor(device.$id)}}</label>
</div>
The data contains only a Firebase $uid. And I want to make a second request to the database to get information thats associated with this $uid and place it as the label's content. I thought I could use an angular expression with a function to pass in the Firebase $uid and return some data.
I declare this $scope function:
$scope.processor = function(uid) {
function getDeviceInfo(callback) {
_.child('device/' + uid).once('value', function(snapshot) {
callback(snapshot.val())
})
}
getDeviceInfo(function(data) {
console.log(data)
return data
})
}
Basically I call the processor($id) in my scope, passing in the uid I want to lookup. The function getDeviceInfo() runs and has a callback, when the data is returned, I log it to the console, which works perfect, all the data is there. But then when I try and return a value to the $scope, it doesn't update.
I've tried about every combination of Angular/AngularFire code available and haven't gotten anything to work, any ideas?
If the function that you pass as a parameter to the once function is executed asynchronous, you can't return the data from the processor function.
The best you can do is to add the resulting data to the device object or to create another object to hold all the data for all the devices.
Try this:
<div ng-repeat="device in myData">
<label>{{device.data}}</label>
</div>
$scope.processor = function(device) {
_.child('device/' + device.$id).once('value', function(snapshot) {
device.data = snapshot.val();
});
}
}
$scope.myData.forEach($scope.processor);
Or this:
<div ng-repeat="device in myData">
<label>{{deviceData[device.$id]}}</label>
</div>
$scope.deviceData = {};
$scope.processor = function(device) {
_.child('device/' + device.$id).once('value', function(snapshot) {
$scope.deviceData[device.$id] = snapshot.val();
});
}
}
$scope.myData.forEach($scope.processor);
If that function is not asynch you can return the data using something like this:
$scope.processor = function(uid) {
var data = undefined;
_.child('device/' + uid).once('value', function(snapshot) {
data = snapshot.val()
})
return data
}
reference
Your function is not returning anything. Try this:
$scope.processor = function(uid) {
function getDeviceInfo(callback) {
_.child('device/' + uid).once('value', function(snapshot) {
callback(snapshot.val())
})
}
return getDeviceInfo(function(data) {
console.log(data)
return data
})
}
This seems overcomplicated though, why not do this?
$scope.processor = function(uid) {
return _.child('device/' + uid).once('value', function(snapshot) {
return callback(snapshot.val());
})
}
Check whether you have multiple instances of the same controller in your application. I made the same mistake a couple of times. I had ngRoute instantiating the controller using
app.config(function($routeProvider) {
$routeProvider
.when('/', {
// template : 'NIIIIICE',
templateUrl : 'pages/enquiries.html',
controller : 'firstcontroller'
});
});
Then I mistakenly created another instance of the SAME controller inside the HTML like:
ng-controller="firstcontroller as fctrl"
When I call $apply() it would only apply to child instance and that made it seem as if it wasn't working....So yeah long story short make sure you are running $apply in the same instance. :)

Best way to pass variables between controllers

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.

Angular, Promises, ngResource

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.

Build simple cache in Angularjs service for data provide from http request which return object reference

I want build some simple cache in Angularjs service for data provide from http request. Additional I want always get reference to the same object. I prepare example code to illustrate my thinking and problem which I have now.
jsfiddle code illustrate problem
I have service UsersModel which provide me user from http request.This user data are shared between controllers. So want to have always reference to same data. I add to him simple logic. Before UsersModel.getUsers() call service check if exist any data from previous call, if exist return him, if not do a http request. I inject that service in tree controller. In first two controllers UsersModel.getUsers() is call immediately after page load. In last after click on button.
Problem is when two first controller call UsersModel.getUsers() in the same time. Then any cached data don't exist and both do http request After that I have in first two controller reference to different user objects. We can see this clicking on load button.
And now my question. How to make this work for the simultaneous first call UsersModel.getUsers() and always have reference to the same object data.
app.js
var APP = angular.module('APP', []);
APP.SidebarCtrl = function ($scope, UsersModel) {
var sidebarCtrl = this;
UsersModel.getUsers()
.then(function (users) {
sidebarCtrl.users = users;
});
};
APP.ContentCtrl = function ($scope, UsersModel) {
var contentCtrl = this;
UsersModel.getUsers()
.then(function (users) {
contentCtrl.users = users;
});
};
APP.FootCtrl = function ($scope, UsersModel) {
var footCtrl = this;
function load() {
UsersModel.getUsers()
.then(function (users) {
footCtrl.users = users;
});
}
footCtrl.load = load
};
APP.service('UsersModel', function ($http, $q) {
var model = this,
URLS = {
FETCH: 'http://api.randomuser.me/'
},
users;
function extract(result) {
return result.data.results['0'].user.email;
}
function cacheUsers(result) {
users = extract(result);
return users;
}
model.getUsers = function () {
return (users) ? $q.when(users) : $http.get(URLS.FETCH).then(cacheUsers);
};
});
Index.html
<div ng-app="APP">
<div ng-controller="APP.SidebarCtrl as sidebarCtrl">
<h1>{{ sidebarCtrl.users }}</h1>
</div>
<div ng-controller="APP.ContentCtrl as contentCtrl">
<h1>{{ contentCtrl.users }}</h1>
</div>
<div ng-controller="APP.FootCtrl as footCtrl">
<h1>{{ footCtrl.users }}</h1>
<button ng-click="footCtrl.load()" type="button">Load</button>
</div>
</div>
jsfiddle code illustrate problem
You can modify your functions as follows:
function cacheUsers(result) {
return (users) ? users : users = extract(result);
}
and
model.getUsers = function () {
return (users) ? $q.when(users) : $http.get(URLS.FETCH, {cache: true}).then(cacheUsers);
};
It provides additional cache check after fetch and enables built-in cache for the object.
I suggest you to read http://www.webdeveasy.com/angularjs-data-model/

Categories

Resources