Select to view content in your preferred language

Text encoding in widgets

1046
4
06-19-2020 09:16 AM
EvryIndustries_Sweden
Emerging Contributor

I have issues with text encoding in widgets. This is Angular 8 app. Language is Swedish. Swedish symbols from data layer are displayed correctly. But those defined in html do not display correctly:

Is there any way to resolve this issue?

0 Kudos
4 Replies
Noah-Sager
Esri Regular Contributor

Couple questions to help us get going:

1) What version of the ArcGIS API for JavaScript are you using?

2) What widget(s) are you using?

But those defined in html do not display correctly:

3) How is this defined in the html?

0 Kudos
EvryIndustries_Sweden
Emerging Contributor

1) Version is 4.15
2) It's a custom html widgets but when I tried FeatureTable result was the same.
3) I have two options:

<div #mapViewNode></div>
<div class="container">
    <div class="estate-table-container" id="tableDiv"></div>
</div>
<div class="esri-widget my-estate-widget" id="forestryPlansDiv">
    <h2>Åtgärder</h2>
    <select class="esri-select"
            (change)="onForestryPlansSelectChange($event.target.value)" id="forestryPlansSelect">
        <option [value]="option.forestryPlanId" *ngFor="let option of forestryPlanOptions">
            {{ option.forestryPlanName }}
        </option>
    </select>
    <ul>
        <li *ngFor="let doc of forestryPlanDocuments">
            
            <a href="{{doc.link}}" target="_blank"><i class="material-icons">file_present</i>{{doc.name}}</a>
        </li>
    </ul>
    <!--<button class="esri-button" id="zoom">Zoom to selected feature</button>-->
</div>

<app-estate-data-table
        [dataFields]="tableFields"  
        [tableData]="tableData" 
        [selectedDepartment]="selectedDepartment"
        (selectedDepartmentChanged)="onSelectedDepartmentChanged($event)">
</app-estate-data-table>

Like html (id="forestryPlansDiv")
And as angular component (app-estate-data-table). Html in the component: 

<div id="estateDataTable"  class="table-container">
    <div class="table-wrapper" *ngIf="isDataPrepared(dataFields, tableData)">
        <table class="estate-table">
            <thead>
            <tr>
..........

But I guess it is the same for ArcGIS API.
In scripts:

this._view.ui.add(fullscreen, 'top-left');
this._view.ui.add("forestryPlansDiv", "top-right");
this._view.ui.add(toggle, 'top-right');
this._view.ui.add('estateDataTable', 'bottom-leading');


and result the same for both:

0 Kudos
BjornSvensson
Esri Regular Contributor

 Evry Industries Sweden‌ - I'm not familiar with Angular but it "should" work. In your app, how are you retrieving the data used in that table?  I tested with webmap containing a regular feature layer where the field names contain åäö and it seems to work fine. 

Are you overwriting the field names in your code? Could you share your app?  Or the featurelayer you're using?

EvryIndustries_Sweden
Emerging Contributor

Table - it is a custom widget. Such problem I have with all html content on the map.

But if the data from Feature Layer all displays correctly. The same was with Feature Table

Header names are defined in html (like text in tags). Row data is from Feature Layer.

Unfortunately I can't share app or featurelayer. But I can send parts of the code:

import {
    Component,
    OnInit,
    ViewChild,
    ElementRef,
    Input,
    Output,
    EventEmitter,
    OnDestroy,
    Injector
} from '@angular/core';
import { loadModules } from 'esri-loader';
import esri = __esri;
import { EstateConfigService } from '../services/estate-config.service';
import { EstateConfig } from '../config/estate.config';
import { EsryCredentaials } from '../models/EsryCredentaials.model';
import { TranslateService } from '@ngx-translate/core';

