AngularJS second query not hitting service - javascript

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

Related

Update ng-repeat after ng-resource call is finished

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?

AngularJS: how should I set the params for $http dynamically?

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...

Variable in Angular http jsonp config

I am using http.jsonp to make cross domain calls and everything works fine, My config object is as below:
var config = {
params: {
action: "query",
prop: "revisions",
format: "json",
rvlimit: 50,
titles: 'obama',//works
// titles: val, //doesn't works
callback: "JSON_CALLBACK"
}
};
var url = "http://en.wikipedia.org/w/api.php?";
$http.jsonp(url, config).success(function(data) {
var pages = data.query.pages;
for (var pageid in pages)
{
$scope.revisions = pages[pageid].revisions;
break; // expect only one page
}
$scope.loaded = true;
});
when the titles has a static value of obama, it works fine however I added an input box from where I am getting the value and I am trying to set the value of the input box to titles and load the particular feed, however it is not working. I have reproduced the issue on jsfiddle,
Any Ideas how to fix this/
http://jsfiddle.net/L4qZZ/
I assume you need to fetch the contents after the user hits the "Go" button.
The problem with your code is that you are using val to set the title instead of $scope.val.
If you update that and then correct the code to make the HTTP request when user clicks the button, your data is fetched as expected.
Fiddle that demonstrates the solution.
Just type "obama" into the input and click the button to get your data.
since you are not using $scope for getting the ng-model values, use $scope.val instead of val
Try this out
var config = {
params: {
action: "query",
prop: "revisions",
format: "json",
rvlimit: 50,
titles: $scope.val,
callback: "JSON_CALLBACK"
}
};
It's better to reorganize your code a little. Move data loading functionality into a service like this one:
app.factory('Wiki', function ($q, $http) {
return {
loadData: function (title) {
var deferred = $q.defer(),
config = {
params: {
action: "query",
prop: "revisions",
format: "json",
rvlimit: 50,
titles: title,
callback: "JSON_CALLBACK"
}
},
url = "http://en.wikipedia.org/w/api.php?";
$http.jsonp(url, config).success(function(data) {
var pages = data.query.pages;
for (var pageid in pages) {
deferred.resolve(pages[pageid].revisions);
break;
}
});
return deferred.promise;
}
};
});
And then you can use it like this in controller:
app.controller('HistoryController', function($scope, Wiki) {
$scope.loaded = false;
$scope.getData = function(val) {
Wiki.loadData(val).then(function(data) {
$scope.loaded = true;
$scope.revisions = data;
});
}
$scope.getData('obama');
});
Using services makes everything more flexible when you deal with data.
Demo: http://jsfiddle.net/L4qZZ/1/

How to setup CURD with KendoUI grid for use with Kendo-Angular and an Angular OData factory?

