This question already has an answer here:
AngularJS Variable in service is not updating in view?
(1 answer)
Closed 5 years ago.
I'm new to AngularJs, and I've got a little problem without using $scope in my factory module.
I've got a GET function that's used by a controller via the factory, and it's supposed to return a list from my db as json
and show it on the html using ng-repeat. For a reason I couldn't find a solution for, it doesn't. My only guess was that there's a problem regarding databinding from the factory to the html.
How do I bind the returned json to a variable and bind that variable to my html?
This function worked before I created the factory, but without using the $scope
I'm pretty lost.
I hope I was able to explain myself correctly.
Factory:
(function() {
angular.module("myApp").factory('appServicesProvider',function( $http ) {
var restURL = "http://localhost:8080/Project/rest/api/";
function getAllRows(allRows){
$http.get(restURL).then(
function(response){
allRows = response.data.coupon;
}
);
}
return{getAllRows:getAllRows}
Controller:
(function() {
angular.module("myApp")
.controller("AdminController",function($scope, $http,
appServicesProvider) {
// I know that this method of scoping a service is not best and/or recommended,
//it just works better for me with the admin controller.
$scope.appServicesProvider = appServicesProvider;
HTML:
<div id="getAllRowsDiv">
<table class="table table-hover">
<thead>
<tr>
// list details
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="coupon in allRows">
// list
</tr>
</tbody>
</table>
<button class="btn btn-success" ng-
click="appServicesProvider.getAllRows()" >Get All Rows</button>
</div>
Use $q
(function() {
angular.module("myApp").factory('appServicesProvider',function( $q, $http ) {
var restURL = "http://localhost:8080/Project/rest/api/";
function getAllRows(allRows){
var deffer = $q.defer();
$http.get(restURL).then(
function(response){
deffer.resolve(response.data.coupon;)
},function(error){
deffer.reject(error)
}
);
return deffer.promise
}
return{getAllRows:getAllRows}
and in controller
$scope.getRows = function(){
appServicesProvider.getAllRows().then(function(res){
$scope.allRows = res;
},function(err){
alert(err)
})
}
$scope.getRows();
In the service, return the http promise:
app.factory('appServices',function( $http ) {
return { getAllRows:getAllRows }
var restURL = "http://localhost:8080/Project/rest/api/";
function getAllRows(){
͟r͟e͟t͟u͟r͟n͟ $http.get(restURL)
.then(function(response){
var allRows = response.data.coupon;
͟r͟e͟t͟u͟r͟n͟ allRows;
});
}
});
In the controller, use the .then method to extract the data:
app.controller("AdminController",function($scope, $http, appServices) {
var promise = appServices.getAllRows();
promise.then(function(allRows) {
$scope.allRows = appRows;
});
});
Related
I am showing employee record using AngularJS. I am using two views to show data, I am using two views emp-box.htm and its respective controller(empController) and in this controller employeeBoxController I am fetching data from service, I want the result which is obtained in employeeBoxController to be used in empController and show in view (emp-list.htm), I created a service eService
app.service('dataService',function() {
var s = {};
this.setData = function(data,key) {
s[key]=data;
},
this.getData = function(key) {
return s[key];
}
this.hello = function() {
return 'hello';
}
})
for fetching result and setting data in employeeBoxController and getting in empController but when I fetch the data using console.log(dataService.getData('result')); in empController i get undefined
The employeeBoxController is
app.controller("employeeBoxController", ['$scope', 'employeeService',
'dataService', function($scope, employeeService, dataService) {
$scope.getEmployeeDetails = function(eid) {
$scope.isLoading = false;
employeeService.getDetails($scope.eid).then(function(result) {
dataService.setData(result, 'result');
$scope.isLoading = true;
console.log(dataService.getData('result'));
})
}
}])
The empController is :-
app.controller("empController", ['$scope', 'employeeService', 'dataService',
function($scope, employeeService, dataService) {
$scope.result = dataService.getData('result');
//console.log(dataService.hello());
console.log(dataService.getData('result'));
console.log(dataService.hello());
}
])
The service class employeeService is :-
app.config(["employeeServiceProvider",function(employeeServiceProvider){
employeeServiceProvider.config('http://localhost:8080/pos');
}]);
app.provider("employeeService",function(){
var myurl='';
this.config=function(eurl){
myurl=eurl;
}
this.$get=['$http','$log',function($http,$log){
var employeeobj={};
employeeobj.getDetails=function(eid){
return $http.get(myurl+'/getEmployees/'+eid);
}
return employeeobj;
}];
});
emp-box.htm is:-
<div>
Enter the id: <input type="text" ng-model="eid"/>
<button ng-click="getEmployeeDetails()">show</button>
</div>
emp-list.htm is:-
<div class="panel panel-primary">
<div class="panel-body" style="text-align:center; margin:0 auto">
<h3>Employee Data</h3>
</div>
</div>
<div class="panel panel-primary">
<div class="panel-body">
<!-- <div ng-show="!isLoading" style="color:red">
<span class="glyphicon glyphicon-time"></span>Loading...
</div>-->
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>empno</th>
<th>salary</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="oemp in result.data">
<td>{{oemp.eid}}</td>
<td>{{oemp.name}}</td>
<td>{{oemp.empno}}</td>
<td>{{oemp.sal}}</td>
</tr>
</tbody>
</table>
</div>
</div>
From what I understand you're trying to cache your API results in another service, so you don't have to call the API again in another controller. Also, empController is executed first, and when you're doing dataService.getData('result') the API response of setting it isn't received yet which in turn is called from another service. I would suggest you combine these 2 services, so that instead of caching the exact values in service, you can cache the API call itself, and in case where cache doesn't contain your data, make an API call and cache it.
Here's something I'd make, say CacheAPIService.
app.factory('CacheAPIService', ['$http', function($http) {
var cache = {};
return {
get: function(api) {
if(angular.isUndefined(cache[api])) {
cache[api] = $http.get(api); //storing in cache while making API call
}
return cache[api]; //Return from cache
},
clear: function(api) {
delete cache[api];
}
}
}]);
So, whenever you need to make an cache an API call, use this service in addition to making API call it'll also cache it. And, if it is already cached, no new API call is made. Benefits is you'll never run into cases where it return's undefined, as you are returning promise.
In your 1st controller, updated code becomes:
app.controller("employeeBoxController", ['$scope', 'CacheAPIService', function($scope, CacheAPIService) {
$scope.getEmployeeDetails = function(eid) {
$scope.isLoading = true;
var endpoint = 'api/endpoint/'+$scope.eid; //Replace with your API endpoint
CacheAPIService.get(endpoint).then(function(result) {
$scope.isLoading = false;
console.log(dataService.getData('result'));
})
}
}]);
Here, 1st API call is made and is cached. Take a look at your other controller:
app.controller("empController", ['$scope', 'CacheAPIService', function($scope, CacheAPIService) {
CacheAPIService.get(endpoint).then(function(data) {
var endpoint = 'api/endpoint/'+$scope.eid; //your API endpoint
console.log('data =', data);
});
}]);
Here, you still use the same service, but it'll be cached, and if not cached, it'll make API call and cache that promise. Here I've directly used API endpoint as key for storing in cache. This way you don't have to provide unique keys every time, as endpoint itself is unique.
Note that in cases where you want to delete cached data, when making POST or PUT call, you can call CacheAPIService.clear(apiEndpoint) to clear details from cache.
$http.get will return a promise.
To correctly set data to dataService:
employeeService.getDetails($scope.eid).then(function(result) {
dataService.setData(result.data, 'result'); // note .data
$scope.isLoading = true;
console.log(dataService.getData('result'));
})
Storing the promise in your "cache" service is not the best option.
I expect getEmployeeDetails() function is going to route to empController, so if you still store $promise in your service as you stated, you can do.
dataService.getData('result').then(function(result){
$scope.result = result.data;
});
When $scope.result is set to $scope, ng-repeat will start iterating over.
With the first change suggested, you just don't need to touch empController but just the ng-repeat directive emp-list.html with:
<tr ng-repeat="oemp in result">
Refactor the http call with factory recipe and $resource.
https://docs.angularjs.org/api/ngResource/service/$resource
That is easier and quicker than writing providers.
The following is a mock up of what I have using data from the angular site. The goal is to remove any items in scope2 (newdevices) that already exist in scope1 (devices). I have a working model but do not feel it is the best method.
I have a controller that draws data from two different sources. For simplicity i have made the first scope static, whereas the second will take data via httpget from the angular site and this is initiated from a button click. (My prod code needs to use a button so i can inject variables into the call)
app.controller('customersCtrl', function($scope, $http) {
//Example static data for scope 1
$scope.devices = [
{"Name":"Around the Horn","City":"London","Country":"UK"},
{"Name":"B's Beverages","City":"London","Country":"UK"},
{"Name":"Chop-suey Chinese","City":"Bern","Country":"Switzerland"}
];
//scope 2 data from angular example site that is initiated from a button
$scope.loaddata = function() {
$http.get("http://www.w3schools.com/angular/customers_mysql.php")
.then(function (response) {
$scope.newdevices = response.data.records;
});
}
});
I then have a filter that compares the scopes:
app.filter('matcher', function() {
return function(newdevices, devices) {
var array2Ids = []
angular.forEach(devices, function(value, index) {
array2Ids.push(value.Name);
})
return newdevices.filter(function(val) {
return array2Ids.indexOf(val.Name) === -1;
})
}
});
Lastly, I apply the filter to my ng-repeat call:
<div ng-app="myApp" ng-controller="customersCtrl">
<button ng-click="loaddata()">load me</button>
<table>
<tr ng-repeat="x in newdevices | matcher: devices">
<td width="300px">{{ x.Name }}</td>
<td width="150px">{{ x.City }}</td>
<td width="100px">{{ x.Country }}</td>
</tr>
</table>
</div>
As mentioned, this currently works, but as I am already calling the second scope httpget from a function, is there a way I can integrate the filter into the loaddata function, so it happens all at once and can eliminate the need to filter on the ng-repeat stage?
I am still relatively new to this and have not yet been able to accomplish it.
you don't need an angular "filter". Just filter the response data before it gets assigned to $scope.newdevices. below code has been tested, but you get the idea.
$scope.loaddata = function() {
$http.get("http://www.w3schools.com/angular/customers_mysql.php")
.then(function (response) {
//do things here, i.e.
var array2Ids = [];
angular.forEach(devices, function(value, index) {
array2Ids.push(value.Name);
});
$scope.newdevices = response.data.records.filter(function(val) {
return array2Ids.indexOf(val.Name) === -1;
});
});
}
Controllers and services can retrieve filters using the $filter service.
var matcherFn = $filter('matcher');
var result = marcherFn(newdevices, devices);
AngularJS filters can be used both in templates and JavaScript.
The example in the Docs:
angular.module('filterExample', [])
.controller('MainCtrl', function($scope, $filter) {
$scope.originalText = 'hello';
$scope.filteredText = $filter('uppercase')($scope.originalText);
});
For more information, see AngularJS $filter Service API Reference.
This question already has answers here:
'this' vs $scope in AngularJS controllers
(7 answers)
Closed 6 years ago.
I am trying to populate a list of employee objects from my controller empctrl in a template.
Here's the controller:
app.controller('employeeController', function ($scope, employeeService) {
this.employees = {};
this.populateTable = function (data) {
this.employees = data;
};
var error = function (err) {
console.log("Error: " + err);
};
// Call Service to List all Employees
console.log("Service called to populate table.");
employeeService.output().then(this.populateTable, error);
this.populateTable();
});
However, this code that I wrote isn't working:
<div ng-repeat="employee in empctrl.employees.allEmployees" class="table_row">
<div class="table_column" style="width:2%">{{ $index + 1 }}</div>
<div class="table_column" style="width:8%">{{ employee.employeeName}}</div>
<!-- 7 more columns -->
</div>
Nothing shows up in the UI.
Instead, if I write $scope.employees in the controller, it works:
<div ng-repeat="employee in employees.allEmployees" class="table_row">
Since I know how tempting it is to do $scope.<everything> in the controller, I'm trying to avoid using $scope as much as possible.
If someone could demonstrate the proper use of $scope and difference betwee alias.abc and $scope.abc (where alias is an alias of controller), I'll be thankful.
Edit: Exact same question is this: 'this' vs $scope in AngularJS controllers
Thanks for this link, PankajParkar.
The problem is this which you are accessing inside populateTable function is not this which you have there in your controller function.
Better do keep this variable inside some variable, so that by having it you will make sure you are referring to correct object.
Controller
app.controller('employeeController', function ($scope, employeeService) {
var vm = this;
vm.employees = {};
vm.populateTable = function (data) {
vm.employees = data;
};
var error = function (err) {
console.log("Error: " + err);
};
// Call Service to List all Employees
console.log("Service called to populate table.");
employeeService.output().then(vm.populateTable, error);
vm.populateTable();
});
For more detail, I'd highly recommend you to readup on this article
If you are confused with this vs scope then do read up on this answer
Add your variables to the $scope instead of this like:
$scope.customers = {};
$scope.populateTable = function (data) {
$scope.employees = data;
};
Edit: both methods work. See this article for a in depth explanation.
Substituting "this" to vm (View-Model) will solve your issue. Not polluting $scope object is a groovy thing. this is a global context and its value depends on a function call.
So, in your controller just assign,
var vm = this;
vm.empTable = function (data) {
vm.employeeList = data.data;
};
..and use the vm object elsewhere in your controller. It will be useful to keep the code clean while working with multiple controllers in a view.
Don't forget to give an alias name to the controller,
<div ng-controller="MainCtrl as main">
<div ng-repeat=" employee in main.vm.employeeList ">
{{employee.name}}
</div>
</div>
I have recently started to learn angularjs using restangular to talk to my restfull API (sails). The problem I have stumbled upon is that the ng-repeat does not update after I change the list in the scope.
Controller:
app.controller('UsersCtrl', ['UsersSvc', '$scope', function(UsersSvc, s) {
UsersSvc.getList().then(function (new_users) {
s.users = new_users;
})
s.destroy = function (user) {
user.remove().then(function () {
s.users = _.without(s.users, user);
});
}
}]);
Service:
app.factory('UsersSvc', function(Restangular) {
return Restangular.all('users');
});
Template:
<div ng-controller="UsersCtrl">
...
<tr ng-repeat"user in users">
<td>{{user.firstName}}</td>
<td>{{user.lastName}} </td>
<td>{{user.emailAddress}}</td>
<td>{{user.age}}</td>
</tr>
...
</div>
When I inspect the scope the array of restangular objects is correctly assigned to the scope of the users controller but the template refuses to update.
Thanks in advance
AngularJS (and javascript) care about references vs. overwrites. So to be safe I always set my scope variables initially, and then update using angular.copy() or Restangular.copy() (if it's a Restangular object being set).
Below is how I'd refactor your controller to ensure bindings + digest cycles stay connected.
(Please note I renamed s to the "traditional" $scope for easier reading for everyone else)
app.controller('UsersCtrl', ['$scope', 'UsersSvc', 'Restangular', function($scope, UsersSvc, Restangular) {
// we're expecting a list, so default as array
$scope.users = [];
UsersSvc.getList().then(function (new_users) {
// In normal $resource/ng projects use: angular.copy(src, dst) but
// Restangular has an issue when using angular.copy():
// https://github.com/mgonto/restangular/issues/55
// so use their version of copy():
Restangular.copy(new_users, $scope.users);
});
$scope.destroy = function (user) {
user.remove().then(function () {
$scope.users = _.without($scope.users, user);
});
}
}]);
I have multidimensional json:
{"cat":
{"total":12,"per_page":3,"current_page":1,"last_page":4,"from":1,"to":3,
"data":[{"id":1,"emlakKategori":"a"},{"id":2,"emlakKategori":"b"},{"id":3,"emlakKategori":"c"}]}}
And i am reaching to data via ng-repeat like this:
<tr ng-repeat="kat in kategoriList.cat.data">
<td>{{ kat.id }}</td>
<td>{{ kat.emlakKategori }}</td>
My service is:
app.factory('KategoriData',['$resource', 'api.config', function($resource, config) {
return $resource(config.apiBasePath + 'kategori',{}, {
query: {
isArray: false,
method: 'GET'
}
});
And my controller:
kategori.controller('KategoriListCtrl',['$scope', 'KategoriData', function($scope, KategoriData) {
$scope.kategoriList = KategoriData.query();
}
I wonder how can i assign data to variable inside controller and reach it in view with directly kategoriList instead of kategoriList.cat.data
Thanks.
You can make the assignment in your controller in the success callback of your query.
kategori.controller('KategoriListCtrl',['$scope', 'KategoriData', function($scope, KategoriData) {
KategoriData.query({},
function(data){
$scope.kategoriList = data.cat.data;
});
}
Actually your example is not valid anymore due to this (since v1.2.0-rc.3 )
deprecate promise unwrapping means that you can not bind a scope value to a promise and when the promise will be resolved a $digest will automatically unwrap it.
You should use a callback:
kategori.controller('KategoriListCtrl',['$scope', 'KategoriData', function($scope, KategoriData) {
KategoriData.query({}, function(data){
$scope.kategoriList.cat.data = data;
})
}