Working with Models in SPFx and Consume Some Field Types

April 19, 2023 By pH7x Systems

This article will give you some insights to work with models in the SharePoint Framework SPFx and consume information from some Field Types

FIELDS

  1. URL
  2. CHOICE
  3. PERSON

Why to use Models?

TypeScript is a superset of JavaScript that adds optional static typing and other features to JavaScript. It provides a way to catch errors early through static type checking and can help you write more maintainable code by providing features such as classes, interfaces, and modules. TypeScript also supports the latest JavaScript features on our code.

In summary, some benefits of using TypeScript include:

  • Early error detection through static type checking
  • Improved code maintainability through features such as classes, interfaces, and modules
  • Support for the latest JavaScript features

Source: Conversation with Bing, 4/19/2023

In TypeScript, models are used to describe some form of information. They can describe varying shapes of data, ranging from simple constructs like strings, arrays, and objects. Interfaces, types are only virtual structures that don’t transpile to any JavaScript; they just help the compiler make our life easier.

In summary, models are used in TypeScript to describe data structures and help make our code more maintainable and easier to understand.

Modeling

First you will need a Response Model, await resolves proimises making your code act syncronous, ergo Promise<IResponseModel[]> becomes IModel[], and the you can use map to convert IResponseModel[] into our internal object IModel[]

If you have any problem, keep calm, this link from the Microsoft Learning will guide you on the //…. Omitted for abbreviation areas.

Let’s create our Response Model IResponseModel[] Names must match the List Internal Names

//Create response. Names must match the List Internal Names
export interface IResponseModel {
    Id: number;
    Title: string;
    MyHyperlinkFiledInternalName: string;
    MyChoiceInternalName: string;
    MyPersonInternalName: string;
}

Then you will need your IModel[] to be mapped, but as you can see you have to create more models to be inherit from you Fields Properties, so your Properties must match again with the internal names for the List, namely in IHyperlinkResponse and IPersonResponse for example.

In your Base Model you can give friendly names to be mapped like Hyperlink, MyChoice, Person and so on.

//Your base Model
export interface IModel {
    Id: number;
    Title: string;
    Hyperlink: IHyperlinkResponse;
    MyChoice: string;
    Person: IPersonResponse;
}

For the Hyperlink Field you can create like this

//Create the Url from the Hyperlink Field
export interface IHyperlinkResponse {
    Description: string;
    Url: string;
}

For the Choice field, if you don’t want to give a Key you will only need a string

MyChoice: string;

For the Person Field, I’m giving you an example to get FirstName, LastName and EMail. the EMail property have a capital M, you have more properties.

//Create the Person Field
export interface IPersonResponse {
    FirstName: string;
    LastName: string;
    EMail: string;
}

Now let’s consume and map your Models in the WebPart. I’m not using Services because it’s not the scope for this article. I’m using the PnPjs because it’s an amazing PnP Project

// .... Omitted for abbreviation 

//Import all Interfaces
import { IModelResponse, IModel } from "../../../interfaces/models";

import { getSP } from "../../../pnpJsConfig";
import { SPFI } from "@pnp/sp";
import { Logger, LogLevel } from "@pnp/logging";

// .... Omitted for abreviation

constructor(props: IWebPartProps) {
    super(props);
    // set initial state
    this.state = {
      items: [],
      errors: []
    };
    this._sp = getSP();
  }

// .... Omitted for abbreviation 

Now our Function to consume all information

private _readYourData = async (): Promise<void> => {
    try {
      const response: IModelResponse[] = await this._sp.web.lists
        .getByTitle(this.LIST_SOURCE)
        .items
        .select("Id", "Title", "MyHyperlinkFiledInternalName", "MyChoiceInternalName", "MyPersonInternalName")();

      // use map to convert IModelResponse[] into our internal object IModel[]
      const items: IModel[] = response.map((item: IModelResponse) => {
        return {
          Id: item.Id,
          Title: item.Title || "Unknown",
          Hyperlink: item.MyHyperlinkFiledInternalName.Url || "Unknown",
          MyChoice: item.MyChoiceInternalName|| "Unknown",
          Person: item.MyPersonInternalName.FirstName|| "Unknown"
        };
      });

      // Add the items to the state
      console.log(response);
      this.setState({ items });
    } catch (err) {
      Logger.write(`${this.LOG_SOURCE} (_readTimeline) - ${JSON.stringify(err)} - `, LogLevel.Error);
    }
  }

Let’s get you a Mapping

  public render(): React.ReactElement<IYourWebPartProps> {
    try {
      const {
       // .... Omitted for abbreviation 
      } = this.props;

      return (
        // .... Omitted for abbreviation 

                {this.state.items.map((item, idx) => {
                  return (
                    // .... Omitted for abbreviation 
                  );
                })}
        // .... Omitted for abbreviation
      );
    } catch (err) {
      Logger.write(`${this.LOG_SOURCE} (render) - ${JSON.stringify(err)} - `, LogLevel.Error);
    }
    return null;
  }

CONCLUSION

Data modeling makes it easier for developers, and other stakeholders to view and understand relationships among the data in a SharePoint List or making your call easier when having Expanded Fields. In addition, it can reduce errors in software and Lists development.