i try to fetch api json , here is the link https://api.myjson.com/bins/ut9kq
when i print the output before the loop it does get the data but after the list it give me error
"Unhandled Exception: NoSuchMethodError: The getter 'image' was called on null.
E/flutter (30730): Receiver: null
E/flutter (30730): Tried calling: image"
here is my code
List lists;
Future<List> getLists() async {
lists = new List();
await api.httpGet('bins/ut9kq').then((reponse) {
var data = jsonDecode(reponse.body);
print(data); // i get the json data => [{},{}..]
data.forEach((l) {
lists.add(ArticleModal().fromJson(l));
});
// print(lists[0].image);//Receiver: null
});
return lists;
}
You are using both approaches for future handling, .then callbacks and async and await. Try the following, you can wrap these around try/catch blocks for error handling.
Future<List<ArticleModal>> getLists() async {
lists = new List();
var response = await http.get('bins/ut9kq');
// a switch may be used for a wider range of codes
if (response.statusCode == 200) {
var decodedResponse = json.decode(response.body);
print(decodedResponse);
//if response is a collection
var listAricleModal = (decodedResponse as List).map((collectionElement) => ArticleModal.fromJson(collectionElement)).toList();
return listAricleModal;
}
//Your future is an empty collection
return [];
}
And to properly answer your question, image is called on null is quite obvious message.
At some point you have an image field, that object is not created. Probably is the object in the list, so two things may be happening:
List is empty, so nothing to call image on.
There is a problem when calling fromJson.
Either way, use the debugger and set breakpoints on each method first line to clearly understand what is happening.
If map function is tricky to debug, use a plain for loop until you know where the bug is.
Check out this series of videos from Google.
https://youtu.be/vl_AaCgudcY
Also official docs: https://dart.dev/codelabs/async-await
Related
I'm learning how to use json, not used to Dart null safety & I don't like it so far. But I will have no choice but to adapt to it. I'm trying to parse my json object list into a list of objects. I was able to work out the basics in my main, but when I attempt to create an actual method in a class using the same structure I'm getting a null error. As far as I can tell I'm doing the exact same thing in both with addition of the loop for iterating the entire json list.
Note: I of course did try inserting the optional ? where it asks but the IDE will not allow this.
Can someone help with explaining what I'm doing wrong here?
Error for class method jsonToDatabaseSyncItem()
lib/services/remote_database_services.dart:52:43: Error: Property 'length' cannot be accessed on 'List<dynamic>?' because it is potentially null.
- 'List' is from 'dart:core'.
Try accessing using ?. instead.
final jsonListLength = jsonObjectList.length;
^^^^^^
lib/services/remote_database_services.dart:55:38: Error: Operator '[]' cannot be called on 'List<dynamic>?' because it is potentially null.
- 'List' is from 'dart:core'.
var jsonObject = jsonObjectList[index]['DatabaseSyncItem'];
Class method (class- RemoteDatabaseService)
// This method will get server database
Future<List<dynamic>?> getRemoteDatabase() async {
final events = QueryBuilder<ParseObject>(ParseObject('Event'));
final apiResponse = await events.query();
if (apiResponse.success && apiResponse.result != null) {
return apiResponse.results;
} else {
return [];
}
}
// Method to parse json result list back to objects
Future<List<dynamic>?> jsonToDatabaseSyncItem() async {
final remoteDatabaseList = await getRemoteDatabase();
final jsonObjectList = await Future.value(remoteDatabaseList); /// This method throws the above
final jsonListLength = jsonObjectList.length; /// error when run in main
for (var index = 0; index == jsonListLength; index++) {
var jsonObject = jsonObjectList[index]['DatabaseSyncItem'];
print(jsonObject);
}
}
Main file working code
Future<void> main(List<String> arguments) async {
final test = remoteDatabaseServices.getRemoteDatabase();
Future<List<dynamic>?> getList() {
return Future.value(test);
}
var list = await getList();
print(list?.length);
var jsonObject = list![0]['DatabaseSyncItem'];
print(jsonObject);
var toObject = DatabaseSyncItem.fromJson(jsonObject);
print(toObject);
}
Your problem with null-safety seems to be a missing understanding about the feature. I will recommend you to read through the documentation here and read all the chapters: https://dart.dev/null-safety
About your code, you should consider when something can be null and when you can check the null and handle that case. In your example, it seems like getRemoteDatabase() should just return an empty List if an error happens or no result is returned. So we don't need to have the return type of this method as Future<List<dynamic>?> if we rewrite the method a bit:
Future<List<dynamic>> getRemoteDatabase() async {
final events = QueryBuilder<ParseObject>(ParseObject('Event'));
final apiResponse = await events.query();
if (apiResponse.success) {
return apiResponse.results ?? <dynamic>[];
} else {
return <dynamic>[];
}
}
(the ?? operator will here test if apiResponse.results is null, if that is the case, we return <dynamic>[]. If not, we use the value of apiResponse.results).
Since this method is now guarantee to never return null, we can use that to simplify the next method. I have also rewritten it to use a for-each loop since we don't really need the index of each element.
Future<void> jsonToDatabaseSyncItem() async {
final jsonObjectList = await getRemoteDatabase();
for (final jsonObject in jsonObjectList) {
print(jsonObject['DatabaseSyncItem']);
}
}
I have also removed this line since it does nothing at all. If remoteDatabaseList is a Future you should just await on that instead of creating a new Future.
final jsonObjectList = await Future.value(remoteDatabaseList);
Also, the return type of jsonToDatabaseSyncItem() have been changed to Future<void> since we are never returning any value.
I am a little confused about your main but I think this is where it is most clear that you got a confused about Future and null-safety. I have tried to rewrite it so it is much cleaner but should still do the same:
Future<void> main(List<String> arguments) async {
final list = await getRemoteDatabase();
print(list.length);
final dynamic jsonObject = list[0]['DatabaseSyncItem'];
print(jsonObject);
final toObject = DatabaseSyncItem.fromJson(jsonObject);
print(toObject);
}
so i created a hook to fetch the ISS API. it works fine. but i am having difficulty displaying a specific part of the json that is returned.
my react fetch hook, the useEffect part
my display code
the code works and displays the first two tags, but when i add the 3rd with location.iss_position.longitude i get an undefined error
the console.dir of the json data
i have tried many variations of location.iss_position.longitude but nothing seems to work and a few google searches were unproductive. maybe my own fault for being able to accurately describe my problem with the correct technical language.
EDIT: heres my full code for fetch and display logic. i followed a tutorial and understand about 80% of it now. still learning
export const useFetchPosition = () => {
// define states for the hook
const [location, setLocation] = useState({})
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
useEffect(() => {
// init loading and error states
setLoading(true)
setError(null)
// fetch api url
fetch(issUrl)
// return response as promise with json content
.then(res => res.json())
// return json promise, setLoading state, console log
.then(json => {
setLoading(false)
if (json) {
setLocation(json)
console.dir(json)
} else {
// this else prevents infinite loop
setLocation([])
}
})
// errors update state here
.catch(err => {
setError(err)
setLoading(false)
})
},[])
// return updated states for export to display
return { location, loading, error }
}
const Display = () => {
//call hook and hook data
const { location, loading, error } = useFetchPosition()
// loading and error
if (loading) return <div>Loading...</div>
if (error) return <div>{error}</div>
return (
<>
<h2 className="bg-gray-900 text-gray-300 text-center text-6xl">
{location.message}
</h2>
<h2 className="bg-gray-900 text-gray-300 text-center text-3xl">
response timestamp: {location.timestamp}
</h2>
<h2 className="bg-gray-900 text-gray-300 text-center text-3xl">
current latitude: {JSON.stringify(location) !== '{}' && location.iss_position.latitude}
</h2>
<h2 className="bg-gray-900 text-gray-300 text-center text-3xl">
current longitude: {JSON.stringify(location) !== '{}' && location.iss_position.longitude}
</h2>
</>
)
}
export default Display
Answer:
React isn't having difficulty displaying the JSON response; it's having trouble displaying your component before the response comes in, because you are trying to reference members of undefined objects.
Try putting JSON.stringify(location) !== '{}' && location.iss_position.latitude instead (presuming your default state, when using setState is {})
Alternatively you can define a default state in the same shape as the API's response
Explanation
This is normal Javascript behaviour.
You've assigned {} to location when you first called:
// I'm presuming you did something like this
let [location, setLocation] = setState({});
At this point, location is set to {}. You can, in any JS context, try to refer to members of an object that don't exist and you'll get undefined.
But when you do location.iss_position.longitude, you are trying to reference longitude on a member iss_position which is undefined - this will throw an error. You cannot reference members of undefined, but you can reference undefined members on a defined object.
Try running the following in your console:
let foo = {}; // Can't redefine window.location
console.log(foo); // {}
console.log(foo.iss_position); // undefined
console.log(foo.iss_position.longitude); // TypeError: location.iss_position is undefined
In fact, your console will tell you exactly that. The error your component is throwing specifically says:
location.iss_position is undefined
This is telling you that the object you are trying to reference (location.iss_position) is undefined at some point (before the API responds, for example)
I've been in the process of migrating a code base from Angular 4.x to 5.x, and I'm running into a strange issue. I have a service function, that is intended to return a list of objects to the front end, which I then massage into a specific data format. I know I'll need to keep the mapping, but I'm a little miffed that it's returning just plain string data.
The original function is this: (using Http from #angular/http just renamed to HttpClient)
public GetVendors(showAll = true, screenArea: number = 0): Observable<Array<SelectModel>> {
let link = AppSettings.API_COMMON_VENDORS;
let params: URLSearchParams = new URLSearchParams();
params.set('showAll', showAll.toString());
params.set('screenArea', screenArea.toString());
let requestOptions = new RequestOptions();
requestOptions.search = params;
return this.httpClient.get(link, requestOptions).map(response => {
let result = JSON.parse(response.json());
let list = new Array<SelectModel>();
let vendors: Array<any> = result;
vendors.forEach(vendor => {
list.push(this.CreateSelectModel(vendor));
});
return list;
});
}
and after ripping out ALL of the Http code, here's the function again using HttpClient from #angular/common/http
public GetVendors(showAll = true, screenArea: number = 0): Observable<Array<SelectModel>> {
let link = AppSettings.API_COMMON_VENDORS;
let params: HttpParams = new HttpParams()
.set('showAll', showAll.toString())
.set('screenArea', screenArea.toString());
return this.httpClient.get<Array<any>>(link, {params}).map(response => {
let list = new Array<SelectModel>();
response.forEach(vendor => {
list.push(this.CreateSelectModel(vendor));
});
return list;
});
}
The issue with this is it kind of defeats the purpose of the new client parsing json for me. The response object is a string representing the JSON of the data I requested, but it's still in a string form, and not the type defined in the get<>() call.
What am I doing wrong here? shouldn't it be parsed already?
Sample Response Data A'la Network Tools in Chrome Dev Tools:
Sample Response Body:
Dev Tools Screenshot with Value of response
The backend (C#) responds with this:
[HttpGet]
public JsonResult Vendors(bool showAll = false, int screenArea = 0)
{
var vendors = _commonBL.GetVendorsSlimForUser(UserModel, UserModel.CustomerId, showAll, screenArea);
return GetJson(vendors);
}
this is how it worked before the Http => HttpClient migration, and it worked with ONE JSON.parse() The data in the return line is simply a standard List<T>
This is what the raw response for your data should look like:
[{"Id":1234,"Name":"Chris Rutherford"}]
But this is what it actually looks like:
"[{\"Id\":1234,\"Name\":\"Chris Rutherford\"}]"
So somewhere in your server code, you have applied JSON encoding twice. Once you correct that, HttpClient will do the right thing.
I'd quote an answer from this thread. Hope it will shed some light on how things work, read it thoroughly it enlightened me tough its not easy to find.
TypeScript only verifies the object interface at compile time. Any object that the code fetches at runtime cannot be verified by
TypeScript.
If this is the case, then things like HttpClient.Get should not
return Observable of type T. It should return Observable of type Object because
that's what is actually returned. Trying to state that it returns T
when it returns Object is misleading.
In the documentation the client's return section says this:
#return an Observable of the body as
type T.
In reality, the documentation should say:
#return an Observable of the body which
might be T. You do not get T back. If you got T back, it would
actually be T, but it's not.
I'm trying to use an ajax call to get json data about my model from my controller. I know 500 error could mean lots of things but I would like to eliminate the possibility of simple error by me.
Console gives me error of: 500 Internal Service Error.
Otherwise I can access it in the url just fine but I don't get anything in the console.
Index.cshtml
function getData() {
$.ajax({
url: "#Url.Action("dataTransfer", "Data")",
type: "GET",
dataType: "json",
success: function(data) {
console.log(data);
},
error: function() {
console.log("failed");
}
});
}
setInterval(function() {
getData();
}, 10000);
DataController
public JsonResult dataTransfer()
{
string DataProvider = "Sample";
var model = from d in db.Data
where d.Name == DataProvider
select d;
return Json(model);
}
500 internal error means your server code is failing, running into an exception because of bad code !
From your code, i can see a problem which could be the cause of your error.
When returning Json from a GET action method, you need to pass JsonRequestBehaviour.AllowGet as the second parameter of Json method.
public JsonResult dataTransfer()
{
string DataProvider = "Sample";
var model = from d in db.Data
where d.Name == DataProvider
select d;
return Json(model,JsonRequestBehavior.AllowGet);
}
Usually in an ASP.NET MVC application, the GET method's are supposed to be returning a view and typically the POST method does some processing on the posted form data/ajax data and return a response, which can be JSON. But if you really want to return Json data from your GET action method, You have to explicitly specify that using the above method we did
Ofcourse, Web API's has a different concept (And implementation behind the scene)
I know this has something to do with using $q and promises, but I've been at it for hours and still can't quite figure out how it's supposed to work with my example.
I have a .json file with the data I want. I have a list of people with id's. I want to have a service or factory I can query with a parameter that'll http.get a json file I have, filter it based on the param, then send it back to my controller.
angular
.module("mainApp")
.controller('personInfoCtrl',['$scope', '$stateParams', 'GetPersonData', function($scope, $stateParams, GetPersonData) {
$scope.personId = $stateParams.id; //this part work great
$scope.fullObject = GetPersonData($stateParams.id);
//I'm having trouble getting ^^^ to work.
//I'm able to do
//GetPersonData($stateParams.id).success(function(data)
// { $scope.fullObject = data; });
//and I can filter it inside of that object, but I want to filter it in the factory/service
}]);
Inside my main.js I have
//angular.module(...
//..a bunch of urlrouterprovider and stateprovider stuff that works
//
}]).service('GetPersonData', ['$http', function($http)
{
return function(id) {
return $http.get('./data/people.json').then(function(res) {
//I know the problem lies in it not 'waiting' for the data to get back
//before it returns an empty json (or empty something or other)
return res.data.filter(function(el) { return el.id == id)
});
}
}]);
The syntax of the filtering and everything works great when it's all in the controller, but I want to use the same code in several controls, so I'm trying to break it out to a service (or factory, I just want the controllers to be 'clean' looking).
I'm really wanting to be able to inject "GetPersonData" to a controller, then call GetPersonData(personId) to get back the json
You seems to be syntax issue in your filter function in the service.
.service('GetPersonData', ['$http', function($http){
return function(id) {
return $http.get('./data/people.json').then( function (res) {
return res.data.filter(function(el) { return el.id == id });
});
}}]);
But regarding the original issue you cannot really access the success property of the $q promise that you are returning from your function because there is no such property exist, It exists only on the promise directly returned by the http function. So you just need to use the then to chain it through in your controller.
GetPersonData($stateParams.id).then(function(data){ $scope.fullObject = data; });
If you were to return return $http.get('./data/people.json') from your service then you will see the http's custom promise methods success and error.