I have local json file (Kitchen types), i create KitchenTypesService there are 2 inside function GET AND FIND(ID), GET function its work, but not working find function, have error "ERROR TypeError: Unable to lift unknown Observable type", i try with find function get kitchen with id. Tell me what's the problem
Service
export class KitchenTypesService {
private _jsonURL = 'assets/data/kitchenTypes.json';
constructor(private http: HttpClient) {
this.get().subscribe((data) => data);
}
public get(): Observable<any> {
return this.http.get(this._jsonURL);
}
public find(id: number) {
this.get().subscribe(find((data: any) => data.id == id));
}
}
Component
export class KitchenDimensionComponent implements OnInit {
title: string = 'VirtuvÄ—s matmenys';
step: number = 2;
selectedKitchenId: number;
kitchen: any;
constructor(
private persistenceService: PersistenceService,
private httpKitchenTypes: KitchenTypesService
) {}
ngOnInit(): void {
this.initialSelectedKitchenId();
console.log(this.httpKitchenTypes.find(1));
}
initialSelectedKitchenId(): void {
this.selectedKitchenId = this.persistenceService.get('selectedKitchenId');
}
}
Local KitcehTypes.json
[
{
"id": 1,
"title": "Standartine",
"src": "/assets/images/kitchen-types/one-well.png",
},
{
"id": 2,
"title": "L forma",
"src": "/assets/images/kitchen-types/L-shaped.png",
},
{
"id": 3,
"title": "U forma",
"src": "/assets/images/kitchen-types/U-shaped.png",
},
{
"id": 4,
"title": "G forma",
"src": "/assets/images/kitchen-types/G-shaped.png",
}
]
Error Message
[
So there are a few ways to tackle this. You're right, HttpClient does cause the error you mentioned, but there is a way around this. May be this can help you.
Directly importing the JSON file using resolveJsonModuleTo work with this, you'll need to add the following in your tsconfig.json file
"resolveJsonModule": true Then you can simply import your data by adding this in your service:
import * as data from './kitchenTypes.json'
Note that your will need to update the file path accordingly
Once this is done, you can now access the JSON file data. You can view, and search the contents of the JSON file as per your need.
data: any = (data as any).default;
Related
I have a simple Angular HttpClient, which is correctly returning JSON. I am attempting to cast the results to enforce type safety (not sure if this is correct).
But how do I actually access the returned JSON to copy it into an array?
The httpClient get() request is (and seems to be working fine):
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<Symbols[]>(this.REST_API_SERVER);
}
The Symbols interface is
export interface Symbols {
code: string
desc: string
}
I have a component which calls the data service and is getting a response. However the code below returns an error when attempting to map the JSON into a string array
ERROR TypeError: syms.map is not a function
listOfOption: Array<{ value: string; label: string }> = []
this.dataService.sendGetRequest().subscribe((syms: Symbols[]) => {
console.log('return value ' + JSON.stringify(syms))
// console output shows the returned JSON and it looks correct
//this does not work, how do I copy the results to a string array??
this.listOfOption = syms.map(results => {
return {
value: results.code,
label: results.code,
}
})
})
The JSON data structure is:
{
"results": [
{
"code": "code1",
"desc": "Long description of code 1"
},
{
"code": "code2",
"desc": "Long description of code 2"
},
{
"code": "code3",
"desc": "Long description of code 3"
},
{
"code": "code4",
"desc": "Long description of code 4"
}
]
}
This is driving me crazy
Model a new interface called responseData to support response type.
export interface responseData{
results: Symbols[]
}
export interface Symbols {
code: string
desc: string
}
Update the same in service
public sendGetRequest(): Observable<responseData> {
return this.httpClient.get<responseData>(this.REST_API_SERVER);
}
You can now retrieve the results using array.map()
listOfOption: Array<{ value: string; label: string }> = []
this.dataService.sendGetRequest().subscribe((syms: responseData) => {
console.log('return value ' + syms)
this.listOfOption = syms.results.map(result => {
return {
value: result.code,
label: result.code,
}
})
})
The response data has an object root, but you're trying to parse it as an array root. I think the simplest solution would be something like this:
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<{results: Symbols[]}>(this.REST_API_SERVER)
.pipe(pluck('results'));
}
Which specifies that the response data is an object with a field named results which holds an array of Symbols.
Alternatively you could also extract the response type to a separate definition:
interface ApiResponse {
results: Symbols[]
}
public sendGetRequest(): Observable<Symbols[]> {
return this.httpClient.get<ApiResponse>(this.REST_API_SERVER)
.pipe(pluck('results'));
}
So i have following JSON:
.json
"type": [ {
"id": 2,
"secondid": "1",
"name": "f",
"positionX": 0,
"positionY": 0
}]
and following Service:
public updateposition(Model: typemodel): Observable<any> {
return this.http.post(this.apiEndPoint + '/type' + '/' + typemodel.id , typemodel);
}
and following TS:
.ts
x: number;
y: number;
updateposition()
{
}
So the goal is to update the json object "type" by clicking a button with html. The html part is no problem. But i don't know how to update the json object with the two new positions x and y which are declared in ts. I want to have the process in the function updateposition(). Do you have any suggestions :) ?
You would have to use Dependency Injection to obtain a reference to the Service in your component and use it to retrieve and store the JSON object. Then update it like you would any other object in JS when the button is clicked, and make the API call. Be sure to subscribe to the result or the API call will not go through.
// ...
export class MyComponent {
public jsonObject: TypeModel = { ... };
public constructor(private service: Service) { }
// ...
public function updateposition() {
this.jsonObject[0].positionX = 52;
this.jsonObject[0].positionY = 42;
this.jsonObject = this.service.updateposition(this.jsonObject).subscribe(
result => {
console.log("The API call returned", result);
}
);
}
}
Im working in node.js with typescript and I defined the Tariff and Tariffs classes. I have also fake data created with JSON that should be compatible with the Classes. But I'm getting an error in resolve() method that:
Argument of type '{ blabbla... ' is not assignable to parameter of
type 'Tariffs | PromiseLike'.
export class FakeDataProvider implements IDataProvider {
loadTariffs?(request: LoadTariffsRequest): Promise<Tariffs>{
return new Promise<Tariffs>((resolve, reject) => {
resolve(fakeTariffs);
});
}
}
Next I have defined and exported the classes in another file:
export class Tariff {
tariffOptionId: number = 0;
name: string = '';
}
export class Tariffs {
// tariff: Tariff = new Tariff(); // this does not work
tariff: Array<Tariff> = []; // this does not work too
}
Then I have exported fake mock-up JSON data in another file:
let fakeTariffs =
{
'tariffs': {
'tariff': [
{ "name": "tariff1", "tariffOptionId": 1 },
{ "name": "tariff2", "tariffOptionId": 2 },
{ "name": "tariff3", "tariffOptionId": 3 }
]
}
};
export default fakeTariffs;
What I'm doing wrong and how could I modify the classes in order to be compatible with fake data?
You have;
tarrif
in the first part of your code, and;
tariff
in the JSON...
The solution is to use resolve(fakeData.tariffs) instead of resolve(fakeData).
I am looping through a json file to display data in panels. But i am having some trouble controlling how to display the data appropriately.
This is my json data that is returned from the services:
Object {Group1: Object,
Group2: Object}
The json file data sample:
{
"Group1": {
"name": "Group1List",
"dataFields": [..],
"dataQuery": {..},
"id": 1,
"title": "Group1",
"content": {..}
},
}
This is my services:
getGroupsData(){
return this._http.get('...')
.map((res:Response) => res.json())
}
Component.ts:
groups: Array<any> = [];
getGroups(){
this.groupService.getGroupsData().subscribe(
data => this.groups = data;
}
HTML:
<div dnd-sortable-container [sortableData]="groups" [dropZones]="['container-dropZone']">
<div class="col-sm3" *ngFor="let group of groups; let i = index" dnd-sortable [sortableIndex]="i" [dragEnabled]="dragOperation">
<div class="panel panel-primary" dnd-sortable-container [dropZones]="['widget-dropZone']">
<div class="panel-heading"></div>
<div class="panel-body"></div>
</div>
</div>
</div>
when i render the code i get an error in the console stating: Error trying to diff '[object Object]' in the heading i would like to add Group1 and then in the body i will display different parts from the json.
What is the source of the problem?
*ngFor requires an array, but it looks like you are passing it an object.
If you cannot change the JSON response, and you know the names of the groups beforehand, you can place the objects in an array:
this.groups = [data.Group1, data.Group2, // etc]
*ngFor requires an array [], and you are passing an object
Your sample has unnecessary nested level, flat is always better
Your json should look like this
[
{
"name": "Group1List",
"dataFields": [..],
"dataQuery": {..},
"id": 1,
"title": "Group1",
"content": {..}
},
{
"name": "Group2List",
"dataFields": [..],
"dataQuery": {..},
"id": 2,
"title": "Group2",
"content": {..}
},
// ....
]
Update:
If you have no control over your json scheme, try to flatten it here
getGroupsData(){
return this._http.get('...')
.map((res:Response) => res.json())
.map((obj) => Object.keys(obj).map((key)=>{ return obj[key]})
}
or implement a pipe that iterate over object properties
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push(key);
}
return keys;
}
}
and use it like this
<div *ngFor="let group of groups | keys; let i = index">
</div>
Change your component to:
groups: Array<any> = [];
getGroups(){
this.groupService.getGroupsData().subscribe(
data => this.groups = data.Group1.dataFields;
}
Why?
Because, you want your groups component property to be an array.
So, in your subscribe handler, data will refer to the entire JSON object, and you only care about the Group1 property of your results.
Based on How can I use/create dynamic template to compile dynamic Component with Angular 2.0 I'm currently facing a timing problem when it comes to dynamic property creation of a component model from a json string that is retrieved from a remote server.
Properties are dynamically created properly within in a new entity - that is later assigned to the empty entity, but the hosting component has been already bound to the empty entity and does not know anything about the dynamically created properties at initialization time. It seems that the creation of the hosting component is already finished when the json result from server arrives asynchronously and a bit later.
As a result a list of empty components without any property data is applied to the page. The number of displayed components is equal to the dynamically added properties. But the dynamic data binding obviously does not know anything about the data attached to the properties.
The forementioned original approach loops through a set of hard wired properties of an entity object to assemble the complete template for each property using the specific propertyname.
Defining a hard wired entity object:
entity = {
code: "ABC123",
description: "A description of this Entity"
};
Assembling a dynamic template string looping through properties of entity:
import {Injectable} from "#angular/core";
#Injectable()
export class DynamicTemplateBuilder {
public prepareTemplate(entity: any, useTextarea: boolean){
let properties = Object.keys(entity);
let template = "<form >";
let editorName = useTextarea
? "text-editor"
: "string-editor";
properties.forEach((propertyName) =>{
template += `
<${editorName}
[propertyName]="'${propertyName}'"
[entity]="entity"
></${editorName}>`;
});
return template + "</form>";
}
}
My approach tries to deal with a full configurable approach:
Json string
{
"code": {
"id": "ADRESSE-UPDATE-001",
"componentType": "text-editor",
"rows": 5,
"cols": 25,
"value": "Hello !"
},
"description": {
"id": "ADRESSE-UPDATE-002",
"componentType": "string-editor",
"rows": 1,
"cols": 50,
"value": "Please enter some data"
},
"Street": {
"id": "ADRESSE-UPDATE-003",
"componentType": "text-editor",
"rows": 10,
"cols": 100,
"value": "Mainstreet"
},
"City": {
"id": "ADRESSE-UPDATE-004",
"componentType": "text-editor",
"rows": 3,
"cols": 5,
"value": "Hamburg"
}
}
Basically the Json retrieval and property creation is organized as:
import { Injectable } from '#angular/core';
import {Http, Response} from "#angular/http";
import { Headers, RequestOptions } from '#angular/http';
import {IValue, IEntityClassAdvanced, Member} from './dynamic-config-interfaces';
var jsonX;
#Injectable()
export class DynamicConfigBuilder{
constructor(private http: Http)
{
}
getEntity(callback):any{
this.callConfigApi(callback);
}
callConfigApi(callback:any) {
jsonX = null;
var content = { "Action": "REQUEST-FOR-CONFIG", "Type": 'CONFIG', "Raw":""};
var jsonText = JSON.stringify(content);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post('http://localhost:9123/tasks/config',jsonText, options)
.map((res: Response) => res.json())
.subscribe((message: string) => {
jsonX = JSON.parse(message);
console.log(JSON.stringify(jsonX));
//alert("loaded from server: " + JSON.stringify(jsonX));
var entity: IEntityClassAdvanced = {};
Object.keys(jsonX).forEach(function (key) {
entity[key] = { propertyValue: null };
var val = entity[key];
val.propertyValue = new Member().deserialize(jsonX[key]);
});
callback(entity);
});
}
}
Necessary Interfaces and types are located in a separate file:
import { Injectable} from '#angular/core';
//--- typed nested object from json:
export interface Serializable<T> {
deserialize(input: Object): T;
}
#Injectable()
export class Member implements Serializable<Member> {
id: string;
componentType: string;
rows: number;
cols:number;
value:string;
deserialize(input) {
this.id = input.id;
this.componentType = input.componentType;
this.rows = input.rows;
this.cols = input.cols;
this.value = input.value;
return this;
}
}
//--- dynamic object builder elements
export interface IValue {
propertyValue: Member
}
export interface IEntityClassAdvanced {
[name: string]: IValue;
}
//--- end dynamic object builder
Within the callback method the late assignment occurs:
private entityCallback(entityItem:any)
{
//alert("--->" + JSON.stringify(entityItem));
this.entity = entityItem;
this.refreshContent();
}
The template creation comes with a slightly different flavour:
public prepareTemplate(entity:any, useTextarea: boolean){
let properties = Object.keys(entity);
let template = "<form >";
let editorName = useTextarea
? "otago-text-editor"
: "string-editor";
properties.forEach((propertyName) =>{
// alert(propertyName);
let targetComponent = entity[propertyName]["propertyValue"]["componentType"];
template += `
<${targetComponent}
[propertyName]="'${propertyName}'"
[entity]="entity"
></${targetComponent}>`;
});
return template + "</form>";
}
When trying a hard wired approach using an nested inline object that already exists at compile time the binding works fine:
var json = {
code: {
id: "ADRESSE-UPDATE-001",
componentType: "text-editor",
rows:5,
cols:25,
value:"Hello !"
},
description: {
id: "ADRESSE-UPDATE-002",
componentType: "string-editor",
rows:1,
cols:50,
value:"Please enter some data"
},
firstMember: {
id: "ADRESSE-UPDATE-003",
componentType: "text-editor",
rows:10,
cols:100,
value:"Mainstreet"
},
secondMember: {
id: "ADRESSE-UPDATE-004",
componentType: "text-editor",
rows:3,
cols:5,
value:"Hamburg"
}
};
Any suggestions concerning this generic component creation approach/timing problem appreciated.