Typescript React map API response to internal type with extra properties - reactjs

I am building a react frontend client using typescript. I would like to make a request to an api endpoint that will return a list of events. When I get the response from this event I want to map it to an internal type, that will also have extra properties not returned from the API, specifically, I want to associate an icon to an internal type for when it is rendered. The icon is not part of the API and I do not want it to be so it does not couple the front end to the API. API response:
[
{
"identifier": "1",
"name": "Gig"
},
{
"identifier": "2",
"name": "Concert"
},
{
"identifier": "3",
"name": "Some event type here"
}
{
"identifier": "4",
"name": "other"
}
]
So I can request this fine, but I want to map that response back to an internal type of the following definition:
type Event = {
identifier: string;
name: string;
icon: string;
}
Somewhere I want to map the returned labels from the API to an internal list that holds the corresponding icons. I am not sure of the best way to do this, for that mapping to take place I will need an internal list of potential types that will be returned and the icons, or how to do that mapping?

I believe you can use interfaces that can inherit from another interface in this case. You can have 2 interfaces that can be used as your type.
interface Event{
identifier: string;
name: string;
}
interface EventICON extends Event {
icon: string;
}
The Event interface can be used to map your API response and EventICON can be used after you inject your icon list inside the response you get.

Related

How to shape the data source for PrimeNG Tree (P-Tree) Component?

I am very new to the Angular and PrimeNG components. I want to build a tree view with children nodes, and PrimeNG offered the solution out of the box with the component. However, all the examples have hard-coded data sources. In my case, the data source is from the database, and I don't know if I have to shape my data to match what the P-Tree expected (see below). If yes, what is the technique for doing it?
Here is an example of the data source for P-Tree.
{
"label": "Pictures",
"data": "Pictures Folder",
"expandedIcon": "pi pi-folder-open",
"collapsedIcon": "pi pi-folder",
"children": [
{"label": "barcelona.jpg", "icon": "pi pi-image", "data": "Barcelona Photo"},
{"label": "logo.jpg", "icon": "pi pi-file", "data": "PrimeFaces Logo"},
{"label": "primeui.png", "icon": "pi pi-image", "data": "PrimeUI Logo"}]
}
Yes you would since the data has to match the TreeNode interface.
export interface TreeNode {
data?: any;
children?: TreeNode[];
leaf?: boolean;
expanded?: boolean;
label?: string;
expandedIcon?: string:
collapsedIcon?: string;
}
If you have an API, you can do something like this.
#Injectable()
export class NodeService {
constructor(private http: Http) {}
getFilesystem() {
return this.http.get('showcase/resources/data/filesystem.json')
.toPromise()
.then(res => <TreeNode[]> res.json().data);
}
}
The p-treeSelect and p-treeTable docs has a few good examples:
https://www.primefaces.org/primeng/showcase/#/treeselect https://www.primefaces.org/primeng/showcase/#/treetable.
My question is resolved. A recursive method was used to build a tree hierarchy with the parent ID as root and the children belong the parent. A data table is passed into the method as the parameter. The method iterates through the entire data table.
foreach (DataRow dr in dtData.Select("ParentID = '" + ID+ "'"))
{
// recursive
folder.children.Add(
new ChildFolder()
{
label = dr["Name"].ToString(),
children = GetChildren(Convert.ToInt32(dr["ID"]), dtData),
data = Convert.ToString(dr["ID"])
});
}
return folder;

Custom data unwrapping in ampersand.js model

I have a model - Configuration:
var Configuration = Model.extend({
props: {
name: 'string'
}
});
In the database, configuration model / table has 3 columns -> id, name and fields. The latter stores site config as a serialized array. When retrieving the entry from the database, I unserialize it and then pass it to the front end, so the front end receives this:
{
"id": 1,
"name": 'global',
"fields": {
"enabled": true,
"site_name": "Test"
}
};
What I want to do is to set whatever is inside fields object as properties on my model, or maybe session so that things get triggered throughout the site when they are updated. To visualize it, I want to achieve something like this:
var Configuration = Model.extend({
props: {
enabled: 'boolean',
site_name: 'string'
}
});
So basically, is there are a way to 'unwrap' stuff in fields object somehow?
The parse method is what you're looking for in this case. See https://github.com/AmpersandJS/ampersand-state/blob/master/ampersand-state.js#L93-L98 It allows you to transform incoming props.

angular-schema-form: Add custom html to form fields