@Component({
  selector: 'app-my-estate-map',
  templateUrl: './my-estate-map.component.html',
  styleUrls: ['./my-estate-map.component.scss'],
})
export class MyEstateMapComponent implements OnInit, OnDestroy {
  @Input() personalNumber: string;
  @Output() estateDataLoadedEvent = new EventEmitter<any[]>();

  @ViewChild('mapViewNode', { static: true }) private mapViewEl: ElementRef;

  private _view: esri.MapView = null;
  private _webMap: esri.WebMap = null;
  private _featureLayer: esri.FeatureLayer = null;
  private zoomToExtentFunc: any;
  private highLightPolygonFunc: any;
  tableData: any[];
  tableFields: any[];

  forestryPlanDocuments: any[];
  forestryPlanOptions: any[];
  selectedDepartment: string;

  private estateConfig: EstateConfig;

  constructor(private translateService: TranslateService,injector: Injector) {
      this.estateConfig = injector.get(EstateConfigService);
  }

  ngOnInit(): void {
    try {
      this.initializeMap();
    } catch (e) {
      console.error('EsriLoader: ', e);
    }
    this.tableFields =  [
      {
        name: "Department",
        label: this.translateService.instant('myEstateMap.columnHeadings.department')
      },
      {
        name: "Area",
        label: this.translateService.instant('myEstateMap.columnHeadings.area')
      },
      {
        name: "Age",
        label: this.translateService.instant('myEstateMap.columnHeadings.age')
      },
      {
        name: "MaturityClass",
        label: this.translateService.instant('myEstateMap.columnHeadings.maturityClass')
      },
      {
        name: "ManagementClass",
        label: this.translateService.instant('myEstateMap.columnHeadings.managementClass')
      },
      {
        name: "SiteIndex",
        label: this.translateService.instant('myEstateMap.columnHeadings.siteIndex')
      },
      {
        name: "WoodVolume",
        label: this.translateService.instant('myEstateMap.columnHeadings.woodVolume')
      },
      {
        name: "GrowingDepartmentStock",
        label: this.translateService.instant('myEstateMap.columnHeadings.growingDepartmentStock')
      },
      {
        name: "PineDistribution",
        label: this.translateService.instant('myEstateMap.columnHeadings.pineDistribution')
      },
      {
        name: "SpruceDistribution",
        label: this.translateService.instant('myEstateMap.columnHeadings.spruceDistribution')
      },
      {
        name: "BroadLeavesDistribution",
        label: this.translateService.instant('myEstateMap.columnHeadings.broadLeavesDistribution')
      },
      {
        name: "HardWoodDistribution",
        label: this.translateService.instant('myEstateMap.columnHeadings.hardWoodDistribution')
      },
      {
        name: "ForeignSpeciesDistribution",
        label: this.translateService.instant('myEstateMap.columnHeadings.foreignSpeciesDistribution')
      },
      {
        name: "Stems",
        label: this.translateService.instant('myEstateMap.columnHeadings.stems')
      },
      {
        name: "AverageTreeTrunk",
        label: this.translateService.instant('myEstateMap.columnHeadings.averageTreeTrunk')
      },
      {
        name: "Description",
        label: this.translateService.instant('myEstateMap.columnHeadings.description')
      },
    {
        name: "ActionType",
        label:this.translateService.instant('myEstateMap.columnHeadings.actionType')
    },
    {
        name: "Urgency",
        label: this.translateService.instant('myEstateMap.columnHeadings.urgency')
    },
    {
        name: "RemovalGrowthPerc",
        label: "%"
    },
    {
        name: "RemovalGrowth",
        label: this.translateService.instant('myEstateMap.columnHeadings.removalGrowth')
    },
    {
        name: "YearlyGrowth",
        label: this.translateService.instant('myEstateMap.columnHeadings.yearlyGrowth')
    }];
  }

