Im basically trying to merge these two samples:
Login: https://developers.arcgis.com/javascript/latest/sample-code/identity-oauth-basic/live/
Related Tables: https://developers.arcgis.com/javascript/latest/sample-code/widgets-featuretable-relates/
But I'm stuck with the await (line 187 & 193). If I follow the related table example and put async function at top, then the sign in part stops working.
I'm just not sure where to start and stop the async function.
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
<title>Access ArcGIS Online items using OAuth 2.0 | Sample | ArcGIS Maps SDK for JavaScript 4.30</title>
<script type="module" src="https://js.arcgis.com/calcite-components/2.11.1/calcite.esm.js"></script>
<link rel="stylesheet" type="text/css" href="https://js.arcgis.com/calcite-components/2.11.1/calcite.css" />
<link rel="stylesheet" href="https://js.arcgis.com/4.30/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.30/"></script>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
display: flex;
}
#viewDiv {
height: calc(100% - 65px);
width: 50%;
margin-top:65px;
}
.container {
height: calc(100% - 65px);
width: 50%;
margin-top:65px;
}
calcite-modal {
--calcite-modal-scrim-background: rgb(34, 77, 122, 0.7);
}
#modal hr {
margin: 1rem 0;
border: 0;
border-bottom: 1px solid var(--calcite-color-border-3);
}
#modal ul li {
margin-bottom: 0.5rem;
}
#modal calcite-notice {
margin-bottom: 1.25rem;
}
</style>
<script>
require([
"esri/portal/Portal",
"esri/identity/OAuthInfo",
"esri/identity/IdentityManager",
"esri/portal/PortalQueryParams",
"esri/Map",
"esri/views/MapView",
"esri/WebMap",
"esri/widgets/FeatureTable",
"esri/core/reactiveUtils",
"esri/config"
], (Portal, OAuthInfo, esriId, PortalQueryParams, Map, MapView, WebMap, FeatureTable, reactiveUtils, esriConfig) => {
console.log("startup")
// UI elements
const signInButton = document.getElementById("sign-in-button");
const navLogo = document.getElementById("nav-logo");
const navigationUser = document.getElementById("nav-user");
const galleryPanel = document.getElementById("item-panel");
const itemGallery = document.getElementById("item-gallery");
signInButton.addEventListener("click", signInOrOut);
navigationUser.addEventListener("click", signInOrOut);
esriConfig.portalUrl = "https://webgis.cushwake.com/portal";
//Create a new OAuthInfo object.
const info = new OAuthInfo({
// Swap this ID out with an app ID registered in your ArcGIS Organization.
appId: "9jjg9al9Mm3gFiLy",
// Add the portalUrl property if using your own portal.
portalUrl: esriConfig.portalUrl,
// Set the authNamespace property to prevent the user's signed in state
// from being shared with other apps on the same domain with the same authNamespace value.
authNamespace: "portal_oauth_inline",
// Set popup to true to show the OAuth sign-in page in a separate popup window.
popup: false
});
// Add the OAuthInfo to the IdentityManager.
esriId.registerOAuthInfos([info]);
// Call the checkSignIn function to see if the user is already signed in.
checkSignIn();
// Function to check the current sign in status and query the portal if signed in.
function checkSignIn() {
esriId
.checkSignInStatus(info.portalUrl + "/sharing")
.then(() => {
// If signed in, show the username in the UI.
navigationUser.hidden = false;
signInButton.hidden = true;
const portal = new Portal({
authMode: "immediate"
});
// Check if using a portal other than ArcGIS Online.
if (info.portalUrl !== "https://webgis.cushwake.com/portal") {
portal.url = info.portalUrl;
}
// Load the portal, display the name and username, then call the query items function.
portal.load().then(() => {
navigationUser.fullName = portal.user.fullName;
navigationUser.username = portal.user.username;
navLogo.description =
"Gallery of queried portal items displayed below. To sign out, click on the logged in user button.";
//queryItems(portal);
});
})
.catch(() => {
// If not signed in, then show the sign in button.
signInButton.hidden = false;
navigationUser.hidden = true;
navLogo.description = "Use OAuth to log in to an ArcGIS Organization to view your items.";
});
}
// Function to sign in or out of the portal used by the sign in/out button click event.
function signInOrOut() {
esriId
.checkSignInStatus(info.portalUrl + "/sharing")
.then(() => {
// If already signed in, then destroy the credentials to sign out.
esriId.destroyCredentials();
window.location.reload();
})
.catch(() => {
// If the user is not signed in, generate a new credential.
esriId
.getCredential(info.portalUrl + "/sharing", {
// Set the following property to false to not show a dialog
// before the OAuth popup window is open.
//oAuthPopupConfirmation: false,
})
.then(() => {
// Once a credential is returned from the promise, check the
// sign in status to query the portal for items.
checkSignIn();
});
});
}
const infoButton = document.createElement("calcite-action");
infoButton.text = "Sample information";
infoButton.icon = "information";
infoButton.label = "Info";
const modalDiv = document.getElementById("modal");
// Add event listener to the button
infoButton.addEventListener("click", () => {
// Open the modal
modalDiv.open = true;
});
let features = [];
const webmap = new WebMap({
portalItem: {
id: "4c658233bb5c4dfe9aacf272a195ce75"
}
});
const view = new MapView({
container: "viewDiv",
map: webmap,
popupEnabled: false
});
const calciteLoader = document.getElementById("calcite-loader");
// await reactiveUtils.whenOnce(() => !view.updating);
// Once the view is finished updating, get the first layer in the webmap
const featureLayer = webmap.layers.getItemAt(0);
// Make sure the layer is loaded before constructing the FeatureTable
// await reactiveUtils.whenOnce(() => featureLayer.loaded);
//do what you need here
// Create the feature table
// The title is created as a function that dynamically updates based on the amount of selected features within the table.
// The actionColumnConfig property creates a new action column within the table that creates an action that zooms to the feature in that row.
const featureTable = new FeatureTable({
title: () => {
if (!featureTable) {
return;
}
const state = featureTable.state;
switch (state) {
case "loading":
return "Loading layer data...";
case "loaded":
const title = featureTable.layer?.title;
const rowCount = featureTable.size;
const selectedCount = featureTable.highlightIds?.length ?? 0;
return `${title} (${selectedCount} rows selected out of ${rowCount} total)`;
case "error":
return "Error loading layer.";
default:
return;
}
},
description: "Table Description",
actionColumnConfig: {
label: "Go to feature",
icon: "zoom-to-object",
callback: (params) => {
view.goTo(params.feature);
}
},
view: view,
editingEnabled: true, // required to enable editing
relatedRecordsEnabled: true, // required to enable related records
layer: featureLayer,
tableTemplate: {
// TableTemplate is autocastable
columnTemplates: [
{
type: "field", // FieldColumnTemplate is autocastable, 'type' must be set
fieldName: "property_id",
label: "MNCAR",
icon: "key",
autoWidth: true // set this to have the columns automatically adjust width
},
{
type: "field",
fieldName: "MANUFACTURER",
label: "Manufacturer",
autoWidth: true
},
{
type: "field",
fieldName: "OPERABLE",
label: "Operable",
autoWidth: true
},
{
type: "field",
fieldName: "LASTSERVICE",
label: "Date of last service",
autoWidth: true
},
{
type: "field",
fieldName: "FLOW",
label: "Flow rate",
autoWidth: true
}
]
},
container: document.getElementById("tableDiv")
});
reactiveUtils.when(
() => view.stationary,
() => {
// Filter out and show only the visible features in the feature table.
featureTable.filterGeometry = view.extent;
},
{
initial: true
}
);
// Listen for the view's click event.
// Perform a hitTest on the clicked location
// grab any features that fall within it
view.on("click", async (event) => {
const response = await view.hitTest(event);
const candidate = response.results.find(
(result) => result.graphic && result.graphic.layer === featureLayer
);
if (candidate) {
const objectId = candidate.graphic.attributes.OBJECTID;
const index = featureTable.highlightIds.indexOf(objectId);
// If there are objectIds in the highlightIds, remove the clicked feature
// from the array. If there are no more objectIds, remove the filter
// to show only selected records
if (index > -1) {
featureTable.highlightIds.splice(index, 1);
if (featureTable.highlightIds.length === 0) {
featureTable.filterBySelectionEnabled = false;
}
} else {
// Add the objectId of the clicked feature into the highlightIds.
// This selects the feature in the table and sets a filter to only display
// the selected rows
featureTable.highlightIds.push(objectId);
featureTable.filterBySelectionEnabled = true;
}
}
});
// Check if the highlights (selection) are being changed on the table by
// checking and unchecking the rows,
// and update the features array to match the table selection by adding
// or removing as appropriate
featureTable.highlightIds.on("change", async (event) => {
event.removed.forEach((item) => {
const data = features.find((data) => {
return data === item;
});
if (data) {
features.splice(features.indexOf(data), 1);
}
// Check if there are no more selected rows in the table,
// Once everything is unchecked, remove the filter for selected records
if (featureTable.highlightIds.length === 0) {
featureTable.filterBySelectionEnabled = false;
}
});
// If the selection is added, push all added selections to array
event.added.forEach((item) => {
features.push(item);
});
});
});
</script>
</head>
<body>
<calcite-shell>
<calcite-navigation slot="header">
<calcite-navigation-logo
id="nav-logo"
slot="logo"
heading="Cushman Wakefield"
description="Something Else"
></calcite-navigation-logo>
<calcite-button id="sign-in-button" slot="user">Sign in</calcite-button>
<calcite-navigation-user hidden id="nav-user" slot="user"> </calcite-navigation-user>
</calcite-navigation>
</calcite-shell>
<calcite-modal id="modal">
<div slot="header">About this application</div>
<div slot="content">
<calcite-notice open>
<span slot="title"
>This sample demonstrates how to display and edit related data using the
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html"
>FeatureTable</calcite-link
>
widget.
</span>
</calcite-notice>
This example illustrates the main areas of functionality:
<ul>
<li>
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html#related-records"
>Working with related records in the table</calcite-link
>
</li>
<li>
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html#selection"
>Syncing selection between table and view</calcite-link
>
</li>
<li>
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html#editing-in-featuretable"
>Editing in the table</calcite-link
>
</li>
<li>
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html#filtering-and-sorting"
>Filtering in the table</calcite-link
>
</li>
<li>
<calcite-link
target="_blank"
href="https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-FeatureTable.html#column-configuration"
>Configuring columns</calcite-link
>
</li>
</ul>
</div>
</calcite-modal>
<div id="viewDiv"></div>
<div class="container">
<div id="tableDiv"></div>
</div>
</body>
</html>
When I use the OAuth login, I have to wrap the code that requires the authenticated data in the checkSignInStatus method.
const portalUrl = 'my portal url';
const info = new OAuthInfo({
appId: "myAppId", //*** Your Client ID value goes here ***//
popup: false // inline redirects don't require any additional app configuration
});
identityManager.registerOAuthInfos([info]);
identityManager.getCredential(portalUrl);
identityManager.checkSignInStatus(portalUrl).then(function () {
layer = new FeatureLayer({
portalItem: {
id: "myPortalItem"
},
outFields: ["*"]
});
map = new Map({
basemap: 'oceans',
layers: [layer]
});
view = new MapView({
container: "viewDiv",
map: map,
center: [-153, 63],
zoom: 4,
popup: {
autoOpenEnabled: false
}
});
//etc.
});
Normally it would be good practice not to mix async..await and Promise when writing in JS. Maybe you can change the whole function to Promise.