May 1, 2025

How to Call Dataverse API from Power Pages Using safeAjax JavaScript

Introduction:

Power Pages offers a flexible way to expose Dataverse data to users, but calling the Dataverse Web API directly from JavaScript inside a Power Pages app requires some careful configuration. In this blog, we’ll walk through the required setup steps in Power Pages and demonstrate how to make secure GET and POST calls using a custom safeAjax wrapper.

Whether you're working with authenticated or anonymous users, this guide will help you get started with Dataverse API calls using jQuery.


Step-by-Step Configuration to Enable Dataverse API in Power Pages

Step 1: Configure Site Settings for the Target Table

Navigate to Power Pages Management > Site Settings and add the following entries for your Dataverse table. These settings enable API access, field-level control, and CORS.

  1. Enable Web API for the Table

    • Name: Webapi/{Table Logical Name}/enabled

    • Value: true

  2. Define Fields to Be Accessed

    • Name: Webapi/{Table Logical Name}/fields

    • Value: * (all fields)

  3. Allow Cross-Origin Requests

    • Name: Webapi/{Table Logical Name}/AllowedOrigins

    • Value: * (or specify your domain)

  4. (Optional) Enable Anonymous Access

    • Name: Webapi/{Table Logical Name}/AllowAnonymousAccess

    • Value: true

    • Only required if your Power Pages site supports anonymous users

Make sure to select the correct Website and set Source as Table in all entries.


Step 2: Set Up Table Permissions

Next, go to the Security > Table Permissions section in Power Pages Management.

  • Create a new permission for your table.

  • Assign appropriate permissions (Read, Create, Update, etc.) based on your use case.

  • Link this permission to the appropriate Web Roles (Authenticated or Anonymous Users).

Without table permissions, the API call will fail even if site settings are correct.



safeAjax JavaScript Wrapper for Dataverse API Calls

Once your configuration is complete, use the following script to wrap your AJAX calls securely. This script ensures tokens are included for request validation and handles common error scenarios gracefully.

$(function () {
    // Web API ajax wrapper
    (function (webapi, $) {
        function safeAjax(ajaxOptions) {
            var deferredAjax = $.Deferred();
            shell.getTokenDeferred().done(function (token) {
                // Add headers for ajax
                if (!ajaxOptions.headers) {
                    $.extend(ajaxOptions, {
                        headers: {
                            "__RequestVerificationToken": token
                        }
                    });
                } else {
                    ajaxOptions.headers["__RequestVerificationToken"] = token;
                }
                $.ajax(ajaxOptions)
                    .done(function (data, textStatus, jqXHR) {
                        validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
                    }).fail(deferredAjax.reject); // ajax fail
            }).fail(function () {
                deferredAjax.rejectWith(this, arguments); // On token failure
            });
            return deferredAjax.promise();
        }
        webapi.safeAjax = safeAjax;
    })(window.webapi = window.webapi || {}, jQuery)
});

function appAjax(ajaxOptions) {
    return webapi.safeAjax(ajaxOptions).done(function (res) {
        if (res.value.length > 0) {
            const result = res.value[0];
            alert("Successfully logged in!");
        } else {
            alert("Incorrect ID or Password.");
        }
    })
    .fail(function (response) {
        if (response.responseJSON) {
            alert("Error: " + response.responseJSON.error.message)
        } else {
            alert("Error: Web API is not available... ")
        }
    });
}

Usage Examples

GET Request

Call this when you want to retrieve data from your Dataverse table.

appAjax({
    type: "GET",
    url: "/_api/crb50_tablename?$select=*&$filter=crb50_customerid eq '001' and crb50_password eq '001'",
    contentType: "application/json"
});


POST Request

Use this to insert a new record into Dataverse.

var recordObj = {
    "crb50_name": "Name1"
};

appAjax({
    type: "POST",
    url: "/_api/crb50_TableName",
    contentType: "application/json",
    data: JSON.stringify(recordObj),
    success: function (res, status, xhr) {
        recordObj.id = xhr.getResponseHeader("entityid");
        table.addRecord(recordObj);
    }
});


Conclusion