In a previous project, where I was not using Angular, I setup a Kendo.DataSource that used an OData endpoint as follows:
var userDS = new kendo.data.DataSource({
type: "odata",
transport: {
read: {
url: "/api/Users?$filter=USERGROUPS/any(usergroup: usergroup/ID eq '" + groupData.ID + "')", // only need to expand users for the selected group
dataType: "json", // the default result type is JSONP, but WebAPI does not support JSONP
},
update: {
url: function (data) {
// TODO: write UpdateEntity controller method
return "/api/Users(" + groupData.ID + ")";
},
dataType: "json"
},
destroy: {
url: function (data) {
// TODO: write Delete controller method
return "/api/Users(" + groupData.ID + ")";
},
dataType: "json"
},
parameterMap: function (options, type) {
// this is optional - if we need to remove any parameters (due to partial OData support in WebAPI
var parameterMap = kendo.data.transports.odata.parameterMap(options);
return parameterMap;
}
},
Now, introducing AngularJS into the mix, I would like to know how to define the read, update and destroy events using my AngularJS factory, where there is no URL.
My factory contracts are setup as follows:
contentTypesFactory.getList()
contentTypesFactory.insert(contentType)
contentTypesFacotry.remove(id)
The first problem I see with .getList() is that it doesn't take in any query string parameters, like $orderby and $inlinecount=allpages which I need for use with the KendoUI Grid. It is inside this factory that the URL is defined, then calls an abstract factory (see below).
I need to somehow pass in the URL and the entity name to my factory from the kendo.datasource url: function (remember, that the grid control will append whatever OData querystring parameters are required).
So, how I would setup the factory to output the data expected for each of the CRUD events.
Data source definition:
$scope.contentTypesDataSource = new kendo.data.HierarchicalDataSource({
type: "odata",
transport: {
read: {
//url: "/api/UserGroups?$orderby=GROUPNAME",
url: '/odata/ContentTypes',
//function (data) {
// pass in the URL to the abstract factory
//},
dataType: "json" // the default result type is JSONP, but WebAPI does not support JSONP
},
update: {
},
destroy: {
},
parameterMap: function (options, type) { ...
Abstract repository:
app.factory('abstractRepository', [function () {
// we will inject the $http service as repository service
// however we can later refactor this to use another service
function abstractRepository(repositoryService, whichEntity, odataUrlBase) {
//this.http = $http;
this.http = repositoryService;
this.whichEntity = whichEntity;
this.odataUrlBase = odataUrlBase;
this.route;
}
abstractRepository.prototype = {
getList: function () {
return this.http.get(this.odataUrlBase);
},
get: function (id) {
return this.http.get(this.odataUrlBase + '/' + id);
},
insert: function (entity) {
return this.http.post(this.odataUrlBase, entity);
},
update: function (entity) {
return this.http.put(this.odataUrlBase + '/' + entity.ID, this.whichEntity);
},
remove: function (id) {
return this.http.delete(this.odataUrlBase + '/' + id);
}
};
abstractRepository.extend = function (repository) {
repository.prototype = Object.create(abstractRepository.prototype);
repository.prototype.constructor = repository;
}
return abstractRepository;
}]);
ContentTypesFactory.js:
// each function returns a promise that can be wired up to callback functions by the caller
// the object returned from the factory is a singleton and can be reused by different controllers
app.factory('contentTypesRepository', ['$http', 'abstractRepository', function ($http, abstractRepository) {
var odataUrlBase = '/odata/ContentTypes'
var whichEntity = 'ContentTypes';
function contentTypesRepository() {
abstractRepository.call(this, $http, whichEntity, odataUrlBase);
}
abstractRepository.extend(contentTypesRepository);
return new contentTypesRepository();
}]);
After looking at kendo-examples-asp-net, I'm thinking that I should do away with the ContentTypesFactory and the abstract repository and call the OData endpoint directly - of course this is relatively easy.
However, my initial reason for creating an Angular repository was so that I could do JS unit testing on the data functions. To retain this feature, how can I call the abstract repository directly from the data source functions, and this the recommended way of accomplishing this?

AngularJS service depending on resource from 2nd service

Maybe I'm going about this the wrong way, i'm not sure, but I have 2 services, one is a user service which gets a bunch of details about the user from the server, the other being one that relies on some user details from the user service and then makes some more calls to the server to get other information.
Anyway, because of the async stuff that goes on when the 2nd service makes the calls the information required from the user server has not yet been populated.
I know Angular services can depend on one another, but not in this context it would appear?
factory('User', ['$resource', function ($resource) {
return $resource(usersUrl, {}, {
//The data model is loaded via a GET request to the app
query: {method: 'GET', params: {}, isArray: false},
putupdate: {method: 'PUT', params:{}}
});
}])
.factory('UserData', function() {
var data = {}
data.userinfo = {};
if(data = {}){
}
return {
updateinfo: function(newdata) {
data.userinfo = newdata;
// alert(data.userinfo.user)
},
userinfo: data
}
})
.factory('PlansData', ['UserData', 'User', '$rootScope', function(userData, user, $rootScope) {
var data = {}
data.plansinfo = {};
//alert(userData.data.userinfo.user.email)
if(data = {}){
}
return {
updateinfo: function(newdata) {
alert(user.query())
data.plansinfo = newdata;
},
plansinfo: data
}
}])
So I have a user service and a caching userdata service, but if I ever try and call anything from UserData in the PlansData service I get undefined.
How do I get plansData to wait for UserData to have some data?
Thanks
Tom
I'm not sure what you're trying to accomplish, but this line of code:
if(data = {}){
}
In both your services is wiping out your data object. You're setting the whole data object to be {}

Categories