פיתוח Widget בסביבת הפיתוח של ArcGIS Experience Builder
ה-ArcGIS Experience Builder מייצג את "הדור הבא" של מחוללי היישומים הגיאוגרפים של Esri. מחולל היישומים מאפשר למיישם להפוך במהירות את הנתונים המרחביים ליישומים ודפי אינטרנט מרשימים ללא צורך בפיתוח קוד. אבל איפה כל זה מותיר אותנו, המפתחים???
בגרסת המפתחים של ה-ArcGIS Experience Builder ניתן להוסיף ווידג'טים שאינן חלק מתוכנת המדף, ניתן ליצור נושאים חדשים ובכך לאפשר ליישם אפליקציות עשירות יותר הכוללות פקדים שנתפרו ספציפית לדרישות ושיטות העבודה בארגון שלכם.
מעבר לכך, ה-Builder עדיין לא כולל את כל הפקדים הכלולים ב- ArcGIS JavaScript API 4.X, בבלוג זה נראה איך ניתן לבנות ווידג'ט משלנו המאפשר עבודה עם פקדים שעדיין לא כלולים ב-builder
איך עושים את זה?
מתחילים בהתקנה מאד פשוטה של הסביבה:
- מורידים את ה-zip
- פורסים אותו על המחשב שלכם
- מתקינים את ה-server ב פקודת npm ci
- מתקינים את ה-client בפקודת npm ci
מפעילים את הסביבה
- הפעלת ה-server ב בפקודת npm start
- הפעלת ה-client בפקודת npm start
גלישה למחולל היישומים שלכם
https://<FQDN>:3001/
ואפשר להתחיל לפתח…
כמה מילים על הסביבה
JIMU היא ספריית javaScript שה-builder עובד איתה וכוללת מספר חבילות:
- jimu-core – אחראית לטעינת האפליקציה לפי ה-config , מנהלת את הגישה למאפיינים, מקורות המידע ועוד
- jimu-arcgis – אחראית לעבודה עם ה-ArcGIS JavaScript API
- jimu-ui – אחראית לחווית המשתמש
- jimu-layout – אחראית לעימוד
- jimu-for-builder- אחראית לפיתוח ה-setting page
ArcGIS JavaScript API 4.X – מאפשר גישה לכל יכולות ה-jsapi
אם הגעתם עד לכאן אני בטח לא צריכה להסביר לכם מה זה TypeScript, React, Redux או Webpack…
אז בואו נפתח את סביבת הפיתוח (אני ממליצה על VS Code) ונתחיל לפתח יחד את הווידג'ט הראשון שלנו…
כאמור, מטרת הווידג'ט שלנו היא עבודה עם פקדי jsapi שאינם כלולים עדיין ב-builder.
כל הפיתוח נעשה תחת ספריית your-extension
תחתיו ישנן 2 ספפריות:widgets ו-themes, רנחנו נעבוד תחת ספריית widgets
העתקת ספריית ה-simple לספריה חדשה
שינוי שם הספרייה ל-EXB
בקובץ ה-manifest.json
על מנת לבנות למיישם טופס בו יוכל לעדכן את מאפייני הווידג'ט עלינו לעושים settings לווידג'ט שלנו כך:
הוספת ספריית setting תחת ה-src
הוספת קובץ setting.tsx תחת ספריית setting
והקוד:
import {React, Immutable} from 'jimu-core';
import {AllWidgetSettingProps} from 'jimu-for-builder';
import {TextInput} from "jimu-ui";
import {IMConfig} from "../config";
import {JimuMapViewSelector} from 'jimu-ui/advanced/setting-components';
export default function (props: AllWidgetSettingProps<IMConfig>) {
const onExbPropertyChange = (evt: React.FormEvent<HTMLInputElement>) => {
props.onSettingChange({
id: props.id,
config: props.config.set("exampleConfigProperty",
evt.currentTarget.value)
});
};
const onMapSelected = (useMapWidgetIds: string[]) => {
props.onSettingChange({
id: props.id,
useMapWidgetIds: useMapWidgetIds
});
}
return (
<div>
Choose Map:
<div className="sample-use-map-view-setting p-2" >
<JimuMapViewSelector onSelect={onMapSelected}
useMapWidgetIds={props.useMapWidgetIds} />
</div>
<div className="sample-use-map-view-setting p-2">
exampleConfigProperty:
<TextInput
value={props.config.exampleConfigProperty}
onChange={onExbPropertyChange}
></TextInput>
</div>
</div>
);
}
דגשים:
IMConfig – מפנה להגדות שהוגדרו עבור הווידג'ט שלנו בקובץ ה-config.ts
import { ImmutableObject } from 'seamless-immutable';
export interface Config {
exampleConfigProperty: string;
}
export type IMConfig = ImmutableObject<Config>;
את הקישור למפה מעדכנים באמצעות JimuMapViewSelector
על מנת לאפשר בטופס ההגדרות לעדכן משתנה מסוג string השתמשנו ב- TextInput
לכל סוגי המאפיינים, מעדכנים את המאפיינים בעזרת הפעלת props.onSettingChange (props הוא מסוג AllWidgetSettingProps)
לאחר שקיבלנו את כל מאפייני הןןידג'ט שלנו אנחנו יכולים לעבור לכתיבה שלו…
הקוד:
/** @jsx jsx */
import { React, AllWidgetProps, jsx } from 'jimu-core';
import { IMConfig } from '../config';
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis';
import * as Legend from "esri/widgets/BasemapGallery";
import { useEffect } from 'react';
export default function Widget(props: AllWidgetProps<IMConfig>) {
let apiWidgetContainer: React.RefObject<HTMLDivElement>;
let legendWidget: Legend;
let mapView: __esri.MapView | __esri.SceneView;
apiWidgetContainer = React.createRef();
let onActiveViewChange = (jimuMapView: JimuMapView) => {
if (!(jimuMapView && jimuMapView.view)) {
return;
}
mapView = jimuMapView.view;
createAPIWidget();
}
let createAPIWidget = () => {
if (!mapView) {
return;
}
if (!legendWidget && apiWidgetContainer && apiWidgetContainer.current) {
legendWidget = new Legend({
view: mapView,
container: apiWidgetContainer.current
})
}
}
useEffect(() => {
createAPIWidget();
return function cleanup() {
if(legendWidget){
legendWidget.destroy();
legendWidget = null;
}
};
});
return (
<div className="widget-demo jimu-widget m-2">
<p>Simple Widget</p>
<h3>{props.config.exampleConfigProperty}</h3>
<JimuMapViewComponent
useMapWidgetIds={props.useMapWidgetIds}
onActiveViewChange={onActiveViewChange}></JimuMapViewComponent>
{(!props.useMapWidgetIds || props.useMapWidgetIds.length < 1) ?
"בחר מפה" :
<div ref={apiWidgetContainer}></div>}
</div>
);
}
דגשים:
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis';
import * as Legend from "esri/widgets/BasemapGallery";
הפניה ל-mapview מתבצעץ באמצעות המעטפת של JimuMapViewComponent המוסיפה את כל הנדרש לנו לקישור ולעבודה עםווידג'טים אך עדיין חושפת לנו את אותו mapView או sceneView שאנחנו מכירים מהעבודה עם ה-jsapi
מאחר וההדגמה נבנתה במקור לעבודה עם Legend כך קראנו ב-alias לפקד שלנו אך כל פקד של ה-jsapi שנפנה אליו (בקוד למעלה פונה ל- esri/widgets/BasemapGallery) יוכל לעבוד אם כל הנדרש להפעלתו זה הגדרת view ו-container (בהדגמה ראינו עבודה עם measure,legend ויש עוד).
useEffect – HOOK מאפשר בדומה ל componentDidMount and componentDidUpdate המוכרים מעבודה עם class לפעול לאחר טעינת הקומפוננטה.
React.createRef בשילוב עם הגדרת ref ל-div שחוזר מהפונקציה, מאפשר פניה ישירה למקום בקונטרול שלנו לצורך אכלוסו עם הפקד שלנו.
מאחר וקוד זה מדגים לנו רק כיצד להוסיף פקד של jsapi למפה, כצעד המשך אני ממליצה להוריד את הדוגמאות מאתר ה-github ולעבור באופן ספציפי על הווידג'ט get-map-coordinates-function.
בדוגמה זו ניתן לראות שילוב של עבודה עם events ו-watch על מאפיינים של פקד ה-view של ה-jsapi.
כמו כן, מדגים גם שימוש ב-useState HOOK.