Integrating Power Pages with Dataverse Web API opens up a wide range of possibilities, from custom login experiences to fully dynamic data interactions. With a few key configurations in site settings and table permissions, and a secure AJAX wrapper like safeAjax, you can harness the full power of Dataverse from the front end of your Power Pages apps.

If you have any questions you can reach out our SharePoint Consulting team here.

Fixing SPFx Build Error: “UglifyJs Unexpected Token” When Running gulp bundle --ship

Introduction:

While packaging your SharePoint Framework (SPFx) solution using the production command: 

gulp bundle --ship

You may encounter a frustrating error message like: 

Unexpected token: name (corefeature) - SPFx while build solution

This usually indicates that UglifyJS, the default minifier in SPFx, stumbled upon ES6+ syntax (e.g., class, let, const) that it does not understand. 

In this post, I will guide you through a clean and effective workaround using terser-webpack-plugin, a modern minifier that fully supports ES6+. 

Why This Error Occurs:

  • Root Cause: UglifyJS does not support modern JavaScript (ES6+). 
  • Impact: Webpack fails during the minification process, stopping the bundle process for production. 
  • Trigger: Usage of ES6+ syntax like class, const, etc., in your SPFx web part code.  

Solution: Swap UglifyJS with Terser:

To resolve this, we will: 

  1. Add Terser and Webpack merge dependencies. 
  2. Update gulpfile.js to override the default SPFx Webpack configuration. 
  3. Clean and rebuild your project. 

Step-by-Step Fix:

Step 1: Install Compatible Dependencies:

Update your package.json to include: 

"terser-webpack-plugin-legacy": "1.2.3",
"webpack-merge": "4.2.1"

Then run the following commands in your terminal: 

npm install terser-webpack-plugin --save-dev
npm install terser-webpack-plugin-legacy --save-dev
npm install webpack-merge@4.2.1 --save-dev

Optional (if Babel is needed for ES6+ transpilation): 

npm install @babel/core @babel/preset-env babel-loader --save-dev

Step 2: Update gulpfile.js:

Modify your gulpfile.js as shown below: 