I have just started to look into angular-schema-form, so this might be something I've missed in the docs or description.
What I am trying to do is to add an icon next to the label of generated form fields and next to the field itself. Like so:
But out of the box angular-schema-form will generate:
I know I can make my own custom field types, but is that the way to go? That would require me to redefine all field types in a custom variant, because I need these two icons and their functionality on all my form fields.
I was hoping there were an easier way to add this functionality to generated html, and an easy way to add functionality (ng-click function) on them.
Edit: After reading through the docs again, I've figured out that I need to define my own custom field type (https://github.com/Textalk/angular-schema-form/blob/development/docs/extending.md)
From what I gather, I need to add the following to my modules config block:
schemaFormDecoratorsProvider.addMapping(
'bootstrapDecorator',
'custominput',
'shared/templates/customInput.tpl.html',
sfBuilderProvider.builders.sfField
);
I have also added the contents of shared/templates/customInput.tpl.html to $templatesCache.
But when I try to render a form, with a schema like
"schema": {
"type": "object",
"properties": {
"firstName": {
"title": "First name",
"type": "string"
},
"lastName": {
"title": "Last name",
"type": "custominput"
},
"age": {
"title": "Age",
"type": "number"
}
}
}
I only see the first field (firstName) and age. The custom type is just ignored.
I have tried to debug my way to the problem, but as far as I can see, the custom field is correctly added to the decorator. I've tried to console.log the schemaFormDecoratorsProvider.decorator() and there I can see my custom field type.
I've also tried to fire off a $scope.$broadcast('schemaFormRedraw') in my controller, but I still only see the built in field types.
As a test, I've tried to define my own decorator, overwriting the default Bootstrap decorator:
schemaFormDecoratorsProvider.defineDecorator('bootstrapDecorator', {
'customType': {template: 'shared/templates/customInput.tpl.html', builder: sfBuilderProvider.stdBuilders},
// The default is special, if the builder can't find a match it uses the default template.
'default': {template: 'shared/templates/customInput.tpl.html', builder: sfBuilderProvider.stdBuilders},
}, []);
I would expect to see all fields to be rendered the same, since I only define a default type and my own custom type. But still, I only see built in types rendered, my custominput is till just ignored.
What am I missing?
I've had this same problem, the problem is that you should not confuse the JSON schema with the form definition.
To render a custom component you have to change the form definition. I.e in your controller your standard form defintion might look something like:
$scope.form = [
"*",
{
type: "submit",
title: "Save"
}
];
You'll have to change this to:
$scope.form = [
"firstName",
"age",
{
key:"lastName",
type:"customInput"
},
{
type: "submit",
title: "Save"
}
];

Creating an array from GeoJSON file in OpenLayers 3

I am using OpenLayers 3 to animate the paths of migrating animals tagged by scientists. I load the geoJSON file like so
var whaleSource = new ol.source.Vector({
url: 'data/BW2205005.json',
format: new ol.format.GeoJSON()
});
Instead of loading this directly into a layer, I would like to use and reuse the data in the geoJSON file for different purposes throughout my program. For example, I want to pull the lat & lon coordinates into an array to manipulate them to create interpolated animated tracks. Later I will want to query the geoJSON properties to restyle the tracks of males and females.
How might I load the geoJSON data into various arrays at different stages of my program instead of directly into a layer?
Thanks much
When using the url property of ol.source.Vector the class loads the given url via XHR/AJAX for you:
Setting this option instructs the source to use an XHR loader (see ol.featureloader.xhr) and an ol.loadingstrategy.all for a one-off download of all features from that URL.
You could load the file yourself using XHR/AJAX using XMLHttpRequest or a library like jquery which has XHR/AJAX functionality. When you've retreived the GeoJSON collection you can loop over the features array it holds and split it up into what every you need and put those features into new separate GeoJSON collections. Here's a very crude example to give you and idea of the concept:
Assume the following GeoJSON collection:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, 0]
},
"properties": {
"name": "Free Willy"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [1, 1]
},
"properties": {
"name": "Moby Dick"
}
}, {
// Etc.
}]
}
Here's how to load it (using jQuery's $.getJSON XHR function) and to split it up in to separate collections:
// Object to store separated collections
var whales = {};
// Load url and execute handler function
$.getJSON('collection.json', function (data) {
// Iterate the features of the collection
data.features.forEach(function (feature) {
// Check there is a whale already with that name
if (!whales.hasOwnProperty(feature.properties.name)) {
// No there isn't create empty collection
whales[feature.properties.name] = {
"type": "FeatureCollection",
"features": []
};
}
// Add the feature to the collection
whales[feature.properties.name].features.push(feature);
});
});
Now you can use the separate collections stored in the whale object to create layers. Note this differs some from using the url property:
new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(whales['Free Willy'], {
featureProjection: 'EPSG:3857'
})
})
});
Here's a working example of the concept: http://plnkr.co/edit/rGwhI9vpu8ZYfAWvBZZr?p=preview
Edit after comment:
If you want all the coordinates for Willy:
// Empty array to store coordinates
var willysCoordinates = [];
// Iterate over Willy's features
whales['Free Willy'].features.forEach(function (feature) {
willysCoordinates.push(feature.geometry.coordinates);
});
Now willysCoordinates holds a nested array of coordinates: [[0, 0],[2, 2]]

Restangular - How do I reach this API using GET method?

How do I make a GET call to a REST API with the below signature:
http://www.example.com/hierarchies/nodes/1005/parents
I am trying to call the API like so:
var service = Restangular.all('hierarchies');
return service.one('nodes', id).all('parents').get();
But it throws the following error:
TypeError: Cannot read property 'toString' of undefined
The API call(if successful) would respond in a nested format as below:
{
name: "",
children: [
{
name: "",
children: [
{
name: "",
children: [
..
]
}
]
}
]
}
Thanks in advance!
I think if you use all as the last part of the builder, a list is expected and you should use getList instead of get. However the object you are expecting does not look like a list, so you could change the last part of your builder to just use one without the second parameter and then a single object as the response will be expected.
service.one('nodes', 5).one('parents').get().then(function(response) {
});

Resources