  onSelectedDepartmentChanged(feature: any): void {
    this.highLightPolygonFunc(feature);
  }
  onForestryPlansSelectChange(value: string): void {
    this.selectedDepartment = '';
    this.forestryPlanDocuments = this.forestryPlanOptions.filter(obj => { return obj.forestryPlanId === value })[0].documents
      
    this._featureLayer.definitionExpression = this.createDefinitionExpression(`ForestryPlanId = ${value}`);  
    this.zoomToExtentFunc();
    const query = this._featureLayer.createQuery()
    query.returnGeometry = true;
      this._featureLayer.queryFeatures(query).then(response => {
          this.tableData = response.features.map(item => ({
              Department: item.attributes.Department,
              Area: item.attributes.Area,
              Age: item.attributes.Age,
              MaturityClass: item.attributes.MaturityClass,
              ManagementClass: item.attributes.ManagementClass,
              SiteIndex: item.attributes.SiteIndex,
              WoodVolume: item.attributes.WoodVolume,
              GrowingDepartmentStock: item.attributes.GrowingDepartmentStock,
              PineDistribution: item.attributes.PineDistribution,
              SpruceDistribution: item.attributes.SpruceDistribution,
              BroadLeavesDistribution: item.attributes.BroadLeavesDistribution,
              HardWoodDistribution: item.attributes.HardWoodDistribution,
              ForeignSpeciesDistribution: item.attributes.ForeignSpeciesDistribution,
              Stems: item.attributes.Stems,
              AverageTreeTrunk: item.attributes.AverageTreeTrunk,
              Description: item.attributes.Description,
              ActionType: item.attributes.ActionType,
              Urgency: item.attributes.Urgency,
              YearlyGrowth: item.attributes.YearlyGrowth,
              RelatedFeature: item
          }))
      })
  }

  createDefinitionExpression(subExpression: string): string {
    const baseExpression = `SupplierIds LIKE '%${this.personalNumber}%'`;

    return subExpression
     ? baseExpression + " AND (" + subExpression + ")"
     : baseExpression;
  }