'use strict';
 
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
const merge = require('webpack-merge');
const TerserPlugin = require('terser-webpack-plugin-legacy');
 
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase
  and will not be type-safe.`);

build.initialize(gulp);
 
build.configureWebpack.setConfig({
 additionalConfiguration: function (config) {
   config.plugins = config.plugins.filter(plugin => !(plugin.options && plugin.options.mangle));
 
   return merge(config, {
     optimization: {
       minimize: true,
       minimizer: [new TerserPlugin()]
     }
   });
 }
});

This code replaces UglifyJS with Terser during the bundle phase. 

Step 3: Clean & Rebuild the Project: 

Run the following commands in sequence: 

gulp clean
gulp build --ship
gulp bundle --ship

Your project should now build successfully, free of any “unexpected token” errors from UglifyJS. 

Optional: Babel Setup (Only If Needed): 

If your project uses newer JavaScript features not supported in your target environments, consider setting up Babel. However, for the UglifyJS error alone, swapping in Terser is typically enough. 

Conclusion: 

  • If you are getting the "UglifyJs Unexpected Token" error while bundling your SPFx project, it is because the default minifier does not support modern JavaScript. By switching to it terser-webpack-plugin, you can fix the issue and bundle your project without errors. Just follow the steps to update your packages and gulpfile, and you will be good to go!
  • If you run into any issues while implementing this solution, feel free to drop a comment. I will be happy to help. 

If you have any questions you can reach out our SharePoint Consulting team here.

How to Add a Custom Command Bar Button to Create a New Row in a Model-Driven App

How to Add a Custom Command bar Button to Create a New Row in a Model-Driven App

Introduction

Model-driven apps in Power Platform offer a powerful way to create data-centric business apps with minimal code. In this post, we’ll walk through the steps to create a custom command bar button on the Main Grid view that allows users to add a new row to a table (entity).

This guide assumes you have some basic familiarity with the Power Apps Maker Portal and Dataverse.


Step 1: Create Your Model-Driven App

  1. Go to Power Apps.
  2. Click Apps from the left navigation pane.
  3. Click + New app > Model-driven app.
  4. Give your app a name (e.g., Data Form) and click Create.
  5. Use the app designer to add a table (e.g., Data) to your app.
  6. Save and publish the app.

Step 2: Add a Custom Command bar Button

We will now add a custom button to the command bar of the Main Grid for the table.

2.1 Navigate to the App

  1. In the Maker Portal, go to View.
  2. Select your view (e.g., DataForms view).
  3. Click the three dots (⋮) beside the view.
  4. Click Edit under the Edit command bar options.














2.2 Edit the Main Grid Command Bar

  1. In the command designer, choose Main grid.


















  2. Click +New and select Command.



















2.3 Configure the Custom Button

  1. Label: Add New Row
  2. Icon: Choose an appropriate icon or upload a custom one.
  3. Action: Choose JavaScript or Modern Action (recommended).














Step 3: Add the Button Logic (Use JavaScript)

  1. Create a JavaScript web resource.
  2. Add the following function:
  3. Click Add Library button to open new side panel
  4. Click New web resource



























Below is the JavaScript Code.

    
function AddNewRowToMainGrid(primaryControl) {
	try {
		// Check if primaryControl is defined (for main grid button)
        if (!primaryControl || !primaryControl.data || !primaryControl.data.entity) {
            console.log("No record selected. This is a new record creation.");
        }

        // Define the table name (logical name of your table)
        var tableName = "new_dataform"; // Make sure this is correct!

        // Prepare default values for the new row
        var defaultData = {
        "new_dateoftillcheck": null,
        "new_tillid": null,
		"new_nameofcurrenttillholder": "",
        "new_discrepancydeficiency": "",
        "new_discrepancysurplus": "",
        "new_actiontakeen": "",
        "new_postplusadjustmentdate": null,
        "new_signatureoftillholdercheckingofficer": "",
        };

        //console.log("Creating record in table: " + tableName);
        //console.log("Data: ", JSON.stringify(defaultData));

        // Create a new record in Dataverse
        Xrm.WebApi.createRecord(tableName, defaultData).then(
		function success(result) {
		  //console.log("Record created successfully with ID: " + result.id);

		  // Refresh the main grid after adding the record
		  if (primaryControl && primaryControl.refresh) {
			primaryControl.refresh();
		  } 
		  else {
		console.warn("primaryControl not available. Trying page refresh.");
		Xrm.Page.ui.refresh(); // Fallback
		}
		  },
		  function(error) {
		  	console.error("Error creating record: ", error.message);
			Xrm.Navigation.openErrorDialog({
			message: "Error adding new record: " + error.message
		  });
		}
	);
} 
catch (e) {
	console.error("Unexpected error:", e);
	Xrm.Navigation.openErrorDialog({
	message: "Unexpected error occurred: " + e.message
	});
  }
}

Step 4: Test the Custom Button

  1. Open your model-driven app.
  2. Navigate to the DataForm Main Grid.
  3. Click on your custom Add New Row button.
  4. A new row added, allowing the user to add a new record.










Conclusion

Customizing command bars in model-driven apps can significantly enhance user experience and streamline operations. Whether using Power Fx for low-code scenarios or JavaScript for more complex logic, adding a "New Row" button makes it easier for your users to create data on the fly.

April 25, 2025

Retrieving Viva Engage (Yammer) Posts in SPFx – A Complete Guide

Overview

With Microsoft Viva Engage (formerly Yammer) becoming a central hub for company-wide communication and community engagement, integrating its content directly into SharePoint through SPFx (SharePoint Framework) can provide a seamless user experience.

While Viva Engage content can be embedded using the out-of-the-box (OOTB) web part, it may not provide the flexibility and customization required for more complex or tailored use cases. That’s where SPFx steps in. With custom development, you can fetch and display posts from specific communities, apply filters, or even perform additional business logic as needed.

In this blog post, we will walk you through how to retrieve Viva Engage posts using SPFx and Microsoft Graph API. This is especially useful when the OOTB web part doesn't offer sufficient customization for your needs.

Prerequisites

Before diving into code, make sure you have the following ready:

  • SPFx development environment set up
  • Access to Microsoft 365 admin portal (to grant API permissions)
  • Viva Engage license and active community
  • Basic knowledge of React (optional, but helpful)

Step 1: Register Your App in Azure AD

To access Viva Engage data, you need permissions via Microsoft Graph API.

  1. Go to Azure Portal
  2. Navigate to Azure Active Directory > App registrations
  3. Click New registration
  4. Name your app (e.g., SPFxVivaEngageIntegration)
  5. Add redirect URI: https://localhost:5432
  6. Once registered, go to API permissions > Add permission
  7. Choose APIs my organization uses and search for Yammer
  8. Select Yammer and then Delegated permissions
  9. Add: user_impersonation
  10. Click Grant admin consent

Step 2: Get Access Token

Here’s how to get a token for Yammer (Viva Engage) using AAD token provider in SPFx:

 private async getViVaEngageToken(): Promise<void> {  
  try {  
   const tokenProvider = await this.props.spcontext.aadTokenProviderFactory.getTokenProvider();  
   const token = await tokenProvider.getToken("https://api.yammer.com");  
   this.setState(  
    { vivaEngageToken: token },  
    this.getAllPostsfromGroups  
   );  
  } catch (error) {  
   console.error("Error getting token: ", error);  
  }  
 }  

Step 3: Retrieve Group ID Using Community Name

This method fetches all groups and finds the group ID based on the community name.

 private async getGroupIdByName(communityName: string): Promise&lt;{ topLevelMessages: any[] }&gt; {  
  try {  
   const response = await this.props.spcontext.httpClient.get(  
    `https://api.yammer.com/api/v1/groups.json`,  
    HttpClient.configurations.v1,  
    {  
     headers: {  
      Authorization: `Bearer ${this.state.vivaEngageToken}`,  
      "Content-type": "application/json",  
     },  
    }  
   );  
   const data = await response.json();  
   const group = data.find((g: any) =&gt; g.full_name === communityName);  
   if (group) {  
    return await this.getPosts(group.id);  
   } else {  
    console.warn("Community not found.");  
    return { topLevelMessages: [] };  
   }  
  } catch (error) {  
   console.error("Error fetching group ID: ", error);  
   return { topLevelMessages: [] };  
  }  
 }  

