Angular json data showing in console, but not in the view - javascript

I am newbie at Angular and I don't really know how to solve this problem. I have looked online and read the documentation, but I haven't found a proper answer. I also asked coworkers about this issue. They couldn't figure it out to assist me, so I thought it would be best to ask you guys about what the best way to solve this is.
Basically, the app is supposed to change the json data when the user clicks link in the menu. It is supposed to grab the current index and then display the corresponding data based on that index in the array. I will post the code that I have so far.
Here is a link to the code on Plunker.
app.factory('quest', ['$http', function($http) {
return $http({
method: 'GET',
url: 'data/study.json'
}).success(function(data) {
return data;
})
.error(function(err) {
return err;
});
}]);

In order to use HTTP requests I would suggest to use the following pattern:
app.factory('quest', function($http, $q) {
var promise;
return {
getQuests: function() {
// $http returns a promise, so we don't need to create one with $q
promise = $http.get('data/study.json')
.then(function(data) {
return data;
}, function(err) {
return $q.reject(err);
});
return promise;
}
}
});
So later you can fetch the factory in your Controller with:
quest.getQuests()
.then(function(data) {
$scope.data = data;
}, function(res) {
if(res.status === 500) {
// server error, alert user somehow
} else {
// probably deal with these errors differently
}
});
You can find the pattern here: https://stackoverflow.com/a/18383845/1918775
There is also a good example of saving data under a factory so you need only one HTTP request to get data from web-service.

Thanks everyone for your answers. I ended up figuring it out. What I did was created a function to search the array and I passed in the current term. The function returned a data associated with that item.

Related

Handle syntax error in JSON coming from API

