Help with Angular Service

1185
1
03-26-2020 10:18 AM
ionarawilson
New Contributor III

I am a beginner with the angular framework. I need to populate a drop in my component. The elements will come from a feature layer. The drop down needs be populated in the beginning to work or I guess I could subscribe to a method. I created a service that should return a promise so and on the element I believe I am subscribing to the method. However I am having issues with returning a promise in the service. Can you guys tell me how I should change the service to return the promise I need or if there is a better way to do it? I am using Angular 9, and the esri loader I think it is the latest because I used the command

npm install --save esri-loader 

to install it, although my package.json lists the 2.13.0 version. Thank you for any help!

The Component:


@Component({
  selector: 'app-esri-map',
  templateUrl: './esri-map.component.html',
  styleUrls: ['./esri-map.component.css']
})

export class EsriMapComponent implements OnInit {

   // this is needed to be able to create the MapView at the DOM element in this component
   @ViewChild('mapViewNode', { static: true })  mapViewElElementRef;

//  @ViewChild('economicimpactfactorsselect', { static: false }) economicimpactsselect: ElementRef;
  attributesarray;
  constructor(private esriMapServiceEsriMapService) { }

  public ngOnInit() {

    this.esriMapService.loadMap(this.mapViewEl).then(attrarray => this.attributesarray = attrarray)
 
  } // ngOnInit

}

The Service:

import {ElementRefInjectableViewChildfrom '@angular/core';
import { loadModules } from 'esri-loader';
import esri = __esri;

@Injectable()
export class EsriMapService {

     mapView;
  constructor() { }

  loadMap(mapContainerElementRef) {
 
    const promise = new Promise((resolvereject=> {
      loadModules([
        'esri/Map',
        'esri/views/MapView',
        "esri/layers/FeatureLayer"
      ])
        .then(([EsriMapEsriMapViewFeatureLayer]) => {

          let mapesri.Map = new EsriMap({
            basemap: 'hybrid'
          });
          const economicimpactLayer = new FeatureLayer({
            url: '//tfsgis.tamu.edu/arcgis/rest/services/EconomicImpactNew/TFEI_new_notsde/MapServer/0',
            outFields: ["*"],
            id: "economicimpactLayer"
          });
          let mapViewesri.MapView = new EsriMapView({
            container: mapContainer.nativeElement,
            center: [-99.531.2],
            zoom: 6.5,
            map: map
          });
  map.add(economicimpactLayer);
          mapView.when(() => {
            return economicimpactLayer.when(function() {

                let values = [];
                
                               for (let i = 0i < economicimpactLayer.fields.lengthi++) {
                                  console.log(economicimpactLayer.fields[i].alias);
                                 values.push(economicimpactLayer.fields[i].alias)
                                
                                }
                                return values
                     
                              }).then(getValues)
            resolve('true');
          }, err => {
            console.error(err);
            reject(err);
          });

          function getValues(response) {
            let array = [];
            for (let i = 0i < response.lengthi++)
               {
           array.push({name: response[i]});
                 }    
        
             this.attributesarray = array.slice();
             console.log(this.attributesarray)
          
                }
        })
        .catch(err => {
          console.error(err);
          reject(err);
        });
    });

    return promise;
    }
}

And the html:

<div class="container">
    <div class="row">
      <div class="column-12">
          <h5>Summary Tool (click one of the buttons below to start)</h5>
  
          <select name="economicimpactfactorsselect" id="economicimpactfactorsselect" >
             <option value="" selected>

             </option>
            <option *ngFor="let attribute of attributesarray let i = index;" value={{i}}>{{attribute.name}}</option> 
          </select> 

      </div>
      <div class="column-12">

        </div>
    </div>
  </div>
  <br>
<div #mapViewNode></div>
0 Kudos
1 Reply
RichardReinicke
Occasional Contributor II

Hello ionara wilson‌,

I've modified your code a little to match it to my data and use case, fixed some style issues but overall the code should be easy to understand. Please see the changes between both versions.

Component Template:

<div class="flex-esri-map-container">
  <div #mapViewNode class="esri-map-container"></div>
</div>
<select name="economicimpactfactorsselect" id="economicimpactfactorsselect" >
  <option value="" selected>

  </option>
  <option *ngFor="let attribute of attributes let i = index;" value={{i}}>{{attribute.name}}</option>
</select>

Component Code:

import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { loadModules } from 'esri-loader';
import { Config } from '../../configuration/config';
import { ConfigService } from '../../configuration/config.service';
import { EsriMapService } from './esri-map.service';

@Component({
  selector: 'fssb-esri-map',
  templateUrl: './esri-map.component.html',
  styleUrls: ['./esri-map.component.scss']
})
export class EsriMapComponent implements OnInit {

  attributes: any[];

  // tslint:disable-next-line:no-input-rename
  @ViewChild('mapViewNode', { static: true }) private mapViewEl: ElementRef;

  constructor(private esriMapService: EsriMapService) { }

  ngOnInit(): void {
    // this.initializeMap();
    this.esriMapService.loadMap(this.mapViewEl).then(attributes => {
      this.attributes = attributes;
    });
  }
}

Service with Promise:

import { ElementRef, Injectable } from '@angular/core';
import { loadModules } from 'esri-loader';

import esri = __esri;

@Injectable({
  providedIn: 'root'
})
export class EsriMapService {

  mapView;
  constructor() { }

  loadMap(mapContainer: ElementRef): Promise<any[]> {

    const promise = new Promise<any[]>((resolve, reject) => {

      loadModules([
        'esri/Map',
        'esri/views/MapView',
        'esri/layers/FeatureLayer'
      ])
        .then(([EsriMap, EsriMapView, FeatureLayer]) => {

          const map: esri.Map = new EsriMap({
            basemap: 'hybrid'
          });

          const countyLayer = new FeatureLayer({
            url: 'https://ripsstaging.lubw.baden-wuerttemberg.de/arcgis/rest/services/webapps/Hochwasser/MapServer/17',
            outFields: ['*'],
            id: 'counties'
          });

          countyLayer.on('layerview-create', (layerViewCreated: LayerViewCreatedResult) => {

            const layer: esri.FeatureLayer = layerViewCreated.layerView.layer as esri.FeatureLayer;
            const values = [];

            for (const field of layer.fields) {
              console.log(field.alias);
              values.push({name: field.alias});
            }

            const outArray = values.slice();
            console.log(outArray);
            resolve(outArray);

          });

          const mapView: esri.MapView = new EsriMapView({
            container: mapContainer.nativeElement,
            center: [-99.5, 31.2],
            zoom: 6.5,
            map
          });

          mapView.when(() => {
            map.add(countyLayer);
          }).catch(err => {
            console.error(err);
            reject(err);
          });

      }, err => {
        console.error(err);
        reject(err);
      });

    });

    return promise;
  }
}

interface LayerViewCreatedResult {
  view: esri.View;
  layerView: esri.LayerView;
}

After that I get the feature layer attribute aliases as values into my drop down list:

I hope that helps anybody who have a similar task. By the way in Angular I tend to use observable services with private subjects.

Best regards
Richard

0 Kudos