Skip to content

Tips and tricks

Shortcut List

Feedback

M365 Docs

Start SPFx Training

Start SPFx React Development

Start Teams Development

Reference Samples

M365 Full Stack / SPFx

Enterprise Reference Samples

PnPJS-V3

Fluent UI

Learning SPFx Videos

DEV-Tenant

Development Roadmap

Code Guideline

Performance Guideline

Design Guideline

Code Review Checklist

Security Guideline

Granting Permission

m365 spo login https://<tenant>-admin.sharepoint.com
spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Mail.Read'

Content Style Guide

Coding Patterns

SPFx Workflow

SharePoint PNP Community

Create Project (pnpm)

with pnpm manager

yo @microsoft/sharepoint package-manager pnpm
pnpm i tslint@5.9.1 -DE
pnpm i typescript@2.4.2 -DE

Check Package Version

node v
npm v
yo --generators
gulp v
tsc v
tsd --version

Git Basics

Init local repo

git init

Add all Files to local repo

git add -A
git commit -m "init repo"

Show last commit

git show --name-only

show full history

git log --full-history --src/path/to/file.js

Development(local workbench)

Importend: only for spfx < 13.1

gulp serve
https://localhost:4321/temp/workbench.html

Development(online workbench)

gulp serve --nobrowser
/_layouts/15/workbench.aspx?forceLocale=de-de
/_layouts/workbench.aspx

Prepare Deployment Solution

gulp clean
gulp build --ship
gulp bundle --ship
gulp package-solution --ship

Debug in vs.code

Checklist SPFx initial

Update Version

npm version major
npm version minor
npm version patch

Add imported pnp modules

npm install @pnp/logging @pnp/common @pnp/odata @pnp/sp @pnp/graph --save
npm install @pnp/spfx-controls-react@latest --save --save-exact
npm install @pnp/spfx-property-controls@latest --save --save-exact

Localizations

  • Location files are in JSON format
  • They work similar as Resources files (XML) on VS Solution
  • The default language is English (en-us)
  • Developers can test the locale by:
  • Updating write-manifests.json file
{
  "cdnBasePath": "<!-- PATH TO CDN -->",
  "debugLocale": "de-de"
}

or by using the "locale" command argument

gulp serve --locale=de-de

App Permission

SharePoint Online Data

MSGraph Data

Scoped service

Data Service

Sample Folder structure

Data Model

Interface to define our Data structure

export interface IHelpDeskItem {
  id?: number;
  title?: string;
  description?: string;
  level?: string;
  status?: string;
  assignedTo?: string;
  resolution?: string;
}

Interface to define our Data Access services

import { IHelpDeskItem } from "./../models/IHelpDeskItem";
import { WebPartContext } from "@microsoft/sp-webpart-base";

export default interface IDataService {
  getTitle(): string;
  isConfigured(): boolean;
  getItems(context: WebPartContext): Promise<IHelpDeskItem[]>;
  addItem(item: IHelpDeskItem): Promise<void>;
  deleteItem(id: number): Promise<void>;
}

Mocking Service for testing in local Workbench development

import { IHelpDeskItem } from "./../models/IHelpDeskItem";
import IDataService from "./IDataService";
import { IWebPartContext } from "@microsoft/sp-webpart-base";

export default class MockDataService implements IDataService {
...
  private _webPartContext: IWebPartContext;
  private _listId: string;

  constructor(webPartContext: IWebPartContext, listId: string) {
    this._webPartContext = webPartContext;
    this._listId = listId;
  }
...

  public getItems(context: IWebPartContext): Promise<IHelpDeskItem[]> {
    return new Promise<IHelpDeskItem[]>((resolve, reject) => {
      setTimeout(() => resolve([
        {
          id : 1,
          title : "That doesn't work",
          description : "When I do that, it doesn't work",
          level : "Low",
          status: "Open",
          resolution: "Do this and it will work!",
          assignedTo: "Sébastien Levert",
        }
      ]), 300);
    });
  }
}

Get Data with Sharepoint REST

Source

public getItems(context: WebPartContext): Promise<IHelpDeskItem[]> {
return new Promise<IHelpDeskItem[]>((resolve, reject) => {
    context.spHttpClient
    .get( `${this._webPartContext.pageContext.web.absoluteUrl}/_api/web/lists/GetById('${this._listId}')/items` +
            `?$select=*,HelpDeskAssignedTo/Title&$expand=HelpDeskAssignedTo`, SPHttpClient.configurations.v1)
    .then(res => res.json())
    .then(res => {
        let helpDeskItems:IHelpDeskItem[] = [];

        for(let helpDeskListItem of res.value) {
        helpDeskItems.push(this.buildHelpDeskItem(helpDeskListItem));
        }

        resolve(helpDeskItems);
    })
    .catch(err => console.log(err));
});
}