Step 4: Retrieve Posts from Viva Engage

This method calls the messages API and returns only the top-level posts (ignores replies).

 private async getPosts(communityId: string): Promise&lt;{ topLevelMessages: any[] }&gt; {  
  try {  
   const apiUrl = `https://api.yammer.com/api/v1/messages/in_group/${communityId}.json?threaded=true`;  
   const response = await this.props.spcontext.httpClient.get(  
    apiUrl,  
    HttpClient.configurations.v1,  
    {  
     headers: {  
      Authorization: `Bearer ${this.state.vivaEngageToken}`,  
      "Content-type": "application/json",  
     },  
    }  
   );  
   const data = await response.json();  
   const messages = data?.messages || [];  
   const topLevelMessages = messages.filter((msg: any) =&gt; !msg.replied_to_id);  
   return { topLevelMessages };  
  } catch (error) {  
   console.error("Error fetching posts: ", error);  
   return { topLevelMessages: [] };  
  }  
 }  

Conclusion

By following the steps above, you can build a custom SPFx web part to pull Viva Engage posts using a community name and show them dynamically in your SharePoint site. This allows for a much more flexible and personalized experience beyond the OOTB web part.

You now have a streamlined way to:

  • Authenticate with Azure AD
  • Retrieve a Viva Engage community’s group ID using its name
  • Fetch and return only the main posts (excluding replies)

This approach is perfect if you're building SPFx web parts or extensions to display real-time conversations and announcements from Viva Engage.

If you have any questions you can reach out our SharePoint Consulting team here.

Transform Your SharePoint Edit Forms into Structured, User-Friendly Interfaces – No Code Needed!

Why Customize SharePoint Edit Form? 

SharePoint’s default edit form is functional but often lacks visual appeal and logical grouping, especially for lists with many fields. JSON form formatting empowers you to: 

  • Improve usability by organizing fields into collapsible sections. 
  • Enhance aesthetics with icons, headers, and consistent spacing. 
  • Add functionality like help links or dynamic buttons. 