  async initializeMap(): Promise<void> {
      // Load the modules for the ArcGIS API for JavaScript
      const [WebMap, MapView, esriId, ServerInfo, FullScreen, BaseMapToggle, SimpleFillSymbol] = await loadModules([
        'esri/WebMap',
        'esri/views/MapView',
        'esri/identity/IdentityManager',
        'esri/identity/ServerInfo',
        'esri/widgets/Fullscreen',
        'esri/widgets/BasemapToggle',
        'esri/symbols/SimpleFillSymbol'
      ], this.estateConfigForMap);


      this.zoomToExtentFunc = function () {
        const query = this._featureLayer.createQuery();
        query.outSpatialReference = this._view.spatialReference
        this._featureLayer.queryExtent(query).then(response => {
          this._view.goTo(response.extent ).catch(function (error) {
             console.error(error);
          });
        });
      }
      this.highLightPolygonFunc = function (graphic) {
        this._view.graphics.removeAll();
        if (graphic) {
          let symbol = new SimpleFillSymbol({
            color: [102, 204, 255, 0.5],
            style: "solid",
            outline: {
              color: [0, 102, 204],
              width: 1
            }
          });
          var selectionGraphic = graphic;
          selectionGraphic.symbol = symbol;
          this._view.graphics.add(selectionGraphic);
        }
      }

      this._webMap = new WebMap({
        portalItem: {
           id: '696e6f1ce4af48358d1b6ee3faf4278d',
        }
      });

      this._webMap.when((map) => {
        this._featureLayer = map.layers.getItemAt(0);
        this._featureLayer.definitionExpression = this.createDefinitionExpression('');
    
        this._featureLayer.when(() => {
            const query = this._featureLayer.createQuery()
            query.returnGeometry = true;
          return this._featureLayer.queryFeatures(query);
        }).then(response => {
           this.forestryPlanOptions = response.features
            .map(item => ({
              forestryPlanId: item.attributes.ForestryPlanId,
              forestryPlanName: item.attributes.ForestryPlanName,
                documents: item.attributes.DocumentLinks.split(',').map(document => ({
                  name: document.substr(document.lastIndexOf('/') + 1),
                  link: document
                }))
            }))
            .filter((obj, index, self) => {
              return self.findIndex(t => t.forestryPlanId === obj.forestryPlanId) === index
            })
            this.onForestryPlansSelectChange(this.forestryPlanOptions[0].forestryPlanId);
        });
      });


      const mapViewProperties: esri.MapViewProperties = {
        container: this.mapViewEl.nativeElement,
        map: this._webMap
      };

      const serverInfo = new ServerInfo();
      serverInfo.server = 'https://www.arcgis.com';
      serverInfo.tokenServiceUrl = 'https://www.arcgis.com/sharing/rest/generateToken';
      serverInfo.shortLivedTokenValidity = 7200; //this parameter should be investigated and adjusted
      esriId.registerServers([serverInfo]);

      const userInfo = {
        username: this.esryCredentials._userId,
        password: this.esryCredentials._password,
      };

      esriId.generateToken(serverInfo, userInfo).then((response) => {
        response.server = serverInfo.server;
        response.userId = this.esryCredentials._userId;
        esriId.registerToken(response);
      });

      this._view = new MapView(mapViewProperties);
      this._view.popup.autoOpenEnabled = false;

      this._view.when(() => {
        this._view.on("click", event => {
           this._view.hitTest(event.screenPoint).then(response => {
               let result = response.results[0];
               this.highLightPolygonFunc(response.results[0]?.graphic)
             if (!result?.graphic?.attributes?.OBJECTID) {
               this.selectedDepartment = '';
               return;
             }

             this._featureLayer.queryFeatures({
               outFields: ["*"],
               objectIds: result.graphic?.attributes?.OBJECTID
             }).then(response => {
               const department = response.features[0]?.attributes.Department
               this.selectedDepartment = department;
             });
          })
        });
      });

      let fullscreen = new FullScreen({
        view: this._view,
      });

      const toggle = new BaseMapToggle({
        view: this._view,
        nextBasemap: 'hybrid',
      });
      this._view.ui.add(fullscreen, 'top-left');
      this._view.ui.add("forestryPlansDiv", "top-right");
      this._view.ui.add(toggle, 'top-right');
      this._view.ui.add('estateDataTable', 'bottom-leading');

      await this._view.when();

      return;
  }

  get estateConfigForMap(): EstateConfig {
      return this.estateConfig;
  }

  get esryCredentials(): EsryCredentaials {
      const credentials = this.estateConfig.esryCredentials;
      return { ...credentials }
  }

  ngOnDestroy(): void {
      if (this._view) {
          // destroy the map view
          this._view.container = null;
      }
  }
}

<div #mapViewNode></div>
<div class="container">
    <div class="estate-table-container" id="tableDiv"></div>
</div>
<div class="esri-widget my-estate-widget" id="forestryPlansDiv">
    <h2>Åtgärder</h2>
    <select class="esri-select"
            (change)="onForestryPlansSelectChange($event.target.value)" id="forestryPlansSelect">
        <option [value]="option.forestryPlanId" *ngFor="let option of forestryPlanOptions">
            {{ option.forestryPlanName }}
        </option>
    </select>
    <ul>
        <li *ngFor="let doc of forestryPlanDocuments">
            
            <a href="{{doc.link}}" target="_blank"><i class="material-icons">file_present</i>{{doc.name}}</a>
        </li>
    </ul>
    <!--<button class="esri-button" id="zoom">Zoom to selected feature</button>-->
</div>

<app-estate-data-table
        [dataFields]="tableFields"  
        [tableData]="tableData" 
        [selectedDepartment]="selectedDepartment"
        (selectedDepartmentChanged)="onSelectedDepartmentChanged($event)">
</app-estate-data-table>
0 Kudos