Get Data with Pnp-JS-Core

Reference Sample

Advantages

  • Type safe so you get your errors while you code and not when you execute and test
  • Works on all versions of SharePoint (On-Premises, Online, etc.)
  • Offers built-in caching mechanisms
  • Heavily used in the SharePoint Development Community

Init context in react webpart component source

public onInit(): Promise<void> {
    return super.onInit().then(_ => {
        pnpSetup({
        spfxContext: this.context
        });
    });
}

init service in react webpart component

public render(): void {
    const element: React.ReactElement<IListContentProps> = React.createElement(
      ListContent,
      {
        context: this.context,
        dataService: this.getDataService(),
        list: this.properties.list
      }
    );

    ReactDom.render(element, this.domElement);
}

Get items from list Source

public getItems(context: WebPartContext): Promise<IHelpDeskItem[]> {
return new Promise<IHelpDeskItem[]>((resolve, reject) => {

    sp.web.lists.getById(this._listId).items
    .select("*", "HelpDeskAssignedTo/Title")
    .expand("HelpDeskAssignedTo").getAll().then((sessionItems: any[]) => {
    let helpDeskItems:IHelpDeskItem[] = [];

    for(let helpDeskListItem of sessionItems) {
        helpDeskItems.push(this.buildHelpDeskItem(helpDeskListItem));
    }

    resolve(helpDeskItems);
    });

});
}

Get Search Data with async/await

Using PnPJS and Async/Await to Really Simplify Your API Calls

async/await

  private async _getSiteData(): Promise<ISPSite[]> {

    var thisDomain: string = location.host.split(".")[0];
    var exclusions: string[] = ["https://" + thisDomain + "-my.sharepoint.com", "https://" + thisDomain + ".sharepoint.com/portals/personal"];
    var exclusionString: string = " -Path:" + exclusions.join(" -Path:");
    exclusionString += " -Path=https://" + thisDomain + ".sharepoint.com";

    try {

      let result = await sp.search(&lt;SearchQuery>{
        Querytext: "contentclass:sts_site " + exclusionString,
        RowLimit: 500,
        TrimDuplicates: false,
        Properties: [{
          Name:"EnableDynamicGroups",
          Value: {
            BoolVal: true,
            QueryPropertyValueTypeIndex: QueryPropertyValueType.BooleanType
          }
        }],
        SelectProperties: ["Title", "Path", "SiteLogo"]
      });

      return this.processSearchResults(result);

    } catch (e) {

      console.error(e);
      return null;

    }

  }
public async getShipmentStatuses(serviceProps: IServiceProperties): Promise<IStatus[]> {

    try {

      let items = await sp
        .web
        .lists
        .getByTitle("SL_ShippingStatuses")
        .items
        .select("Id", "Title", "SortOrder", "CanBeCancelled")
        .orderBy("SortOrder")
        .get(spODataEntityArray<Item, IStatus>(Item));

      return items;

    } catch (e) {

      console.error(e);
      return null;

    }
}

joelfmrodrigues - demos

private async _getItems() {
  let select = '*';
  let expand = 'File';
  let filter = '';

  // filter by selected term if required
  if (this.props.term !== undefined && this.props.term !== null && this.props.term.length > 0) {
    const term = this.props.term[0];

    select = `${select},TaxCatchAll/Term`;
    expand = `${expand},TaxCatchAll`;
    filter = `TaxCatchAll/Term eq '${term.name}'`;
  }

  const items = await sp.web.lists.getById(this.props.list).items
    .select(select)
    .expand(expand)
    .filter(filter)
    .get();

  // update state
  this.setState({
    items: items ? items : []
  });
  console.log('List Items:', this.state.items);
}

Get Data from MSGraph

Start Office Fabric React

Create Sample

create-react-app demo-office-fabric-react-ts --scripts-version=react-scripts-ts

init git

git init
git add .
git commit -m "Initial commit."

add office fabric react

npm install office-ui-fabric-react@5.132.0 --save

Generator for SP applications

Upgrade yo generator SPFx

npm list -g --depth=0
npm ls -g --depth=0 @microsoft/generator-sharepoint
npm outdated --global
npm install @microsoft/generator-sharepoint@latest --global

SPFx Version Upgrade

Office 365 CLI

Latest version

npm i -g @pnp/cli-microsoft365@latest

Latest Beta version

npm i -g @pnp/cli-microsoft365@next

Create report for upgrade

m365 spfx project upgrade --shell powershell --toVersion 1.12.1 --output md > report.md

Update SPFx packages

Custom yo spfx generator

Add External JS-Frameworks

SPFx Utilities

Team Development Tools