How to prime angular $http cache ($cacheFactory) with static JSON content? - json

My goal is to get $http to work on my local filesystem by caching some static JSON objects in a $cacheFactory. I wish to avoid network requests entirely and use only cached content.
The issue is that $http is making server requests regardless of the existence of cached content. My code is as follows.
Cache Factory
myApp.factory('jsonCache', function($cacheFactory){
// create new cache object
// (tried $cacheFactory.get('$http') as well, but same result)
var cache = $cacheFactory('jsonCache');
// put static value in cache
cache.put('/json/file1.json', {"key":"value"});
return cache;
});
Factory using $http
myApp.factory('AjaxFactory', function($http, jsonCache){
console.log(jsonCache.info()); // {id: 'jsonCache', size: 1}
// this will make a request to "http://localhost/json/file1.json"
// even though there is an entry for that URL in the cache object
$http.get('/json/file1.json', {cache: jsonCache}).success(/* ... */);
return { /* ... */ };
});
At this point I'm thinking it may be the format of the data I'm using in cache.put(), but unsure.

Please see demo code below, commends should help you a bit
var app = angular.module('app', ['ui.router']);
app.config(function($stateProvider, $urlRouterProvider) {
//
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/state1");
//
// Now set up the states
$stateProvider.state('state1', {
url: "/state1",
template: "<h1>State1 </h1> <pre>{{cache | json}}</pre>",
controller: 'state1Ctrl'
})
.state('state2', {
url: "/state2",
template: "<h1>State2 </h1><pre>{{cache | json}}</pre>",
controller: 'state2Ctrl'
});
});
app.controller('state1Ctrl', function($scope, myCache) {
var cache = myCache.cache.get('jsonCache');
//check if cached data exist
if (cache) {
//use cached data
$scope.cache = myCache.cache.get('jsonCache');
//if not update cache
} else {
myCache.update().success(function(data) {
//set cache
myCache.cache.put('jsonCache', data.info);
console.log(myCache.cache.info());
//get cached data
$scope.cache = myCache.cache.get('jsonCache');
}).error(function() {
console.log("error");
});
}
});
app.controller('state2Ctrl', function($scope, myCache) {
var cache = myCache.cache.get('jsonCache');
if (cache) {
$scope.cache = myCache.cache.get('jsonCache');
} else {
myCache.update().success(function(data) {
myCache.cache.put('jsonCache', data.info);
console.log(myCache.cache.info());
$scope.cache = myCache.cache.get('jsonCache');
}).error(function() {
console.log("error");
});
}
});
app.factory('myCache', function($cacheFactory, $http) {
// create new cache object
var cache = $cacheFactory('jsonCache');
// put static value in cache
function update() {
alert("update")
return $http.get("https://ws.spotify.com/search/1/track.json?q=kaizers+orchestra");
}
return {
cache: cache,
update: update
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
<script src="
https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.13/angular-ui-router.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<body ng-app="app">
<div ui-view></div>
<!-- We'll also add some navigation: -->
<a ui-sref="state1">State 1</a>
<a ui-sref="state2">State 2</a>
</body>

I was actually able to get it working as desired on this plunk http://plnkr.co/edit/x1nfjwEoJOxzZN5PUyrX?p=preview
angular.module("myApp", [])
.factory('jsonCache', function($cacheFactory) {
// create new cache object
// (tried $cacheFactory.get('$http') as well, but same result)
var cache = $cacheFactory('jsonCache');
// put static value in cache
cache.put('file1.json', {
"key": "From Cache Factory"
});
return cache;
})
.factory('jsonFactory', function($http, jsonCache) {
var get = function(url) {
return $http.get(url, {
cache: jsonCache
});
};
return {
get: get
};
})
.controller("Ctrl", function($scope, jsonFactory, jsonCache) {
$scope.cacheInfo = jsonCache.info();
jsonFactory.get('file1.json').success(function(res) {
$scope.json = res;
});
});
I think the issue with my original code was the result of one of the many 3rd party module dependencies. (doh!)
My workaround for the code as it was, was the following:
myApp.factory('jsonFactory', function($http, $q, jsonCache){
var get = function(url){
var data = jsonCache.get(url);
// if data exists in cache, wrap in promise and return
// or do regular $http get
if(data){
return $q(function(resolve, reject){ resolve(data); });
} else {
return $http.get(url);
}
};
return {
get: get
};
});

Related

Login Service implementation in angularjs not working

This is my controller which is calling the login service
mod.controller("loginCtrl",function($scope,loginService,$http)
{
$scope.Userlogin = function()
{
var User = {
userid :$scope.uname,
pass:$scope.pass
};
var res = UserloginService(User);
console.log(res);
alert("login_succ");
}
});
And this is the login service code which takes the User variable and checks for username & password
mod.service("loginService",function($http,$q) {
UserloginService = function(User) {
var deffered = $q.defer();
$http({
method:'POST',
url:'http://localhost:8080/WebApplication4_1/login.htm',
data:User
}).then(function(data) {
deffered.resolve(data);
}).error(function(status) {
deffered.reject({
status:status
});
});
return deffered.promise;
// var response = $http({
//
// method:"post",
// url:"http://localhost:8080/WebApplication4_1/login.htm",
// data:JSON.stringify(User),
// dataType:"json"
// });
// return "Name";
}
});
I have created a rest api using springs which upon passing json return back the username and password in json like this
Console shows me this error for angular
You need to enable CORS for your application for guidance see this link
https://htet101.wordpress.com/2014/01/22/cors-with-angularjs-and-spring-rest/
I prefer to use Factory to do what you're trying to do, which would be something like this:
MyApp.factory('MyService', ["$http", function($http) {
var urlBase = "http://localhost:3000";
return {
getRecent: function(numberOfItems) {
return $http.get(urlBase+"/things/recent?limit="+numberOfItems);
},
getSomethingElse: function(url) {
return $http.get(urlBase+"/other/things")
},
search: function (searchTerms) {
return $http.get(urlBase+"/search?q="+searchTerms);
}
}
}]);
And then in your controller you can import MyService and then use it in this way:
MyService.getRecent(10).then(function(res) {
$scope.things = res.data;
});
This is a great way to handle it, because you're putting the .then in your controller and you are able to control the state of the UI during a loading state if you'd like, like this:
// initialize the loading var, set to false
$scope.loading = false;
// create a reuseable update function, and inside use a promise for the ajax call,
// which is running inside the `Factory`
$scope.updateList = function() {
$scope.loading = true;
MyService.getRecent(10).then(function(res) {
$scope.loading = false;
$scope.things = res.data;
});
};
$scope.updateList();
The error in the console shows two issues with your code:
CORS is not enabled in your api. To fix this you need to enable CORS using Access-Control-Allow-Origin header to your rest api.
Unhandled rejection error, as the way you are handling errors with '.error()' method is deprecated.
'Promise.error()' method is deprecated according to this and this commit in Angular js github repo.
Hence you need to change the way you are handling errors as shown below :
$http().then(successCallback, errorCallback);
function successCallback (res) {
return res;
}
function errorCallback (err) {
return err;
}
One more thing in your code which can be avoided is you have defined a new promise and resolving it using $q methods, which is not required. $http itself returns a promise by default, which you need not define again inside it to use it as a Promise. You can directly use $http.then().

Angular factory does not return array of objects but a single object

I am new to angular and I am trying to load a CSV list inside a factory and then convert it to json. I am using Papaparse (CSV to json library) inside the factory. When I console log the factory I get the array of objects which is exactly what I want but when I pass it inside my controller I get a single object which holds all the data.
This is my factory
(function() {
var app = angular.module('test');
app.factory('testFactory', ['$http', function($http) {
var url = 'my-list.csv';
var getContact = function() {
return $http.get(url).success(function(data) {
Papa.parse(data, {
header: true,
complete: function(results) {
console.log(results.data);
return results.data;
}
});
});
};
return {
getContact: getContact
};
}]);
}());
And this is my controller
(function() {
var app = angular.module('test');
app.controller('testCtrl', ['$scope', 'testFactory', function($scope, testFactory) {
testFactory.getContact().then(function(data) {
$scope.contacts = data;
console.log(data);
});
}]);
}());
I want be able to do something like this inside my view
{{ contact.firstname }}
The issue is the order of resolution. Inspecting the console statements shows that you're assigning $scope.contacts to the resolution of the $http.get promise, and not the actual parsing.
Instead of returning the $http.get promise, return a deferred promise and resolve at the end of parsing:
var parsePromise = $q.defer();
$http.get(url).success(function(data) {
Papa.parse(data, {
header: true,
complete: function(results) {
console.log(results.data);
parsePromise.resolve(results.data);
}
});
});
return parsePromise.promise;
See working demo here.
Update: As per the comments, you could use .then to chain promises instead of creating a new deferred. The plunkr has both, you can use the changelog to toggle methods.

How can I pass data from a service into my controller?

Okay so right now I am messing around with the ionic framework and learning angularJS at the same time. I just came across $q and async calls, but I just can't seem to get it right. I want to be able to parse a JSON file which I already have set up using GetJsonSpecials then pass that to GetData which will then pass it to my controller SpecialsCtrl so I can attach it to $scope. I know I am not understanding the promises correctly because everything inside SpecialService undefined. I can get the data perfectly fine from the other two serivces, but when I try passing it to SpecialService it all just seems to crumble which in turn ends up as undefined in my controller. Maybe I am not going about this the right way? Are there any best practices of doing this kind of thing?
angular.module('starter.controllers', [])
.controller('SpecialsCtrl', function ($scope, SpecialService) {
$scope.specials = SpecialService.all();
console.log("Specials Controller: Got Data", $scope.specials);
})
//Create methods to access the specials inside the controller in which we inject this in
.factory('SpecialService', function (GetData) {
var specials = GetData.getSpecials();
console.log("DATAAAA: ", specials);
return {
// Return all specials
all: function () {
console.log("Inside return with specials: ", specials);
return specials;
},
getSpecialWithId : function (specialId) {
// Simple index lookup
return specials[i];
}
}
}
})
.factory('GetData', function(GetJsonSpecials) {
return {
getSpecials : function() {
GetJsonSpecials.retrieveData().then(function (data) {
console.log("Got the JSON data", data);
return data;
}, function (status) {
alert("Error getting specicals", status);
console.log("Error getting specicals", status);
});
}
}
})
//Asynchronously get the specials from the json file
.factory('GetJsonSpecials', function ($q, $http) {
return {
retrieveData : function() {
var deferred = $q.defer();
$http.get('js/specials.json').success(function (data, status) {
deferred.resolve(data);
}).error(function (status) {
deferred.reject(status);
console.log("Error in handling json!");
});
return deferred.promise;
}
}
})
The reason I have this overly complicated is because in the end I want to be able to share the data to another controller which will display that specific specials' properties in a new view.
.controller('DetailCtrl', function ($scope, $stateParams, JsonSpecials, $firebaseAuth) {
$scope.id = parseInt($stateParams.specialId);
$scope.special = JsonSpecials.getSpecialWithId($scope.id);
})
There are a few problems here. The main issue being that angularjs promises are asynchronous and you're trying to use them in a synchronous manner.
First off, you having an extra } after your SpecialService definition.
In your SpecialService:
.factory('SpecialService', function (GetData) {
var specials = GetData.getSpecials();
This will be nothing because your GetData.getSpecials() returns nothing.
If you were to fix GetData.getSepcials to return:
getSpecials : function() {
return GetJsonSpecials.retrieveData().then(function (data) {
console.log("Got the JSON data", data);
return data;
}, function (status) {
alert("Error getting specicals", status);
console.log("Error getting specicals", status);
});
}
then back in your SpecialService, you need to change how you get the data back.
var specials = GetData.getSpecials();
won't give you your data either. It will be a promise because it is asynchronous. So it needs to be
GetData.getSpecials().then(function(data) {
return data;
});
Also, as Matt as pointed out, in your retrieveData definition, you're creating an unnecessary promise. So
retrieveData : function() {
var deferred = $q.defer();
$http.get('js/specials.json').success(function (data, status) {
deferred.resolve(data);
}).error(function (status) {
deferred.reject(status);
console.log("Error in handling json!");
});
return deferred.promise;
}
is the same as:
retrieveData : function() {
return $http.get('js/specials.json').error(function (status) {
console.log("Error in handling json!");
return status;
});
}
It seems you are over complicating things a bit. You're passing data around factories for no clear reason. I think those three factories could be combined into just one. Maybe try something like..
index.html
<!DOCTYPE html>
<html ng-app="foobar">
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div ng-controller="SpecialsCtrl">
{{specials}}
</div>
<div ng-controller="AnotherController">
{{specials}}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
script.js
angular.module('foobar', [])
.controller('SpecialsCtrl', function ($scope, JsonSpecials) {
JsonSpecials.retrieveData().then(function(data){
$scope.specials = data;
});
})
.controller('AnotherController', function ($scope, JsonSpecials) {
JsonSpecials.retrieveData().then(function(data){
$scope.specials = data;
});
})
//$http returns a promise anyway so you don't need $q
.factory('JsonSpecials', function ($http){
return {
retrieveData : function() {
return $http
.get('js/specials.json')
.error(function (status) {
console.log("Error in handling json!");
});
}
}
});

How is possible to load some setting from .json file before angular app starts

i'm building application which uses CORS requests. Each request i use get host address from a constant
angular.module('siteApp').constant('baseUrl', {
'server':'htttp://localhost/',
})
And in each service i use to send request like this:
angular.module('siteApp').factory('DocsSvc', function ($http, baseUrl) {
var baseurl = baseUrl.server ;
$http.get(baseurl + 'document')
Is it possible to make 'htttp://localhost/' value - to came from config.json file into baseUrl constant or baseUrl factory?
I mean : how can i load something from ajax request an make it accessible to app modules
i have tried:
.run(['$rootScope', , function ($rootScope) {
$.ajax('config.json', {async: false})
.success(function (data) {
$rootScope.HOST = data.HOST;
});
And tried to access it from baseUrl:
angular.module('siteApp').factory('baseUrl',function($rootScope) {
return {
server: $rootScope.HOST
But no luck - the baseUrl.server comes undefined into functions
You can use run method of angular.
var app = angular.module('plunker', []);
app.run(function($http, $rootScope){
$http.get('config.json')
.success(function(data, status, headers, config) {
$rootScope.config = data;
$rootScope.$broadcast('config-loaded');
})
.error(function(data, status, headers, config) {
// log error
alert('error');
});
})
app.controller('MainCtrl', function($scope, $rootScope) {
$scope.$on('config-loaded', function(){
$scope.name = $rootScope.config.name;
});
});
see this plunker
If you want to do it even before the angular app starts, you can, instead of using the ng-app directive, use the bootstrap function.
From:
https://docs.angularjs.org/api/ng/function/angular.bootstrap
<!doctype html>
<html>
<body>
<div ng-controller="WelcomeController">
{{greeting}}
</div>
<script src="angular.js"></script>
<script>
var app = angular.module('demo', [])
.controller('WelcomeController', function($scope) {
$scope.greeting = 'Welcome!';
});
// Do your loading of JSON here
angular.bootstrap(document, ['demo']);
</script>
</body>
</html>
You need to tell angular about data change, so modify your code something like this:
.run(['$rootScope', function ($rootScope) {
$.ajax('config.json', {async: false})
.success(function (data) {
$rootScope.HOST = data.HOST;
$rootScope.$apply(); // New line
});
}])
That $apply() is needed since its a non-angular asynchronous call.
use the blow code snippet to load the json values
.run(function ($http, $rootScope) {
$http.get('launchSettings.json')
.success(function (data, status, headers, config) {
$rootScope.config = data;
$rootScope.$broadcast('config-loaded');
})
.error(function (data, status, headers, config) {
// log error
alert('error');
});
});

view not gathering data from service through controller

Having trouble loading an external json file and having it's contents display on my view. I've included my view, controller and services code. What do I need to change?
view.html
<div ng-controller='BaseCtrl'>
<table class="table table-hover">
<tbody>
<tr class="tr-sep" ng-repeat="example in examples" ng-click="showUser(example)">
<td>{{example.name}}</td>
<td>{{example.type}}</td>
<td>{{example.size}}</td>
</tr>
</tbody>
</table>
</div>
controller.js
'use strict';
angular.module('projyApp')
.controller('BaseCtrl', function ($scope, data) {
$scope.examples = data.getAllExamples();
$scope.showUser = function(example) {
window.location = '#/user/' +example.size;
};
});
service.js
'use strict';
angular.module('projyApp')
.service('data', function data() {
var examples;
var getAllExamples = function () {
$http.get("../../TestData/Examples.json").success($scope.examples = data.examples);
};
});
Your service code isn't correct. I see the following problems:
You're creating a local variable getAllExamples that's not accessible from outside the service;
You're using the $http service, but that dependency isn't expressed in the service constructor;
You're trying to update the scope from the service, but it's inaccessible from there. Plus, the $scope variable is not even defined inside the service code.
Here's how your service could look like:
.service('data', function($http) {
this.getAllExamples = function(callback) {
$http.get("../../TestData/Examples.json")
.success(function(data) {
if (callback) callback(data.examples);
});
};
});
And your controller code would be like this:
.controller('BaseCtrl', function ($scope, data) {
data.getAllExamples(function(examples) {
$scope.examples = examples;
});
$scope.showUser = function(example) {
window.location = '#/user/' +example.size;
};
});
You could ditch the callback in the getAllExamples function and work directly with the $http.getreturned promise, but that's a bit more complicated.
Update Added a Plunker script to illustrate the code above.
Main module definition should look like:
angular.module("projyApp",[/*dependencies go here*/]);
Service should look like
//this use of module function retrieves the module
//Note from comments in angular doc: This documentation should warn that "angular.module('myModule', [])" always creates a new module, but "angular.module('myModule')" always retrieves an existing reference.)
angular.module('projyApp')
.service('dataService', [/*dependencies,*/function() {
var service = {
examples:[],
getAllExamples = function () {
$http.get("../../TestData/Examples.json").success(function(returnedData){examples = returnedData});
}
}
return service;
});
Controller should look like:
angular.module('projyApp')
.controller('BaseCtrl', function ($scope, dataService) {
$scope.examples = [];
$scope.showUser = function(example) {
window.location = '#/user/' +example.size;
};
$scope.$watch(function(){return dataService.examples}, function(newVal,oldVal) {$scope.examples = newVal});
});
Also you can add
debugger;
on an line to trigger Chrome to break (like a breakpoint but without having to dig through the scripts at run-time) so long as the Debugging Panel is open (F12)
You should use a callback instead of assigning in to a scope in you data service. By doing that, you can use this function in multiple controllers an assign values to appropriate scopes.
Data Service
var getAllExamples = function (callback) {
$http.get("../../TestData/Examples.json").success(function(data) {
if (typeof callback === "function") callback(data);
});
};
Controller
data.getAllExemples(function(data) {
$scope.examples = data;
});
EDIT
Another what is to create a promise object.
Data Service
var getAllExamples = function () {
return $http.get("../../TestData/Examples.json");
};
Controller
var promise = data.getAllExemples();
promise.then(function(data) {
$scope.examples = data;
});
EDIT 2
In your service, you need to return your functions
angular.module('projyApp')
.service('data', function data() {
var examples;
return {
getAllExamples: function () {
$http.get("../../TestData/Examples.json").success(...);
}
};
});