I'll preface this saying I'm a UX Designer that's new to React.
I'm using Expo/React-Native and using the "Location" feature to get the device's location information. I'm able to get the information back in a JSON object by using:
const { height, width } = Dimensions.get("window");
class Explore extends Component {
static navigationOptions = {
header: null
};
state = {
locationResult: ""
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
this.setState({
locationResult: "Permission to access location was denied"
});
}
let location = await Location.getCurrentPositionAsync({});
let geocode = await Location.reverseGeocodeAsync(location.coords);
this.setState({ locationResult: JSON.stringify(geocode) });
};
And in my render I'm calling the value using:
<WelcomeText>
Discover {this.state.locationResult}
</WelcomeText>
Which returns the object:
[{"street":"Stockton St","city":"San Francisco","region":"CA","country":"United States","postalCode":"94108","isoCountryCode":"US","name":"1 Stockton St"}]
But how do I go about just displaying the value of "City" that's in the object?
Try this:
<WelcomeText>
Discover {this.state.locationResult[0].city}
</WelcomeText>
Explanation:
Your this.state.locationResult is an array of objects. With this.state.locationResult[0] we are accessing the first object. Then we can use the . operator to access the property we want. In your case .city
Edit:
You need to pass in geocode without stringifying it, otherwise you cannot access it like i described it above. reverseGeocodeAsync is already returning an array of objects, no need to transform it to a string.
Replace:
this.setState({ locationResult: JSON.stringify(geocode) });
with:
this.setState({ locationResult: geocode });
Edit2:
Here is a working example: https://snack.expo.io/B1SqSd3iN
Related
I'm running into issues with trying to convert a json response from an Api call into an interface that will be accepted by this buildFileTree. So the call is pulling from SQL, it is working in dapper, I also see the array of data in my webapp in my console. The issue is when I try to change the initialize() value for buildFileTree from my static json file 'SampleJson' (inside the project) to my new interface 'VehicleCatalogMod' the tree shows up with SampleJson but when I switch the data to VehicleCatalogMod, the tree collapses.
dataStoreNew: VehicleCatalogMod[] = [];
constructor(private _servicesService: ServicesService){
this._servicesService.GetVehicleCat()
.subscribe(data => {
this.dataStoreNew = [];
this.dataStoreNew = data;
console.log(data);
})
this.initialize();
}
initialize() {
this.treeData = SampleJson;
// Working as SampleJson this is where the problem happens
const data = this.buildFileTree(VehicleCatalogMod, 0);
console.log(data);
this.dataChange.next(data);
}
buildFileTree(obj: object, level: number): TodoItemNode[] {
return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
let value = obj[key];
const node = new TodoItemNode();
node.item = key;
if (value != null) {
if (typeof value === 'object') {
node.children = this.buildFileTree(value, level + 1);
} else {
node.item = value;
}
}
return accumulator.concat(node);
}, []);
}
GetVehicleCat(): Observable<any> {
console.log('Vehicle Catalog got called');
return this.http.get('https://api/right/here',
{ headers: this.options.headers });
}
I tried a multitude of different things to try & get this working. I'm pretty much stagnated. Same error occurs when I try this.dataStoreNew instead. No errors in console, it literally just collapses the tree into one non distinguishable line. Also when I used: const vcm = new VehicleCatalogMod(); it made the tree pop up with the different properties but not the API values.
I also attached an image of the HTML element that appears.
with VehicleCatalogMod
with SampleJson
I need to set the displayName variable but I have no idea how to get to it. For context, I'm making a C# application to set this variable to something else. The parent variables to displayName vary depending on the user that is using this application.
I have blurred these as to not reveal any of my personal information.
I think I might need to loop through JSON object children, but I'm not sure.
Hey so you are correct you are going to have to iterate over the object and search through to find display name.
I wrote a little function below that will recursively go through the object and search for displayName. Obviously its hard if you never know the location or the pathname so you have to have a pretty open way to search the JSON object.
If you can control the way you are requesting the data maybe you could change the format so the data structure is more consistent, but I don't really know anything about where your getting the data from.
This is just one of the many ways to do it.
const obj = {
authenticationDatabase : {
accessToken: 'Mock',
profiles: {
displayName: 'THIS IS A MOCK USER NAME'
},
properties: [],
username: 'MOCK'
}
}
const obj2 = {
authenticationDatabase : {
accessToken: 'Mock',
profiles: {
deep: {
nested: {
object: {
displayName: 'THIS IS A MOCK USER NAME'
}
}
}
},
properties: [],
username: 'MOCK'
}
}
const findDisplayName = obj => {
if(!obj || typeof(obj) != 'object'){
return false;
}
if(Object.keys(obj).includes("displayName")){
return obj["displayName"]
}
for(const key in obj){
if(findDisplayName(obj[key])){
return findDisplayName(obj[key])
}
}
return false;
}
console.log(findDisplayName(obj))
console.log(findDisplayName(obj2))
I am trying to map a Http JSON Response to a Custom Interface in Angular / typescript. I have tried it in several ways but have not made it yet. The JSON object is not correctly mapped to the interface. The map attribute stays "undefined". If I print the data directly, the JSON data is output correctly - the problem is that I don't know how to access it. Here is my code:
export interface IMap<T> {
map: Map<string, Array<T>>;
}
The JSON answer looks like this. It is Map< String,List< ? >> in Java.
{
"somenumbers": [
20,
40
],
"somemorenumbers": [
71,
111
]
}
Now I tried to map it the following way:
public getValues(
paramList: Array<string>
): Observable<IMap<any>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http.get<IMap<any>>(url, { params });
}
In the configservice I subscribe to the Method. How do I map the Response correctly so that the attribute map in data isn't undefined and can be accessed correctly?
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: IMap<any>) => {
//outputs the JSON Data as Object{somenumbers: Array(2), somemorenumbers: Array(2), map: Map(0)}
console.error(data);
console.error(data.map);//map is undefined => ERROR
});
As you can see the map attribute is undefined. It is just a "map: Map(0)". Now... - How do I get the JSON stuff into the export interface? The map attribute should be filled with the associated values.
I appreciate any help! :)
If I understood correctly you're expecting that by adding <IMap<any>> to the get call it will then return you the response mapped to IMap. It doesn't, check this issue.
What you can do instead is use rxjs map to map the response yourself like so:
return this.http.get<IMap<any>>(url, { params }).pipe(
map((response) => {
// map the response here
})
);
I realized that I actually don't need the export interface and changed the code to the following. It took a while for me to get that x.y is in ts the same as x["y"]. Via response[parameter] I can access the attributes of the response Object dynamically - exactly what I needed.
public getValues(
paramList: Array<string>
): Observable<Map<string, Array<any>>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http
.get<any>(url, {
params
})
.pipe(
map(response => {
let toReturn = new Map<string, any[]>();
for (let parameter of paramList) {
toReturn.set(parameter, response[parameter]);
}
return toReturn;
})
);
}
The mapping works now! The JSON answer is still the same as in the question above.
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: Map<string, any[]>) => {
console.error(data);
});
Thanks for the help and links #M Mansour !
I have a service with a method called "getGmapsDistance()". Here im using the google maps api to get the distance between an origin an an destination.
export default Ember.Service.extend({
getShortestDistanceInMeters: function(location) {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix({
...
}, this.callback); //<<<<<< !!!
},
callback: function(response, status) {
....
}
});
In my controller if got a array with locations and now I want to iterate over it and want check each element if the distance is <= the max destination.
locationsNearby: Ember.computed('locations', function() {
//...
var filteredResult = [];
locations.forEach(function(locat) {
if (this.get('distanceService').getShortestDistanceInMeters(locat) <= maxDistance) {
filteredResult.pushObject(locat);
}
});
return filteredResult;
})
Unfortunately the GMaps API for distance calculation uses a callback so the request is async.
How can I solve that problem?
You can not make an async call synchronous! This is an javascript language limitation and is important to understand! Javascript has only one thread, so this can't be changed by a library or so!
The fancy new way to handle callbacks are Promises.
You really really should checkout the specifications!
It's one of the most beautiful specifications you will ever read!
Ember uses Promises heavily! For example a routes model hook waits for a Promise to resolve before going on with the transition.
In your case you want to update the computed property when the promise resolves. Because ember-data causes this to happen often they provide two fancy classes: PromiseObject and PromiseArray. A computed property depending on a computed property that returns a PromiseObject/Array will recompute when the promise resolves:
locationsNearby: Ember.computed('locations', {
get() {
let promise = Ember.RSVP.all(this.get('locations').map(location => Ember.RSVP.hash(({
location,
distance: this.get('distanceService').getShortestDistanceInMeters(location)
})))).then(hashs => hashs.filter(hash => hash.distance <= maxDistance).map(hash => hash.location));
return DS.PromiseArray.create({promise});
}
})
To explain it a little:
I build an array with hash's of the location and a promise to the distance:
let locationsWithDistancePromise = this.get('locations').map(location => {
distance: this.get('distanceService').getShortestDistanceInMeters(location),
location
})
Then I use RSVP.hash on all of them to get an array of promises that will resolve to an array of hashes with distance and location:
let hashPromiseArr = locationsWithDistancePromise.map(h => Ember.RSVP.hash(h));
Now I use Ember.RSVP.all to get an promise that will resolve to an array of hashes with location and distance:
let hashArrPromise = Ember.RSVP.all(hashPromiseArr);
An finally I .then on the promise and filter the nearby locations. Also I map the hash to a array of locations.
let promise = hashArrPromise.then(hashs => {
return hashs.filter(hash => hash.distance <= maxDistance)
.map(hash => hash.location);
});
And wrap it as an PromiseArray
return DS.PromiseArray.create({promise});
You can just loop over this Computed Property from handlebars with {{#each}} or use it in another Computed Property:
allNearbyLocations: Ember.computed('locationsNearby.[]', {
get() {
return this.get('locationsNearby').toArray().join(' - ');
}
}
Of course you need to rewrite getShortestDistanceInMeters so that it returns a Promise:
getShortestDistanceInMeters(location) {
var service = new google.maps.DistanceMatrixService();
return new Ember.RSVP.Promise((resolve, reject) => {
service.getDistanceMatrix({
//...
}, (response, status) => {
if(status.error) {
reject(response);
} else {
resolve(response);
}
});
});
}
First time using Angular JS, I'm using $http.get to return a Json object. When I output the response data, I can see entire JSON object in the console, but I can't access the object or properties. What am I missing here?
$scope.init = function (value) {
$scope.productEditorModel.productId = value;
$scope.loadData($scope.productEditorModel.productId);
}
$scope.loadData = function (productId) {
var responsePromise = $http.get("/Product/GetProductModel", {
params: { productId: productId }
});
responsePromise.success(function (dataFromServer, status, headers, config) {
console.log(dataFromServer.DataModel);
});
};
When I first output the dataFromServer to the console, the object is null and then it becomes populated. Since it's an async call, I should be able to access and set whatever vars inside the success
I would like to be able to directly access the object and property names IE:
$scope.productModel.productId = dataFromServer.Data.productId
My json looks like this:
Object{DataModel:Object, IsSuccess: false}
Thanks!
The problem is that you are trying to access the data before it comes back. Here is a plunker that demonstrates how to set it up, and how not to.
//predefine our object that we want to stick our data into
$scope.myDataObject = {
productId: 'nothing yet',
name: 'nothing yet'
}
//get the data, and when we have it, assign it to our object, then the DOM will automatically update
$http.get('test.json')
.success(function(data) {
$scope.myDataObject = data
});
var y = $http.get('test.json')
//this throws an error because I am trying to access the productId property of the promise object, which doesn't exist.
console.log(y.productId);
Here is the demo