Imagine the following syntax error in JSON (, instead of :):
[
{
"name": "anna",
"email": "anna#gmail.com",
"town", "london"
},
...
]
I am wondering if it is possible to handle this error instead of getting an exception, getting the erroneous object, correct the error and go on with the correct version.
Here is a part of my Angular service; I am trying to get text and not JSON data but it does not work...
angular.module('mine', [])
.config(function($sceProvider) {
// Completely disable SCE.
$sceProvider.enabled(false);
})
.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'http://www.mocky.io/v2/5807df4a10000004122b74e2'
]);
}])
.config(function ($httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
'request': function(config) {
config.headers.Accept = 'text/plain';
return config;
},
'response': function(response) {
try {
// try to parse it
response.data = JSON.parse(response.data);
} catch (ex) {
// try to fix it
console.log("error " + ex);
console.log(response.data);
response.data = {fixed_data : "data"};
}
// return the corect data.
// note that the original response.data WILL BE CHANGED and this is expected.
return response;
}
};
});
})
angular.module('mine').factory('MyFactory', ['$http','$q', function MyFactory($http,$q) {
return {
getData: function() {
var deferred = $q.defer(),
config = {
params: { }
},
url="http://www.mocky.io/v2/5807df4a10000004122b74e2";
$http.jsonp(url,config)
.then(
function (response) {
deferred.resolve(response.data);
},
function (error) {
console.log(error);
return $q.reject('Error retrieving data');
}
);
return deferred.promise;
}
};
}]);
Is there anyway of directing the above promise into the success callback, retrieving the erroneous JSON and correcting it? How may I code that according to the above example?
Or maybe something easier, how to retrieve text and not JSON data from $http.jsonp as not to be driven to the failure callback?
TL;DR
Edits after further understanding the OP problem:
In the generic case where you want to edit content of a response you can do it with "Interceptors" yet the response should be legitimate to begin with. That is, if you wanted to change numeric strings to integers in an otherwise correct JSON - it would be possible.
In the situation the OP is heaving where the JSON is malformed - it is just not possible!
The long story
First
Getting into a classic XY problem!
You should really ask yourself why is the JSON broken and not attempt to fix it in the client code.
Think of it - You will only get into more problems if you fix it now, and later someone will fix the API - then you will have the broken code.
What if the JSON should have been:
[
{
"name": "anna",
"email": "anna#gmail.com",
"addresses": [{"town": "london", ...}, ...]
},
...
]
Or (god forbid):
[
{
"name": "anna",
"email": ["anna#gmail.com","town", "london"]
},
...
]
You see - my point is - The API is broken, it can be anything. You should fix the API. And if this API is not yours to fix -> use some other API or contact the owner to fix it.
JSONP
JSONP is a way to let APIs to call directly into your code.
You must trust this API. If an API would have giving me malformed JSONs - I would stay away!
In short, the way JSONP works in Angular (or everywhere actually) is by injecting a <script> tag into the DOM with src pointing to the URL of the JSONp request.
The server will pad the JSON data with a function name (most often callback but it can be any globally accessible function (Angular is using angular.callbacks._xyz) and will send it.
The browser then invokes the script that was downloaded from the src.
Now, the thing is that it is the browser calling the script. It is not in the hands of Angular. And that is exactly the problem the OP is confronting - the script must be evaluated as a correct JavaScript to begin with and the browser is doing that, not Angular. It is a MUST. You cannot get in the middle of it. It could pose a security risk if you do. This is why, for instance, the response of a JSONP request will always (by convention...) be returned with MIME type of application/javascript no matter what you ask for.
Warring - here be dragons!
I urge you not to go in this path!
If you are insisting in getting from a JSONP call a JSON with errors (and by errors I mean that the JSON can be parsed as object yet there are some thing you want to change in that object) you could try to add "Interceptors"
.config(function ($httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
'request': function(config) {
// here you can edit the request.
return config;
},
'response': function(response) {
// response.data will hold your bad data
// you could edit it
response.data = fix(response.data);
// return the correct data.
return response;
}
};
});
})
Note that you could also Overriding the Default Transformations
Also, make sure to also to:
// Whitelist the JSONP endpoint that we are using to show that we trust it
.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
'self',
'https://your.api.url/**'
]);
}])
And if all went well you will be able to call:
//uncomment {jsonpCallbackParam: 'callback'} if your jsonp callback
//parameter at the backend uses some other name but the default 'callback'
$http.jsonp(https://your.api.url/*,{jsonpCallbackParam: 'callback'}*/)
.then(function(response) {
$scope.status = response.status;
$scope.data = response.data;
}, function(response) {
$scope.data = response.data || 'Request failed';
$scope.status = response.status;
});
You can use douglascrockford's JSON-js.If it is not a valid json it will throw an error so that you can catch using try/catch and return a new Promise or simple true/false. If you don't use the library it will fallback to built in parser.
$http({
method: "GET",
url: '../data/data-feed.json'
})
.then(
function (response) {
console.log(response);
try {
JSON.parse(json);
console.log("valid");
} catch (e) {
console.log("invalid");
// correct the invalid json here
}
},
function (error) {
console.log('error');
}
);
Default JSON parser behavior
function parseJSON (jsonString){
try {
var jString = JSON.parse(jsonString);
if (jString && typeof jString === "object") {
return jString;
}
}
catch (e) { }
return false;
};
var inValidJson = '[{"name": "anna","email": "anna#gmail.com","town", "london"}]';
var validJson = '[{"name": "anna","email": "anna#gmail.com","town": "london"}]';
console.log("if invalid returns: ", parseJSON(inValidJson));
console.log("if valid get original object: ",parseJSON(validJson));
Short answer: no there isn't.
Long answer: If your JSON serialisation does not work in the backend, you have basically to parse a string an construct a new Object by yourself. There is no library which does that for you. And think of a different backend service.
I agree to #Florian's answer in general - there is no simple way of doing so.
I think that You should:
try to find a clever way to find the problem place;
replace the comma(s);
parse to JSON anew.
Idea of finding problem place: After every second (even) value there needs to be a comma. After every second (odd one - 1., 3., 5) - a colon. On each New { You have to start a new count. It probably is a pain in the ass, but doable.
It's possible, but cumbersome. For reasons It-Z went into, generally the best practice would be to fix the JSON being served by the API or to find a new API to work with. Assuming you have your reasons for not doing that, here is the logic flow:
catch the error from JSON.parse and feed it to a new function, along with the unparsed response string
in that new function
if you know you're only going to have this one case for your error, create logic to find and fix it; regex will be your friend here
if you're trying to catch multiple types of syntax problems, you'll need more complex regex and much more complex logic
pass the corrected string back to the original JSON.parse function

Initialize Current User Service on Application Start in AngularJS

I’m developing a Single Page Application with AngularJS.
When a user successfully logs in, a security token is stored in a cookie. Now, when he refreshes the page, the token will be sent to the backend, which returns a JSON object "currentUser" containing all the relevant information about the current user (as name, access-groups, profile picture, etc.).
The problem is, this is an asynchronous process of course, so when the controller starts another operation, say, just alerting the user’s name, this value will be undefined at that time.
Of course, I could set a timeout but is there a better solution?
I thought about a "currentUserService", which initializes first (sending the cookie and filling the user information with the backend response) and can only be processed after this initialization is completed.
But how can this be done? Or are there any other possibilities?
edit:
Hi guys,
thanks for the input!
Both of your suggestions seem to be very promising for asynchronous requests in general, but I think they might not fit perfectly for my concern:
The information about the current user only have to be requested once, so I would like to store them for the whole application (e.g. in the rootScope or a service) accessible from any controller without having to request them again in every controller (as in the callback or resolve-solution) but make sure that there won’t be any „timeout“ problems. Do you have any ideas?
You can resolve the user's data before the view loads either with ng-route or ui-router:
This example is written for ui-router:
.state('profile', {
url: '/profile',
controller: 'profileCtrl as vm',
resolve: {
user: function(AuthService) {
//Return a promise or an object to be resolved.
return AuthService.getUserFromToken(); //Say this is asynchronous and returns a promise
}
}
});
//In controller:
.controller('profileCtrl', function(... , user) {
//User data available here
this.user = user;
});
Please note if any errors arise during the resolve stage the state will not be loaded so you'll have to take care of the errors!
If a user refreshes you have to initialize everything. I assume the token is stored in localstorage or something and I assume this is angular 1.*. To do this I think you should call user-related functions from your http call-callback:
$scope.user = {};
$scope.getUser = function(){
$http({
method: 'GET',
url: '/someUrl'
}).then(function (res) {
$scope.user = res.data; //or whatever the response is
$scope.handleUserRelatedThings();
}).catch(function(err) {
//handle error
})
}
$scope.handleUserRelatedThings = function(){
//do things with $scope.user
alert($scope.user.name);
}
//on init
$scope.getUser();

Sending data (received from backend) from on html to another with angularJS

I feel like tons of people do this all the time, and yet I have been unsuccessful in finding similar examples.
I am getting data from the backend using angularJS ($http.post) to a controller in a javascript file, and presenting it in one html. The data is received after sending a search query from that html. Now, I want to "export" that data to another JS file and to present some of it again, in a new html. The problem is that I have access to that data only through the Search Controller during the searching session, and I probably need to store it somehow or send it to another controller/ JS file.
Unfortunately, I cannot use $cookies. Also, I am trying to avoid sending a new request through the server if I don't have to.
I have read a some about services in angular, however, I am new to angular (and UI in general), and for some reason was unable to implement this for my specific case.
Here is an example of the relevant controller, after getting a search request from the html page:
app.controller('SearchCtrl', ['$scope', '$http',
function($scope, $http) {
$scope.searchJSON = {
searchToken: [],
searchOption: []
$scope.sendSearch = function() {
//preparing JSON to send request to server
$scope.searchJSON["searchToken"] = this.search.searchToken;
$scope.searchJSON["searchOption"] = this.search.searchOption;
var json = $scope.searchJSON;
//sending and getting response (JSON object)
$http.post("http://some_host", json)
.success(function(response) {
$scope.collections = response.searchResults;
});
};
}]);
So the data I am interested in passing on to another JS file is in $scope.collections , which is a JSON file (I don't want use the same JS file for both html pages, so was hoping to call that data from a new controller in a new JS file).
Will appreciate any answers, leads, or similar examples from the web. Thank folks!
One possible way to solve this is by using sessionStorage/localStorage on $window. You can store your data there and after redirecting to another file, you can use it by invoking.
You are right to bring up services because that is how I would personally implement this. Create a service to handle the search request and also to store the result via promises:
angular.module('yourModule')
.factory('searchService', function($http, $q) {
var searchService = {
resultsPromise: null,
sendSearch: function(token, option) {
var dfd = $q.defer();
var json = {
searchToken: token,
searchOption: option
};
$http.post("http://some_host", json).then(
function(response) {
// resolve your deferred
dfd.resolve(response.data);
},
dfd.reject
);
this.resultsPromise = dfd.promise;
return dfd.promise;
}
};
return searchService;
});
Then in your current controller, just do:
app.controller('SearchCtrl', ['$scope', 'searchService',
function($scope, searchService) {
$scope.searchJSON = {
searchToken: [],
searchOption: []
$scope.sendSearch = function() {
searchService.sendSearch($scope.searchJSON.searchToken, $scope.searchJSON.searchOption);
};
Then in your other file, simply look at the currentResults of the same service:
app.controller('OtherCtrl', function($scope, searchService) {
if (searchService.resultsPromise) {
searchService.resultsPromise.then(function(results) {
$scope.results = results;
});
}
});
You can ditch the $http service and use $resource instead. From there you can use $cacheFactory as seen in this post: How to cache an http get service in angularjs
An alternative solution is http://jmdobry.github.io/angular-cache/ which works well with ngResource and can also easily be configured to sync to localStorage, so requests don't need to be re-done after page refresh.
`$resource('my/kewl/url/:key', { key: '#key' }, {
'get': { method: 'GET',
cache: $angularCacheFactory('MyKewlResourceCache', {
storageMode: 'localStorage' })
}
});`

AngularJS: dependent asynchronous requests

New to AngularJS, wondering What would be the correct way to handle three asynchronous requests where the parameters of the third are defined by the responses of the previous two.
two addresses are passed to this function
$scope.getRoutes = function(origin, destination) {
//1.A request is made to get the coordinates of the first address
dataFactory.geocode(origin)
.success(function(data) {
$scope.originObj = data;
})
.error(function () {
});
//2.Same for the second address
dataFactory.geocode(destination)
.success(function(data) {
$scope.destinationObj = data;
})
.error(function () {
});
//3.Finally a request for transport routes between the two sets of coordinates
dataFactory.getRoutes($scope.originObj.coordinates, $scope.destinationObj.coordinates)
.success(function(data) {
$scope.routes = data;
})
.error(function () {
});
};
This gives me the error: TypeError: Cannot read property 'coordinates' of undefined
Nesting the requests in the success functions functions, but is there a better way to have the last request wait around for the other two?
What you're looking for is the ability to chain promises. "Once you receive the data from this one, call this one" is the general format. Angular takes advantage of the $q library. You can find documentation here.

Angular $http.jsonp or get returning 404 on a mobile

I am pulling in JSON from a file I am storing locally. When I view this in the browser it works, when I view it in an Ionic packaged app it returns 404
I am using the following code:
.factory('cardFactory', function ($q, $http, $rootScope) {
return {
getCards: function () {
var deferred = $q.defer(),
httpPromise = $http.jsonp('/static/cards.json');
httpPromise.then(function (response) {
deferred.resolve(response);
}, function (error) {
console.error(error);
});
return deferred.promise;
}
};
});
And calling it like so:
cardFactory.getCards()
.then(cardSuccess, cardError);
I have tried with GET instead of JSONP, both return a 404 response.
I am aware of the access-control-allow-origin issue, but surely the jsonP should solve that?
This is at the same level (hierarchically) as my images, which are served fine.
Any ideas what's going on?
The solution was to change the request back to a simple GET, and lose the first slash as so:
var httpPromise = $http.get('static/cards.json');

Categories

Resources