Angular 13 - Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed - angular12

Error trying to diff '[object Object]'. Only arrays and iterables are allowed
I am getting the following error
Error: Error trying to diff '[object Object]'. Only arrays and
iterables are allowed
I am consuming a JSON response and trying to display it in the UI in a ComboBox. please find the attached code and let me know what the error is in the code I made
here are the errors when I receive and the data when I do console.log
model.ts
export class FamilleProblem {
familleId!: number;
familleLibelle!: string;
}
service.ts
export class DeclarProblemService {
apiUrlFamille: string = 'https://www.mytds.fr/webservices_test/ws/gig/getFamille';
apiUrlType = 'https://www.mytds.fr/webservices_test/ws/gig/';
familleProblem!: Observable<FamilleProblem[]>;
currentUser!: AuthLoginInfo;
currentParc!: GestionParc;
familleId = null;
constructor(private http: HttpClient,
private tokenService: TokenStorageService,
private gestionParc: GestionParcService) { }
getFamille(): Observable<FamilleProblem[]>{
this.currentUser = this.tokenService.getUser();
let IdxUser = this.currentUser.userId;
let IdUser: string= '' + IdxUser;
this.currentParc = this.tokenService.getParc();
let IdxTypeMat = this.currentParc.materielIdxTypeMat;
let IdTypeMat: string= '' + IdxTypeMat;
let httpOptions = new HttpHeaders({'Content-Type': 'application/json',
'accept': 'application/json',
'LoginUser': IdUser,
'IdxTypeMat': IdTypeMat})
return this.http.get<FamilleProblem[]>(this.apiUrlFamille, {headers: httpOptions});
}
component.ts
export class DeclarProblemComponent implements OnInit {
currentUser = new AuthLoginInfo();
parcs!: GestionParc[];
materielCode!: number;
matrielImmat!: string;
mode!: number;
familleListe: FamilleProblem[] = [];
isLoggedIn= false;
errorMessage: any;
typeProblem: TypeProblem[] = [];
familleId!: number;
constructor(private router: Router,
private gestionParcService: GestionParcService,
private declarProblem: DeclarProblemService,
private tokenService: TokenStorageService) { }
ngOnInit(): void {
this.declareProbleme();
}
declareProbleme(){
this.declarProblem.getFamille().subscribe({
next: (familleListe: any) => {
//this.tokenService.saveToken(data.accessToken);
this.familleListe = familleListe;
//this.familleListe = [];
console.log(this.familleListe);
//this.familleListe = Array.from(Object.values(res.data));
this.isLoggedIn = true;
},
error: err => {
this.errorMessage = err.error.message;
}
});
}
template.html
<div class="row">
<label class="col-sm-4 col--form-label">Problème constaté :</label>
<div class="col-sm-8">
<select class="form-select" id="familleId" name="familleId" >
<option *ngFor="let famille of familleListe [value]="famille.familleId" >
{{famille.familleLibelle}}
</option>
</select>
</div>
</div>

finally the problem is that I used a our name for iterated and not the one of the server response is an object, but not an array.
Service.ts
getFamille(): Observable<FamilleProblem[]>{
let httpOptions = new HttpHeaders({'Content-Type': 'application/json',
'accept': 'application/json'
})
return this.http.get<{listeDesFamilles:FamilleProblem[]}>(this.apiUrlFamille, {headers: httpOptions}).pipe(
map(listeDesFamilles => listeDesFamilles.listeDesFamilles)
);

Related

Flow error `indexer property is missing in undefined`

Error:
Cannot get keys[key] because an indexer property is missing in undefined
Code:
export type Keys = {
[string]: string,
id?: number,
href?: string,
expand?: Array<string>
}
const keys: Keys = {
};
const requiredKeys: Array<string> = ['aa'];
let keyedUrl: string = 'hi/:aa/test';
function a(keys?: Keys) {
requiredKeys.forEach((key: string) => {
keyedUrl = keyedUrl.replace(`:${key}`, keys[key]);
delete keys[key];
});
}
a(keys);
Live Example
How do I fix this?

Reset ActivatedRoute in Angular 4

There is some code I was using that builds up a series of breadcrumb links via a do while loop. The loop terminates when the ActivatedRoute is null as below:
constructor(
private router: Router,
private route: ActivatedRoute
) {
this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event) => {
this.breadcrumbs = [];
let currentRoute = this.route.root,
url = '';
do {
const childrenRoutes = currentRoute.children;
currentRoute = null;
childrenRoutes.forEach(route => {
if (route.outlet === 'primary') {
const routeSnapshot = route.snapshot;
url += '/' + routeSnapshot.url.map(segment => segment.path).join('/');
this.breadcrumbs.push({
label: route.snapshot.data,
url: url
});
currentRoute = route;
}
});
} while (currentRoute);
});
After updating to Angular 4.4.4 and TypeScript 2.5.3 the following assignment no longer compiles:
currentRoute = null;
where current route is an ActivatedRoute.
Type null is not assignable to type ActivateRoute
I've tried
private route: ActivatedRoute | any
but that in turn breaks other parts of the method. So im not sure if i should be fixing those issues or resetting ActivatedRoute in a more appropriate way.
You could try ActivatedRoute | null, but I generally shy away from null unless I have a compelling reason for a value of null.
Try using currentRoute = undefined.

Problems with onQueryEvent observable failing upon routing in Nativescript with Angular

I am using Nativescript with Angular and have code written that succesfully calls an onQueryEvent from the nativescript-firebase-plugin for data set upon first building the application. However after following a route to a second component containing the exact same onQueryEvent the data succeeds to retreive a complete list but skips the onQueryEvent.
In all honesty I don't know best practices for queries in any situation let alone this one, so I hope it is just a matter of manipulating how I call the onQueryEvent.
I believe the problem to be in the firebase.query inside the getMyTransactionList() function of the firebase.service.ts file.
the overview.component.html page has a transaction | async RadListView that successfully filters upon running tns run android. Then clicking any link directing to the deal-summary.component.html page where the function is re-iterated refuses to query by the static storage variable set in the firebase.service
Here is my code:
firebase.service.ts
export class FirebaseService {
private _allItems: Array<any> = [];
items: BehaviorSubject<Array<any>> = new BehaviorSubject([]);
public storage: any = '-KomUSGcX-j6qQmY4Wrh'; // set statically to test different routes
constructor(
private ngZone: NgZone,
){}
// fetch data
getMyDealList(): Observable<any> {
return new Observable((observer: any) => {
let path = `deals/${BackendService.token}`;
let onValueEvent = (snapshot: any) => {
this.ngZone.run(() => {
let results = this.handleSnapshot(snapshot.value);
observer.next(results);
});
};
firebase.addValueEventListener(onValueEvent, `/${path}`);
}).share();
}
getMyTransactionList(): Observable<any> {
return new Observable((observer: any) => {
let path = `transactions/${BackendService.token}`;
// this is a merge of jen loopers giftler code combined with nativescrip-firebase-plugins standard onQueryEvent. It works on the first load but routing to a second instance of the same function retrieves all the data without queryEvent
let onQueryEvent = (snapshot: any) => {
this.ngZone.run(() => {
let results = this.handleSnapshot(snapshot.value);
observer.next(results);
});
};
firebase.query(
onQueryEvent,
`/transactions/${BackendService.token}`,
{
singleEvent: true,
orderBy: {
type: firebase.QueryOrderByType.CHILD,
value: 'dealId' // mandatory when type is 'child'
},
range:
{
type: firebase.QueryRangeType.EQUAL_TO,
value: `${this.storage}` // this calls a static variable for testing consistency
}
,
}
);
firebase.addValueEventListener(onQueryEvent, `/${path}`);
console.log("transaction Listener added");
}).share();
}
handleSnapshot(data: any) {
//empty array, then refill and filter
this._allItems = [];
if (data) {
for (let id in data) {
let result = (<any>Object).assign({id: id}, data[id]);
this._allItems.push(result);
}
this.publishUpdates();
}
return this._allItems;
}
publishUpdates() {
// here, we sort must emit a *new* value (immutability!)
this._allItems.sort(function(a, b){
if(a.date < b.date) return -1;
if(a.date > b.date) return 1;
return 0;
})
this.items.next([...this._allItems]);
}
}
app.component.ts
<page-router-outlet></page-router-outlet>
overview.component.ts
export class OverviewComponent implements OnInit {
public deals: Observable<any>;
public transactions: Observable<any>;
constructor(private router: Router,
private firebaseS: FirebaseService,
){ }
ngOnInit() {
this.deals = <any>this.firebaseS.getMyDealList();
this.transactions = <any>this.firebaseS.getMyTransactionList();
}
viewDealSumm(id){
this.router.navigate(['dashboard/deal-summary', id]);
}
}
overview.component.html
<RadListView [items]="deals | async ">
<ng-template tkListItemTemplate let-item="item">
<StackLayout (tap)="viewDealSumm(item.id)">
<Label [text]="item.dealName"></Label>
</StackLayout>
</ng-template>
</ListViewGridLayout>
</RadListView>
<RadListView [items]="transactions | async " >
<ng-template tkListItemTemplate let-item="item">
<GridLayout>
<Label [text]="item.transName"></Label>
</GridLayout>
</ng-template>
</RadListView>
deal-summary.component.ts
export class DealSummaryComponent implements OnInit {
public transactions: Observable<any>;
constructor(
private firebaseS: FirebaseService,
){ }
ngOnInit() {
this.transactions = <any>this.firebaseS.getMyTransactionList();
}
deal-summary.component.html
<RadListView [items]="transactions | async " >
<ng-template tkListItemTemplate let-item="item">
<GridLayout >
<Label col="1" [text]="item.transName"></Label>
</GridLayout>
</ng-template>
</RadListView>

Aurelia: How to handle a async request in a view?

I have a dotnet core api that returns a FileContentResult..
return new FileContentResult(bytes, contentType)
{
FileDownloadName = Path.GetFileName(request.Filename)
};
Via postman I can read out the image perfectly fine. Now I want to read the image, via the aurelia fetch client, and show it in my html view. This is my function to retrieve the image from the api.
public image(filename: string) {
return this.http.fetch(AppConfiguration.base_url + 'assets/image',
{
method: 'post',
body: json({
filename: filename
})
});
}
I've tried to convert the blob in the response with this value converter. But I can't get that to work
Converter:
export class BlobToUrlValueConverter {
public toView(blob) {
return URL.createObjectURL(blob);
}
}
Viewmodel:
export class Dashboard {
public blob: any;
constructor(
public assets_service: AssetsService
) { }
async attached() {
let response = await this.assets_service.image('test.png');
this.blob = response.blob();
}
}
View
<div if.bind="blob">
${ blob | blobToUrl }
</div>
I'm not sure this is the right approach. Also not sure how handle the async request part of it either. What is the best way to get that image response to show in the html view? Lets say via a img tag?
I was close. Here is how I got the image to show.
Viewmodel:
export class Dashboard {
public url: string;
constructor(
public assets_service: AssetsService
) { }
async attached() {
let blob = await this.assets_service.image('test.png')
.then(response => response.blob());
this.url = URL.createObjectURL(blob);
}
}
View:
<div if.bind="url">
<img src.bind="url">
</div>
EDIT:
Found a better solution using parts written above:
The exported function that does the call (for reusability on both ts and html sides):
export function image_request(filename: string): Promise<Response> {
let http = new Http();
return http.fetch(<your-url-that-fetches-the-image>,
{
method: 'post',
body: json({
filename: filename
})
});
}
Value converter that uses above function
import { image_request } from './AssetsRequests';
export class ImageRequestValueConverter {
public toView(filename: string) {
return image_request(filename);
}
}
The important and most awesome part of the solution. Many thanks to http://www.sobell.net/aurelia-async-bindings/
for getting me on my way. You can override the binding behaviour. You can use this override to process async
Promise in a view in combination with a value converter.
export class AsyncImageBindingBehavior {
public bind(binding, source): void {
binding.originalupdateTarget = binding.updateTarget;
binding.updateTarget = (target) => {
// When we have a promise
if (typeof target.then === 'function') {
// Set temp value to loading so we know its loading
binding.originalupdateTarget('Loading...');
// Process the promise
target
.then(response => response.blob())
.then(blob => binding.originalupdateTarget(
URL.createObjectURL(blob)
));
}
else {
binding.originalupdateTarget(target);
}
};
}
unbind(binding) {
binding.updateTarget = binding.originalupdateTarget;
binding.originalupdateTarget = null;
}
}
Finally the view is very simple
<img src="${ 'test.png' | imageRequest & asyncImage }">

Efficient async call in Pipe angular2

I am trying to get an address conversion of longitude and latitude values via the google maps api. I have looked into the angular2 pipe example concerning impure pipes (https://angular.io/docs/ts/latest/guide/pipes.html), and this indeed gives me a working piece of code (pure pipes result into null)
The only problem is, that the http call is being done multiple times, because of the impure pipe. The data does not change anymore, and needs to be converted only once.
The pipe:
import {Pipe, PipeTransform} from '#angular/core';
import {Http} from '#angular/http';
#Pipe({name: 'location', pure: false})
export class LocationPipe implements PipeTransform {
private readableLocation = null;
private fetchedJson = null;
private prevUrl = '';
constructor(private http:Http) {}
transform(location:string):string {
if (location !== this.prevLoc) {
var arr = location.split(",", 2);
var lat = arr[0];
console.log(lat);
var lon = arr[1];
console.log(lon);
var url = 'http://maps.googleapis.com/maps/api/geocode/json?latlng=' + lat + ',' + lon + '&sensor=true';
console.log(url);
this.prevLoc = location;
this.fetchedJson = null;
this.http.get(url)
.map( result => result.json() )
.subscribe( result => this.fetchedJson = result );
}
if (this.fetchedJson != null) {
return this.fetchedJson.results[0].formatted_address;
}
return this.fetchedJson;
}
}
The HTML:
<td>{{log.location | location}}</td>
The 'log' data is received from the backend via a service:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
export class Log {
_id: Object;
username: string;
status: boolean;
location: string;
modelName: string;
searchTerm: string;
created: number;
static create(data){
console.log("New log created and added to the list:");
console.log(data);
return new Log(data);
}
constructor(data){
this._id = data._id;
this.username = data.username;
this.status = data.status;
this.location = data.location;
this.modelName = data.modelName;
this.searchTerm = data.searchTerm;
this.created = data.created;
}
}
#Injectable()
export class LogService {
public logs;
constructor(private http: Http){
this.logs = http.get('https://arest-api.herokuapp.com/users?userStatus=true')
.map(res => res.json())
.map(rawUsers => rawUsers.map(Log.create));
}
}
log.location contains the lon and lat values in string format: "51.993398,4.386851"
It takes some seconds for the data to load and display, although it contains only 2 records.
Is there perhaps any better/faster/more efficient way of getting the same result?
Thank you so much for your help! Please let me know if any more information is needed.
edit: moving up the caching results in only two requests. Though the question remains whether this could be implemented in a better/more efficient way? The impure pipe is still called upon many times, while the data does not change.

Resources