In this guide, you will learn to customize the header, body and footer of the edit form popup using JSON let’s dive in! 

Step 1: Enable JSON Customization for Your List: 

  1. Navigate to your SharePoint list where you want to customize the edit form. 
  2. Click on the “Add new item” button. 
  3. In the form, click on the “Edit Form” option (top-right corner). 
  4. Easily customize your SharePoint list using JSON with a no-code, low-code approach to enhance layout and design.
  5. Select “Configure Layout” from the dropdown. 
  6. A panel will appear with three sections: Header, Body, Footer. 
  7. Choose the section you want to customize and paste your JSON code into the editor. 

Step 2: Structure of JSON form formatting: 

SharePoint Forms are divided into three sections: 

  1. Header: Modify the form header to display a title, icon or additional details 
  2. Body: Organize fields into sections for better readability 
  3. Footer: Customize the footer with buttons and actions. 

Step 3: Customizing the Header: 

The header appears at the top of the form. We can add: 

The header appears at the top of the form, and we can customize it to enhance the appearance by adding an icon, title, or even a custom logo. 

Adding an Icon in the header: 

{
  "elmType": "div",
  "attributes": {
    "class": "ms-borderColor-neutralTertiary"
  },
  "style": {
    "width": "100%",
    "border-top-width": "0px",
    "border-bottom-width": "1px",
    "border-left-width": "0px",
    "border-right-width": "0px",
    "border-style": "solid",
    "margin-bottom": "16px"
  },
  "children": [
    {
      "elmType": "div",
      "style": {
        "display": "flex",
        "box-sizing": "border-box",
        "align-items": "center"
      },
      "children": [
        {
          "elmType": "div",
          "attributes": {
            "iconName": "Onboarding",
            "class": "ms-fontSize-42 ms-fontWeight-regular ms-fontColor-themePrimary",
            "title": "Details"
          },
          "style": {
            "flex": "none",
            "padding": "0px",
            "padding-left": "0px",
            "height": "36px"
          }
        }
      ]
    },
    {
      "elmType": "div",
      "attributes": {
        "class": "ms-fontColor-neutralSecondary ms-fontWeight-bold ms-fontSize-24"
      },
      "style": {
        "box-sizing": "border-box",
        "width": "100%",
        "text-align": "left",
        "padding": "21px 12px",
        "overflow": "hidden"
      },
      "children": [
        {
          "elmType": "div",
          "txtContent": "= 'Employee Information of ' + [$Title]"
        }
      ]
    }
  ]
}

Where to make changes for popup form Appearance:

Change Required 

Modify This Part in JSON 

Change Icon 

"iconName": "Onboarding" (Replace with another Fluent UI icon) 

Change Icon Size 

“ms-fontsize-42" (increase/decrease number) 

Change Icon Color 

“ms-fontColor-themePrimary" (change theme color) 

Change Header Background Color 

Add “background-color” : “#F32F1” inside style 

Change Title Text 

Modify “txtContent”: “= 'Employee Information of  ' + [$Title]" 

Change Title Font Size 

“ms-fontSize-24" (increase/decrease number) 

Change Title Alignment 

“text-align”: “left” ->”center” or “right” 

Adjust Header Height & Spacing 

Modify “height”, “margin-bottom”, “padding” 

Make your SharePoint Online list header more attractive by adding icons and matching the design to the list name.

How to select an Icon for the Header: 

You can select any Fluent UI icon for your header. Follow these steps to get the correct icon name:  

  1. Go to https://uifabricicons.azurewebsites.net/ . 
  2. Search for the icon you want. 
  3. Right-click the icon and select “Copy Friendly Name”. 
  4. Paste the copied name into the “iconName” field in the JSON above. 

This will display the selected icon in your SharePoint form header. 

Adding a Custom logo in the header: 

If you want to display a custom logo instead of an icon, modify the JSON as follow: 

{
  "elmType": "img",
  "attributes": {
    "src": "https://yourtenant.sharepoint.com/sites/YourSite/Shared%20Documents
    /Logo.jpg",
    "title": "Company Logo",
    "class": "ms-borderRadius-circle"
  },
  "style": {
    "width": "200px",
    "height": "100px",
    "margin-right": "10px"
  }
}
Customize SharePoint Online list headers by adding an image or logo to create a more branded and visually appealing layout

