Skip to content

@pnp/sp/fields

Fields allow you to store typed information within a SharePoint list. There are many types of fields and the library seeks to simplify working with the most common types. Fields exist in both site collections (site columns) or lists (list columns) and you can add/modify/delete them at either of these levels.

Get Fields

import { sp } from "@pnp/sp";

let web = sp.web;

// get all the fields in a web
web.fields.get().then(f => {

    console.log(f);
});

// you can use odata operators on the fields collection
web.fields.select("Title", "InternalName", "TypeAsString").top(10).orderBy("Id").get().then(f => {

    console.log(f);
});

// get all the available fields in a web (includes parent web's fields)
web.availablefields.get().then(f => {

    console.log(f);
});

// get the fields in a list
web.lists.getByTitle("MyList").fields.get().then(f => {

    console.log(f);
});

// you can also get individual fields using getById, getByTitle, or getByInternalNameOrTitle
web.fields.getById("dee9c205-2537-44d6-94e2-7c957e6ebe6e").get().then(f => {

    console.log(f);
});

web.fields.getByTitle("MyField4").get().then(f => {

    console.log(f);
});

web.fields.getByInternalNameOrTitle("MyField4").get().then(f => {

    console.log(f);
});

Filtering Fields

Sometimes you only want a subset of fields from the collection. Below are some examples of using the filter operator with the fields collection.

import { sp } from '@pnp/sp';

const list = sp.web.lists.getByTitle('Custom');

// Fields which can be updated
const filter1 = `Hidden eq false and ReadOnlyField eq false`;
list.fields.select('InternalName').filter(filter1).get().then(fields => {
    console.log(`Can be updated: ${fields.map(f => f.InternalName).join(', ')}`);
    // Title, ...Custom, ContentType, Attachments
});

// Only custom field
const filter2 = `Hidden eq false and CanBeDeleted eq true`;
list.fields.select('InternalName').filter(filter2).get().then(fields => {
    console.log(`Custom fields: ${fields.map(f => f.InternalName).join(', ')}`);
    // ...Custom
});

// Application specific fields
const includeFields = [ 'Title', 'Author', 'Editor', 'Modified', 'Created' ];
const filter3 = `Hidden eq false and (ReadOnlyField eq false or (${
    includeFields.map(field => `InternalName eq '${field}'`).join(' or ')
}))`;
list.fields.select('InternalName').filter(filter3).get().then(fields => {
    console.log(`Application specific: ${fields.map(f => f.InternalName).join(', ')}`);
    // Title, ...Custom, ContentType, Modified, Created, Author, Editor, Attachments
});

// Fields in a view
list.defaultView.fields.select('Items').get().then(f => {
    const fields = (f as any).Items.results || (f as any).Items;
    console.log(`Fields in a view: ${fields.join(', ')}`);
});

Add Fields

You can add fields using the add, createFieldAsXml, or one of the type specific methods. Functionally there is no difference, however one method may be easier given a certain scenario.

import { sp } from "@pnp/sp";

let web = sp.web;

// if you use add you _must_ include the correct FieldTypeKind in the extended properties
web.fields.add("MyField1", "SP.FieldText", { 
    Group: "~Example",
    FieldTypeKind: 2,
    Filterable: true,
    Hidden: false,
    EnforceUniqueValues: true,
}).then(f => {

    console.log(f);
});

// you can also use the addText or any of the other type specific methods on the collection
web.fields.addText("MyField2", 75, { 
    Group: "~Example"
}).then(f => {

    console.log(f);
});

// if you have the field schema (for example from an old elements file) you can use createFieldAsXml
let xml = `<Field DisplayName="MyField4" Type="Text" Required="FALSE" StaticName="MyField4" Name="MyField4" MaxLength="125" Group="~Example" />`;

web.fields.createFieldAsXml(xml).then(f => {

    console.log(f);
});

// the same operations work on a list's fields collection
web.lists.getByTitle("MyList").fields.addText("MyField5", 100).then(f => {

    console.log(f);
});

// Create a lookup field, and a dependent lookup field
web.lists.getByTitle("MyList").fields.addLookup("MyLookup", "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", "MyLookupTargetField").then(f => {
    console.log(f);

    // Create the dependent lookup field
    return web.lists.getByTitle("MyList").fields.addDependentLookupField("MyLookup_ID", f.Id, "ID");
}).then(fDep => {
    console.log(fDep);
});

Adding Multiline Text Fields with FullHtml

Because the RichTextMode property is not exposed to the clients we cannot set this value via the API directly. The work around is to use the createFieldAsXml method as shown below

import { sp } from "@pnp/sp";

let web = sp.web;

const fieldAddResult = await web.fields.createFieldAsXml(`<Field Type="Note" Name="Content" DisplayName="Content" Required="{TRUE|FALSE}" RichText="TRUE" RichTextMode="FullHtml" />`);

Update a Field

You can also update the properties of a field in both webs and lists, but not all properties are able to be updated after creation. You can review this list for details.

import { sp } from "@pnp/sp";

let web = sp.web;

web.fields.getByTitle("MyField4").update({ 
    Description: "A new description",
 }).then(f => {

    console.log(f);
});

Update a Url/Picture Field

When updating a URL or Picture field you need to include the __metadata descriptor as shown below.

import { sp } from "@pnp/sp";

const data = {
    "My_Field_Name": {
        "__metadata": { "type": "SP.FieldUrlValue" },
        "Description": "A Pretty picture",
        "Url": "https://tenant.sharepoint.com/sites/dev/Style%20Library/DSC_0024.JPG",
    },
};

await sp.web.lists.getByTitle("MyListTitle").items.getById(1).update(data);

Delete a Field

import { sp } from "@pnp/sp";

let web = sp.web;

web.fields.getByTitle("MyField4").delete().then(f => {

    console.log(f);
});
spacer