I am using ngResource in combination with ng-repeat and noticed that slow REST calls doesn't update the list properly. It keeps empty.
As far as I understood I need a binding between controller and ng-repeat element.
My resource and controller definition:
(function (configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource, REST_CONFIG) {
return {
Technical: $resource(REST_CONFIG.baseUrl + REST_CONFIG.path + '/', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function (configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
loggingResource.Technical.query(function (data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
ng.repeat usage:
<tr class="table-row" ng-repeat="log in logController.logs">
What I have tried so far:
ng-bind in combination with ng-repeat
$q with deferrer
$promise of ngResource
What did I miss?
My try to get it on plnkr: https://plnkr.co/edit/t1c5Pxi7pzgocDMDNITX
The {{}} provides a default binding between your controller and view and you don't need to add anything explicitly. I have updated your plunkr with some minor changes to injected constants etc. and it is working.
// Code goes here
angular.module("loggingModule", ['ngResource']);
(function(configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource) {
return {
Technical: $resource('https://api.github.com/users/addi90/repos', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function(configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
that.logs = [{
title: 'test2'
}];
loggingResource.Technical.query(function(data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
Since the api resource was not working, I have used my github repo api link there
The updated working plunkr is available here: https://plnkr.co/edit/cederzcAGCPVzc5xTeac?p=preview
Try to use
that.logs.push(data)
so you prevent to reinitialise the logs list. If you override the logs list and the ng-repeat is initialised before the logs list it seems to not be resolved.
Your logs list is set correctly after the rest call?
Related
I've been trying to make a request to a NodeJS API. For the client, I am using the Mithril framework. I used their first example to make the request and obtain data:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://localhost:3000/store/all"});
}
};
var Component = {
controller: function() {
var stores = Model.getAll();
alert(stores); // The alert box shows exactly this: function (){return arguments.length&&(a=arguments[0]),a}
alert(stores()); // Alert box: undefined
},
view: function(controller) {
...
}
};
After running this I noticed through Chrome Developer Tools that the API is responding correctly with the following:
[{"name":"Mike"},{"name":"Zeza"}]
I can't find a way to obtain this data into the controller. They mentioned that using this method, the var may hold undefined until the request is completed, so I followed the next example by adding:
var stores = m.prop([]);
Before the model and changing the request to:
return m.request({method: "GET", url: "http://localhost:3000/store/all"}).then(stores);
I might be doing something wrong because I get the same result.
The objective is to get the data from the response and send it to the view to iterate.
Explanation:
m.request is a function, m.request.then() too, that is why "store" value is:
"function (){return arguments.length&&(a=arguments[0]),a}"
"stores()" is undefined, because you do an async ajax request, so you cannot get the result immediately, need to wait a bit. If you try to run "stores()" after some delay, your data will be there. That is why you basically need promises("then" feature). Function that is passed as a parameter of "then(param)" is executed when response is ready.
Working sample:
You can start playing with this sample, and implement what you need:
var Model = {
getAll: function() {
return m.request({method: "GET", url: "http://www.w3schools.com/angular/customers.php"});
}
};
var Component = {
controller: function() {
var records = Model.getAll();
return {
records: records
}
},
view: function(ctrl) {
return m("div", [
ctrl.records().records.map(function(record) {
return m("div", record.Name);
})
]);
}
};
m.mount(document.body, Component);
If you have more questions, feel free to ask here.
I am attempting to build a generic filter which needs to be able to handle - based on configuration - parameters. Both a resource instance and a service.
I have the following factory code:
angular.module('sample').factory('Sample', function($resource) {
var methods = {
search: {
method: 'POST',
isArray: true
}
}
var Sample = new $resource('/sample', methods);
Sample.prototype.elasticFilter = function (search) {
var query = {
query: {
filtered: {
query: {
wildcard: {
name: '*' + search.toLowerCase() + '*'
}
}
}
}
}
return this.$search(query);
}
return Sample;
});
Now if I do the following in my directive:
scope.search = function (search) {
Sample.elasticFilter(search).$promise.then(function () {
handle
});
this results in
TypeError: Cannot read property 'then' of undefined
printing out Sample.elasticFilter(search), the result is directly a Promise.
Modifying the directive to read directily off Sample.elasticFilter(search).then results in:
TypeError: value.push is not a function.
For non-instance-resources (e.g. NOT newed up, rather service directly), .$promise works fine.
Why is this happening? How can I work around it? When does a resource have a $promise, and when does it not?
This appears to be a "feature" https://github.com/angular/angular.js/issues/11767
The only way to really work around this is to not use isArray on instance level resource methods.
I have a .NET webservice that returns an object like:
myObj = {
prop1: value,
prop2:value,
...
prop5:value
}
I created an angular service that returns this entire object(myObj).
I created 5 distinct directives to display these properties in different pages in the application(sometimes, some of them can be in the same page).
I'm calling the angular service in these directives, creating for any of them this "link" function:
link: function (scope, element, attrs) {
getService.getMethod().$promise.then(
function (myObj) {
element.text(myObj.prop1); // .prop2, ... , prop5
},
function (statusCode) {
console.log(statusCode);
}
);
}
I have the feeling that my approach is not the best, calling five times the angular service(through the $promise) obtaining actually the same object(myObj).
If you are interested also how the service is looking:
var localResource = $resource('https://.....',
{},
{'getAll': {method: 'JSONP', isArray: false, params: {callback: 'JSON_CALLBACK'}}}
);
return {
getMethod: function () {
return localResource.getAll();
}
}
Please help, if someone has an ideea haw can I improve it.
Thank you!
It sounds like you can make your getMethod issue only one server call.
.factory("getService", function () {
var getAll;
return {
getMethod: function () {
if (!getAll) {
getAll = localResource.getAll();
}
return getAll;
};
};
});
I am very new with AngularJS. Thank you for answer. My code is as follow:
mainModule.controller('MainController', function($scope, $http) {
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'IndexPageEntity'}}).
success(function(data) {
$scope.intro = data[0].IndexPageContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ExhibitionServiceEntity'}}).
success(function(data) {
$scope.exhibit = data[0].ExhibitionServiceContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ShootingServiceEntity'}}).
success(function(data) {
$scope.shooting = data[0].ShootingServiceContent;
});
});
My html file would be:
<div ng-controller="MainController">
<div>{{intro}}</div>
<div>{{exhibit}}</div>
<div>{{shooting}}</div>
</div>
I believe there must be some ways to improve the above code in order to reduce repetition. What I want is to pass entity parameter to the controller on creation.
Using ng-init to pass parameter is discouraged, according to the documentation. Writing custom directive to pass argument to scope does not work since parameters would be overwrittern.
What is the best practice to set params dynamically for use in $http? Thank you.
You should move all the logic to a service and use a directive. I would suggest you to modify your backend to return the same structured data, instead of IndexPageContent, ExhibitionServiceContent, etc. it should be Content or whatever name you want to use. But for now I've added a replace function to get the name of the content from the name of the entity.
mainModule.factory('webService', function($http) {
var apiUrl = 'http://localhost/backend/WebService.php';
function getContent(params) {
var config = {
'params': params
};
return $http.get(apiUrl, config);
};
return {
getContent: function(params) {
return getContent(params)
}
};
});
mainModule.controller('MainController', function($scope, webService) {
var params = {
'entity': $scope.entity
};
var contentName = $scope.entity.replace('Entity', 'Content');
webService.getContent(params).then(function (data) {
$scope.content = data[0][contentName];
});
});
mainModule.directive('EntityContent', function() {
return {
controller: 'MainController',
replace: true,
restrict: 'E',
scope: {
entity: '#entity'
},
template: '<div>{{ content }}</div>'
};
});
<div>
<entity-content entity="IndexPageEntity">
<entity-content entity="ExhibitionServiceEntity">
<entity-content entity="ShootingServiceEntity">
</div>
Create an object data and send the value for the key inside the object at every call.. Also pass the value for key to be set inside the scope..
E.g.
$scope.makeHttpCall = function(data) {
$http.get('http://localhost/backend/WebService.php', {params: data}).
success(function(data) {
$scope[$scope.key] = data[0][$scope.key];
});
};
you can then call this function as
$scope.key = 'IndexPageContent';
data = {
entity : 'yourValueHere'
};
$scope.makeHttpCall(data);
You can set other values as well inside the scope that are dynamic for each request..
I hope this makes sense to you...
I currently have a factory that looks like this:
ChecklistApp.factory('Api', ['$resource', function ($resource) {
return {
Checklists: $resource('api/checklists', {}, { 'query': { method: 'GET', isArray: false } }),
Checklist: $resource('api/checklist', {}, { 'query': { method: 'GET', isArray: false } }),
AddChecklist: $resource('api/addchecklist', {}, { 'query': { method: 'POST' } }),
UpdateChecklist: $resource('api/updatechecklist', {}, { 'query': { method: 'PUT' } })
};
}]);
I have two controllers that use this factory
a list controller - which lists all checklists
an update controller - which displays one checklist and allows its detailed to be modified
The list controller assigns the data to a variable which in turn is bound to the UI as follows:
$scope.search = function () {
Api.Checklists.query({ Name: $scope.searchName },
function (data) {
$scope.checklists = data.checklists;
}
);
};
In my edit controller I have the following update function which successfully updates the data in the DB and returns the user to the home (list) page.
var EditCtrl = function ($scope, $location, $routeParams, Api) {
$scope.action = "Update";
var id = $routeParams.editId.replace(/\D+/, '');
Api.Checklist.query({ id: id },
function (qd) { $scope.item = qd.checklist; }
);
$scope.update = function () {
Api.UpdateChecklist.save({ Id: $scope.item.id, Name: $scope.item.name },
function (data) {
$scope.item = data.checklist[0];
$scope.$apply();
$location.path('/#'); //Return to list controller
}
);
}
My issue is that after data is modified in my edit controller, I navigate back to the list control and although it hits the search query in the javascript it does not hit the service endpoint on the second call (skips it altogether) and the data is not refreshed (so the modified checklist has been updated on the DB but in the view on list control is remains as it was).
So my question is
How can I forcefully load the data again from the db using the same query that was run to load data initially ($scope.search in list control >> why does it skip this when the page is navigated to for the second time?) and/or alternatively is there a better way to just share the collection over multiple controllers (I read about nesting the scopes and putting the collection in the parent scope which could be accessed by both controllers but not sure if this is best practice or a suitable solution?)
Thanks