Explanation of Changes: 

  • ElmType changed from “div” to “img” - this tells SharePoint to display an image instead of an icon. 
  • Src attribute- replace the placeholder URL with the actual URL of your logo stored in a SharePoint document library. 
  • Style changes- Adjust the width, height and margin as needed. 
  • Note:- Ensure that the image URL is publicly accessible or that SharePoint permissions allow users to view the image. 

Step 4: Customizing the Body into Sections:

{
  "sections": [
    {
      "displayname": "Personal Details",
      "fields": [
        "Title",
        "Address",
        "Email Address",
        "Date of Birth",
        "Contact Number",
        "Blood Group"
      ]
    },
    {
      "displayname": "Company Details",
      "fields": [
        "Previous Company Name",
        "Experience of Previous Company",
        "Total Number of Experience",
        "Designation in Previous Company",
        "Income of Previous Company",
        "Expected Income"
      ]
    }
  ]
}

Where to make changes to affect the popup form appearance: 

Change Required 

Modify This Part in JSON 

Change Section Heading 

Modify “displayname” : “Personal Deatils” or “displayname”: “Company Details” 

Add a New Section 

Copy a section and create a new “displayname” with relevant “fields” 

Add a Field to an Existing Section 

Add the field name to the “fields” array 

Remove a Field 

Delete the field name from the “fields” array 

Rearrange Fields in a Section 

Change the order of fields in the “fields” array 

Use JSON formatting to change how SharePoint list items look, so they fit your design and needs.

Step 5: Customizing the Footer with Action Buttons: 

{
  "elmType": "div",
  "style": {
    "width": "100%",
    "text-align": "left",
    "overflow": "hidden",
    "border-top-width": "1px"
  },
  "children": [
    {
      "elmType": "div",
      "style": {
        "width": "100%",
        "padding-top": "10px",
        "height": "24px"
      },
      "children": [
        {
          "elmType": "a",
          "txtContent": "='Contact Details for ' + [$Title]",
          "attributes": {
            "target": "_blank",
            "href": "https://www.google.com/",
            "class": "ms-fontColor-themePrimary
            ms-borderColor-themePrimary
            ms-fontWeight-semibold
            ms-fontSize-m
            ms-fontColor-neutralSecondary–hover
            ms-bgColor-themeLight–hover"
          }
        }
      ]
    }
  ]
}

Where to make changes for popup form Appearance:

Change Required 

Modification This Part in JSON

Change the Link URL 

Replace “href”: https://www.google.com/ with your desired link(e.g. [$ProfileURL] for dynamic links 

Open in Same Tab 

Change “target”: “_blank” to “target”: “_self”. 

Change Link Text 

Modify “txtContent”: “=’Contact Details for ‘ + [$Title]” 

Change Text Color 

Modify “class” and replace ms-fontColor-themePrimary with ms-fontColor-red. 

Make it a Button instead of a link 

Change “elmType” : “a” to “elmtype” : “button”  

Enhance your SharePoint Online site with a custom, visually appealing footer section that improves user experience and boosts branding consistency.

Step 6:  Testing and Debugging: 

  1. Copy the complete JSON code (header, body, and footer). 
  2. Paste it into the JSON customization panel in SharePoint. 
  3. Click Save and refresh your list. 
  4. Edit an item to see the customized popup form in action! 

Tips for Advanced Customization: 

  1. Conditional Formatting: Hide/show sections based on field values using “visible”: “=[$status]== ‘Active’” 
  2. Theming: Match your organization’s colors with “style”: { “background”: “#f0f0f0”} 
  3. Integrate Power Automate: Add buttons that trigger workflows using “actionType”: “executeFlow”. 

Conclusion:     

  • Customizing forms with JSON in SharePoint makes it much easier to create clean and user-friendly edit forms. You can organize fields better, add helpful icons, and customize buttons to make the form look good and work smoothly. This helps users save time and makes using the form more enjoyable.

If you have any questions you can reach out our SharePoint Consulting team here.