Angular 5 - JSON to "scope" binding to *NgFor - arrays

I want to bind the data that i get from my JSON request to the *NgFor to be able to read it out, This is how the data looks like that i get in:
{Id: null, Code: "9348892084", HasInfo: true, Info: Array(26)}
HasInfo:true
Info:
Array(26)
0:{ProductId: 32631, Name: "JNOOS", Image: "http://sd-m-mg", …}
1:{ProductId: 32969, Name: "SWIFT", Image: "http://sd-33087.jpg",…}
2:{ProductId: 32570, Name: "GABIX", Image: "http://sd-c7273.jpg", …}
3:{ProductId: 32473, Name: "MISMA", Image: "http://sd-mt8-8343e4d95.jpg", …}
I was working with AngularJS before and there i made the request as such:
$scope.getAll{
$http({
method: 'Get',
url: "http://URL/" + Code
})
.success(function (data) {
$scope.All = data.Info;
})
No i am moving to Angular5 and i would like to get the same array of information bind to the :
<div *ngFor="let x of info">
{{ x.Name }}
</div>
How would i adjust the below to get the same as above?
export class AppComponent {
readonly ROOT_URL = 'http://url/content/'
constructor(private http: HttpClient) {}
ngOnInit() {
this.getData()
}
getData() {
this.http.get(this.ROOT_URL + 'Getscope')
.subscribe(
data => {
var test = data;
// var info = data.Info = not valid!;
// var test can't be approached by *ngFor
console.log(test);
console.log(test.info);
//$scope.info = data.Info;
//this.info = data;
}, error => {
console.error("HTTP FAILURE ERROR!!!");
}
)
};
}
Also the json output has an array inside an object, do i say this correct?

From your code you are using info in html but not assigning to any class variable,
Take a public variable public info; and assign data using this.info = data.Info
Component:
export class AppComponent {
readonly ROOT_URL = 'http://url/content/'
public info;
constructor(private http: HttpClient) {}
ngOnInit() {
this.getData()
}
getData() {
this.http.get(this.ROOT_URL + 'Getscope')
.subscribe(
data => {
this.info = data['Info']; // here is the change
}, error => {
console.error("HTTP FAILURE ERROR!!!");
}
)
};
}
Your HTML can be same:
<div *ngFor="let x of info">
{{ x.Name }}
</div>

The simplest solution would be to use an async pipe. This way you do not need to subscribe to the stream returned by http.get.
app.component.ts
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
export class AppComponent {
readonly url = 'http://url/content/'
constructor(private http: HttpClient) {}
info$: Observable<any[]>;
ngOnInit() {
this.info$ = this.http.get(this.url + 'Getscope')
.pipe(map(resp => resp.Info));
}
}
app.component.html
<div *ngFor="let x of info$ | async">
{{ x.Name }}
</div>

Related

when i push select2OptionData object in an array, select2 don't display any value

select2 in my angular project. I want to display a select of an array of certificate requested from an api. For each element if I create and push a select2OptionData Object in an array, but when i want to display it in the Html page it is selected but without any option. When i create an array of object like the gitHub demo everything's right.
Can you help me please.
Here is the ts
export class DashboardCvComponent implements OnInit, OnDestroy {
certificateForm: FormGroup;
certificates: Array<Certificate>;
certificatesTypes: Array<Select2OptionData>;
exampleData: Array<Select2OptionData>;
constructor(private formBuilder: FormBuilder, private certificatesService: CertificatesService,
private trainingsService: TrainingsService, private authService: AuthenticationService,
private datepipe: DateFormatPipe, private CvService: CVService, private fileUploaService: FileUploadService) {
this.certificatesTypes = [];
this.exampleData = [];
}
ngOnInit() {
// get certificate types
this.certificatesService.getAllCertificatesType('50').subscribe(
(response) => {
// console.log('-> CERTIFICATES TYPES LOADING successful : ', response);
/* this.certificatesTypes = response.data; */
response.data.forEach(element => {
const certif = {id: element.id.toString(), text: element.name};
this.certificatesTypes.push(certif);
});
this.exampleData = [{
id: 'basic1',
text: 'Basic 1'
},
{
id: 'basic2',
disabled: true,
text: 'Basic 2'
},
{
id: 'basic3',
text: 'Basic 3'
},
{
id: 'basic4',
text: 'Basic 4'
}]
console.log('les certif', this.exampleData, this.certificatesTypes);
},
(error) => {
console.log('-> CERTIFICATES TYPES LOADING failed : ', error);
},
() => {
}
);
and the html
<div class="dashboard--degrees">
<app-certificate *ngFor="let certificate of certificates" [certificate]=certificate [url]=url></app-certificate>
<button class="button button--bluegreen" type="button" data-modal="modal-certificate">
<svg class="icon icon-plus-o">
<use xlink:href="#icon-plus-o"></use>
</svg> <span i18n="##cvcomponent-addcertificate">J'ajoute un diplôme </span>
</button>
<select2 [data]="certificatesTypes"></select2>
<select2 [data]="exampleData"></select2>
</div>
Here the exampleData select display well but not the certificatesTypes
Don't use this.certificate.push inside for loop. it will not work. instead of you can use something like :
let arrCertificates = [];
response.data.forEach(element => {
const certif = {id: element.id.toString(), text: element.name};
arrCertificates.push(certif);
});
this.certificatesTypes = arrCertificates;

Dynamically add component with data to azure map Angular 7

I am using Azure Maps and am trying to create a reusable library around their maps web sdk. I want to provide a way for a user to pass in their own popup, along with an array strongly typed objects, to add markers to the page. I have the map and the markers working, but am stuck on how to pass data to the popup. Currently I have:
az-map.component.html
<div class="map-wrapper">
<div id="az-map"></div>
</div>
<ng-template #defaultMarker>
<div>
<img alt="" src="https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi2.png" />
</div>
</ng-template>
<ng-template #defaultPopup>
<div>
<h2>~name~</h2>
</div>
</ng-template>
<div style="display:none">
<div id="marker" #marker>
<ng-container *ngTemplateOutlet="markerTemplate ? markerTemplate : defaultMarkerTemplate"></ng-container>
</div>
<div id="popup" #popup>
<ng-content select="[popupTemplate]"></ng-content>
</div>
</div>
az-map.component.ts
#Component({
selector: 'az-map',
templateUrl: './azure-map.component.html',
styleUrls: ['./azure-map.component.scss']
})
export class AzureMapComponent implements OnInit {
#Input() locations: Location[];
#Input() mapConfig: AzureMapConfig;
#Input() markerTemplate: TemplateRef<any>;
#ViewChild('defaultMarker') defaultMarkerTemplate: TemplateRef<any>;
#ViewChild("popup") popupElement: ElementRef;
#ViewChild("marker") marker: ElementRef;
/* These are the map components we can interact with */
public map: any;
public popup: atlas.Popup;
public symbolLayer: atlas.layer.SymbolLayer;
private dataSource: atlas.source.DataSource;
constructor(private configService: ConfigurationService) { }
ngOnInit() {
const centerPosition: any = this.mapConfig.center.length
? this.mapConfig.center
: ( this.locations && this.locations.length > 0 ? [this.locations[0].longitude, this.locations[0].latitude] : [-95, 38]);
this.map = new atlas.Map('az-map', {
center: centerPosition,
zoom: this.mapConfig.zoom,
authOptions: {
authType: atlas.AuthenticationType.subscriptionKey,
subscriptionKey: this.configService.get().azureMapKey
}
});
this.map.events.add('ready', () => {
this.dataSource = new atlas.source.DataSource();
this.map.sources.add(this.dataSource);
this.addMarkers();
this.symbolLayer = new atlas.layer.SymbolLayer(this.dataSource, null)
this.map.layers.add(this.symbolLayer);
if (this.mapConfig.showControls) {
this.map.controls.add(new atlas.control.ZoomControl(), {
position: "top-right"
});
}
if(this.mapConfig.showPopups){
this.popup = new atlas.Popup({
pixelOffset: [0, -18],
closeButton: false
});
const _this = this;
this.map.events.add('mouseover', this.symbolLayer, (e) => {
if (e.shapes && e.shapes.length > 0) {
var coordinate;
const popupHtml = _this.popupElement.nativeElement.innerHTML;
var props = e.shapes[0].getProperties();
coordinate = e.shapes[0].getCoordinates();
_this.popup.setOptions({
//Update the content of the popup.
content: popupHtml,
//Update the popup's position with the symbol's coordinate.
position: coordinate
});
//Open the popup.
_this.popup.open(_this.map);
}
});
this.map.events.add('mouseleave', this.symbolLayer, (e) => {
this.popup.close()
});
}
});
}
private addMarkers(): void {
const html = this.marker.nativeElement.innerHTML;
this.locations.forEach(l => {
const point = new atlas.Shape( new atlas.data.Point([l.longitude, l.latitude]));
this.dataSource.add(point);
});
}
}
and an example of it being used here:
<h1>Account Coverage</h1>
<div class="map-wrapper col-12">
<az-map [mapConfig]="mapConfig" [locations]="locations" class="col-12 h-50">
<coverage-popup popupTemplate></coverage-popup>
</az-map>
</div>
This works and shows the popup, but I need to find a way to pass data to the CoveragePopupComponent in the mouseover event in the az-maps.component.ts
One method we tried to use, which works well when you only have a single popup type that needs to be handled was to use the ComponentFactoryResolver like this:
const factory = this.componentFactoryResolver.resolveComponentFactory(PopupComponent);
const component = factory.create(this.injector);
component.instance.machine = shape.getProperties();
component.changeDetectorRef.detectChanges();
const popupContent = component.location.nativeElement;
this.map.setCamera({
center: shape.getCoordinates(),
type: 'ease',
});
this.popup.setOptions({
position: shape.getCoordinates(),
content: popupContent,
pixelOffset: [0, -20]
});
this.popup.open(this.map);
but then you always have to use the PopupComponent type and couldn't use the ng-content stuff we have in the map template.

Angular 4 Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays

Here is the code below:
component.ts
by selecting match geting id in routing, then taking this id from URL
export class MatchComponent implements OnInit {
_postArrayMatch: match[];
constructor(public router:Router, private matchService: MatchService,
private route: ActivatedRoute) {}
ngOnInit(){
this.getMatchId();
}
getMatchId() :void {
this.route.params.forEach((params: Params)=> {
let id = +params['id'];
this.matchService.getMatch(id).subscribe(
resultArray => this._postArrayMatch = resultArray,
error => console.log("Error ::" + error))
})
}
component.html
just basic interpolation by doing ngFor loop
<div *ngFor="let post of _postArrayMatch">
<p>{{post.team1.name}}</p>
</div>
service.ts
passing the dynamic id
getMatch(id:number): Observable<match[]>{
return this.http.get<match[]>(`http://localhost:3000/match/${id}`)
}
interface
export interface match{
team1:{
name:string,
id:number
}
team2:{
name:string,
id:number
}
}
Try something like this where you create the object in your response
component.ts
export class MatchComponent implements OnInit {
_postArrayMatch: match[];
newArr: Array<Object> = [];
constructor(public router:Router, private matchService: MatchService,
private route: ActivatedRoute) {}
ngOnInit(){
this.getMatchId();
}
getMatchId() :void {
this.route.params.forEach((params: Params)=> {
let id = +params['id'];
this.matchService.getMatch(id).subscribe(
resultArray => {
this._postArrayMatch = resultArray;
const keys = Object.keys(this._postArrayMatch) // new stuff starts here
for(let i = 0; i < keys.length; i++) {
newArr.push(this._postArrayMatch[keys[i]]);
newArr[i]['team'] = keys[i];
}
},
error => console.log("Error ::" + error))
})
}
And then you can access ALL of your sub objects in your html
Component.html
<div *ngFor="let post of newArr">
<p> {{post.team}}: {{post.name}}</p>
</div>
Currently, with what you have you are hard coding for team 1, and if you want to do that then you shouldn't be using the *ngFor
Thats seems the easiest way i could find on how to get the data from objects.
<div *ngFor="let post of objectKeys(_postArrayMatch.team1)">
<div> Team1: {{ _postArrayMatch.team1[post]}}</div>
</div>
component.ts
objectKeys = Object.keys;

AngularJS Typescript Controller Values not updating page

I'm picking up Angular for the first time and coming from a .NET backround feel a lot more comfortable working with typescript.
I've got a problem where a cant seem to get values to update on a page when a controller populates the controller values with an object received from a service. I'm probably just making a noob error.
Can anyone give a pointer on what I'm doing wrong.
So my typescript is
module ConnectAdmin.Interfaces {
export interface ITemplate {
templateId: number;
name: string;
description: string;
}
}
module ConnectAdmin.Interfaces {
export interface ITemplateCollection {
total: number;
set: number;
skipped: number;
collection: Array<ITemplate>;
}
}
module ConnectAdmin.Controllers {
import TemplateCollection = Interfaces.ITemplateCollection;
export class TemplateIndexController {
static $inject = ["ConnectAdmin.Services.TemplateService"];
constructor(templateService: ConnectAdmin.Services.TemplateService) {
this.defaultTemplates = { skipped: 0, set: 0, total: 0, collection: [] };
this.templates = this.defaultTemplates;
this.processing = true;
this.store = this;
templateService.index(this.take, this.skip, this.successCallback, this.errorCallback);
this.processing = false;
}
successCallback(data: TemplateCollection) {
this.templates = { skipped: 0, set: 0, total: 0, collection: [] }
this.templates = data;
alert(this.templates.collection.length);
}
errorCallback(response: any) {
this.templates = this.defaultTemplates;
alert(response.status);
this.message = "An Error Occurred Contacting the API";
}
processing: boolean;
store = this;
defaultTemplates: TemplateCollection;
templates: TemplateCollection;
take = 20;
skip = 0;
message: string;
}
angular.module("ConnectAdmin").controller("ConnectAdmin.Controllers.TemplateIndexController", TemplateIndexController);
}
module ConnectAdmin.Services {
import TemplateCollection = Interfaces.ITemplateCollection;
import TemplateIndexController = Controllers.TemplateIndexController;
export class TemplateService {
constructor($http: ng.IHttpService) {
this.http = $http;
}
http: ng.IHttpService;
index(take: number, skip: number, successCallback: Function, errorCallback: Function) {
const req = {
method: "GET",
url: "https://localhost:44336/api/Templates?take=" + take + "&skip=" + skip,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
};
this.http(req).then(response => {
successCallback(response.data);
},
response => {
errorCallback(response);
});
//return { total: 1, skipped: 0, set: 1, collection: [{ templateId: 1, name: "Template 1", description: "" }] };
}
}
angular.module("ConnectAdmin").service("ConnectAdmin.Services.TemplateService", TemplateService);
}
with my html being:
<div id="template-index">
<div class="row">
<div class="col-sm-12">
<div class="info-box">
<div class="info-box-title">
Templates
</div>
<div class="info-box-content" ng-controller="ConnectAdmin.Controllers.TemplateIndexController as templateCtrl">
<table class="table table-striped table-responsive">
<thead>
<tr>
<td>#</td>
<td>Name</td>
<td>Description</td>
</tr>
</thead>
<tbody>
<tr class="tr-select" ng-click="templateCtrl.openTemplate(template.templateId)" ng-repeat="template in templateCtrl.templates.collection">
<td>{{template.templateId}}</td>
<td>{{template.name}}</td>
<td>{{template.description}}</td>
</tr>
</tbody>
</table>
<div id="template-index-loader" class="loader" ng-show="templateCtrl.processing"></div>
<div class="info-box-footnote" ng-hide="templateCtrl.templates.collection.length">
Displaying {{templateCtrl.templates.skipped + 1}} to {{templateCtrl.templates.set + templateCtrl.templates.skipped}} of {{templateCtrl.templates.total}}
</div>
<div class="info-box-footnote" ng-show="templateCtrl.message.length">
{{templateCtrl.message}}
</div>
</div>
</div>
</div>
</div>
I don't think its the http call as I get no update if I return a hardcoded object.
The alert in the success callback gives me a correct value.
Thanks
My hunch is that your scope is being changed in your success callback. Try using your controller scope you defined in your constructor like this
successCallback(data: TemplateCollection) {
store.templates = { skipped: 0, set: 0, total: 0, collection: [] }
store.templates = data;
alert(store.templates.collection.length);
}
Otherwise I think when you say, "this" you're referring to the scope of the successCallback function
Ah great! Thanks, you put me on the right track.
Doing the update you said I was getting intellisense promts to say do you mean this.store which had the same outcome.
Following on from that and doing a bit of digging yes all the variables were undefined.
I've updated the Controller and Service to be:
module ConnectAdmin.Controllers {
import TemplateCollection = Interfaces.ITemplateCollection;
export interface ITemplateIndexScope extends ng.IScope {
vm: any;
}
export class TemplateIndexController {
static $inject = ["ConnectAdmin.Services.TemplateService"];
constructor(templateService: ConnectAdmin.Services.TemplateService) {
this.templates = { skipped: 0, set: 0, total: 0, collection: [] };
this.templateService = templateService;
this.take = 20;
this.skip = 0;
this.refresh();
}
refresh() {
this.processing = true;
this.templateService.index(this.take, this.skip, this);
}
successCallback(data: TemplateCollection) {
this.templates = data;
alert(this.templates.collection.length);
this.processing = false;
}
errorCallback(response: any) {
this.templates = { skipped: 0, set: 0, total: 0, collection: [] };
alert(response.status);
this.message = "An Error Occurred Contacting the API";
this.processing = false;
}
processing: boolean;
templates: TemplateCollection;
take: number;
skip: number;
message: string;
templateService: Services.TemplateService;
}
angular.module("ConnectAdmin").controller("ConnectAdmin.Controllers.TemplateIndexController", TemplateIndexController);
}
module ConnectAdmin.Services {
import TemplateCollection = Interfaces.ITemplateCollection;
import TemplateIndexController = Controllers.TemplateIndexController;
export class TemplateService {
constructor($http: ng.IHttpService) {
this.http = $http;
}
http: ng.IHttpService;
index(take: number, skip: number, templateController: TemplateIndexController) {
const req = {
method: "GET",
url: "https://localhost:44336/api/Templates?take=" + take + "&skip=" + skip,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
};
this.http(req).then(response => {
var data = response.data as TemplateCollection;
templateController.successCallback(data);
},
response => {
templateController.errorCallback(response);
});
}
}
angular.module("ConnectAdmin").service("ConnectAdmin.Services.TemplateService", TemplateService);
}
So I'm passing the instance of the controller into the service rather than the callback functions.
Cheers

Push object into new array with interfaces in Angular2

Im retrieving a list of objects from a service in Angular2.
On click I want to put the object from my ngFor onto a new array (with the same interface).
I'm able to display the list with items just fine. I'm also able to display the item I selected in the console, but I'm not able to push this object into a new array with the same interface.
My interface:
export interface Item {
id: string,
name: string,
icon_url: string,
exterior: string,
type: string,
tag: string,
addedClass: string,
selected: boolean
}
My component:
import {Component, OnInit, DoCheck} from 'angular2/core';
import {Item} from './../interfaces/interfaces.ts';
import {ItemService} from './../services/item.service.ts';
#Component({
template: `
<h2>Add item</h2>
<div class="itemlist">
<ul class="items">
<li
*ngFor="#item of items"
[class.selected]="item === selectedItem"
(click)="onSelect(item)"
>
<span class="item">
{{item.name}}
</span>
</li>
</ul>
</div>
`
})
export class AddTradeComponent implements OnInit {
public items: Item[];
public selectedItems: Item[];
constructor(private _itemService: ItemService) { }
getUserItems() {
this._itemService.getUserItems().then(userItems => this.items = userItems);
}
ngOnInit() {
this.getUserItems();
}
onSelect(item) {
console.log('items ', item);
this.selectedItems.push(item);
}
}
Errors that I'm getting:
Error during evaluation of "click"
TypeError: Cannot read property 'push' of undefined
Logging the object works fine, I just cannot push it onto this.selectedItems.
You didn't initialize your arrays. Either initialize the arrays when you declare your member variables:
public items: Item[] = [];
public selectedItems: Item[] = [];
Or initialize them in your constructor:
constructor() {
this.items = [];
this.selectedItems = [];
}

Resources