Skip to content

A declarative JavaScript library for creating SharePoint client-side CAML queries

License

Notifications You must be signed in to change notification settings

kofrimpong/caml4js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

caml4js

A declarative JavaScript library for creating SharePoint client-side CAML queries.

All Query elements mentioned in the CAML docs are supported by Caml4Js

Table of Contents

Installation

Npm:

npm install caml4js --save

Npm TypeScript definitions:

npm install @types/caml4js --save-dev

Usage

In browser:

<script type="text/javascript" src="//caml4js.js"></script>

In node:

var caml4js = require('caml4js');

ES6 modules:

import { query, textField, where, or, userField, orderBy, groupBy, booleanField, and} from 'caml4js';

Basics

It's important to keep in mind the structure of CAML query and how Caml4Js tries to map it to code. The structure looks like this:

view/
  viewFields/
  aggregations/
  joins/
    join/
  projections/
  query/
    where/
    orderBy/
    groupBy/
  rowLimit/

Assume we want to fetch the Name and Population size from a SharePoint list where the country name is Ghana. To generate the CAML query using Caml4Js, you could use the following code:

let v = view(
            viewFields("Name","Population"),
            query(
                where(
                    textField("Name").equalTo("Ghana")
                )
            )
        )

Very simple, it follows the structure for CAML query we stated earlier. This will generate the following xml:

<View>
    <ViewFields>
        <FieldRef Name="Name" />
        <FieldRef Name="Population" />
    </ViewFields>
    <Query>
        <Where>
            <Eq>
                <FieldRef Name="Name"/>
                <Value Type="Text">Ghana</Value>
            </Eq>
        </Where>
    </Query>
</View>

To search using the Poulation field, we could write something like this:

let v = view(
            viewFields("Name","Population"),
            query(
                where(
                    numberField("Population").greaterThan(2000)
                )
            )
        )

If you want items where Country field is Ghana or USA:

let v = view(
            viewFields("Name","Population"),
            query(
                where(
                    or(
                        textField("Name").equalTo("Ghana"),
                        textField("Name").equalTo("USA")
                    )
                )
            )
        )
<View>
    <ViewFields>
        <FieldRef Name="Name" />
        <FieldRef Name="Population" />
    </ViewFields>
    <Query>
        <Where>
            <Or>
                <Eq>
                    <FieldRef Name="Name"/>
                    <Value Type="Text">Ghana</Value>
                </Eq>
                <Eq>
                    <FieldRef Name="Name"/>
                    <Value Type="Text">USA</Value>
                </Eq>
            </Or>
        </Where>
    </Query>
</View>

or

let v = view(
            viewFields("Name","Population"),
            query(
                where(
                    textField("Name").in(["USA","Ghana"])
                )
            )
        )
<View>
    <ViewFields>
        <FieldRef Name="Name" />
        <FieldRef Name="Population" />
    </ViewFields>
    <Query>
        <Where>
            <In>
                <FieldRef Name="Name" />
                <Values>
                    <Value Type="Text">USA</Value>
                    <Value Type="Text">Ghana</Value>
                </Values>
            </In>
        </Where>
    </Query>
</View>

Complex nested operators

Caml4Js makes it easy to generate complex queries.

let q = query(
            where(
                and(
                    and(
                        booleanField("Enabled").isTrue(),
                        or(
                            userField("Audience").includes(100),
                            userField("Audience").includes(101)
                        )
                    ),
                    or(
                        textField("Title").equalTo("Test Suites"),
                        and(
                            choiceField("Status").equalTo("Open"),
                            dateTimeField("Created").greaterThan("2019-01-01T00:00:00.000Z")
                        )
                    )
                )
            )
    )
<Query>
    <Where>
        <And>
            <And>
                <Eq>
                    <FieldRef Name="Enabled"/>
                    <Value Type="Integer">1</Value>
                </Eq>
                <Or>
                    <Eq>
                        <FieldRef Name="Audience"/>
                        <Value Type="UserMulti">100</Value>
                    </Eq>
                    <Eq>
                        <FieldRef Name="Audience"/>
                        <Value Type="UserMulti">101</Value>
                    </Eq>
                </Or>
            </And>
            <Or>
                <Eq>
                    <FieldRef Name="Title"/>
                    <Value Type="Text">Test Suites</Value>
                </Eq>
                <And>
                    <Eq>
                        <FieldRef Name="Status"/>
                        <Value Type="Choice">Open</Value>
                    </Eq>
                    <Gt>
                        <FieldRef Name="Created"/>
                        <Value Type="DateTime" IncludeTimeValue="TRUE">2019-01-01T00:00:00.000Z</Value>
                    </Gt>
                </And>
            </Or>
        </And>
    </Where>
</Query>

Membership

Let's find all items where the Author is the current user

let v = view(
            viewFields("Name","Population"),
            query(
                where(
                    userField("Author").equalToCurrentUser()
                )
            )
        )
<View>
    <ViewFields>
        <FieldRef Name="Name"/>
        <FieldRef Name="Population"/>
    </ViewFields>
    <Query>
        <Where>
            <Eq>
                <FieldRef Name="Author" LookupId="TRUE"/>
                <Value Type="Integer">
                    <UserID/>
                </Value>
            </Eq>
        </Where>
    </Query>
</View>

Joins

You can use Caml4Js for list joins and field projections to generate your CAML queries. Suppose we have lists Student and Result with a lookup column called Candidate. Joins

let v = view(
        viewFields("Grade","Name"),
        joins(
            join(JoinType.LEFT, "student", "Candidate","", [{ Name: "Name", Field: "Name"}])
        ),
        query(
            where(
                textField("Name").equalTo("John")
            )
        )
    )
<View>
    <ViewFields>
        <FieldRef Name='Name'/>
        <FieldRef Name='Grade'/>
    </ViewFields>
    <Joins>
        <Join Type='LEFT' ListAlias='student'>
            <Eq>
                <FieldRef Name='Candidate' RefType='Id'/>
                <FieldRef Name='ID' List='student'/>
            </Eq>
        </Join>
    </Joins>
    <ProjectedFields>
        <Field Name='Grade' Type='Lookup' List='student' ShowField='Grade'/>
    </ProjectedFields>
    <Query>
        <Where>
            <Eq>
                <FieldRef Name='Name'/>
                <Value Type='Text'>John</Value>
            </Eq>
        </Where>
    </Query>
</View>

Suppose we have an Orders, Customers and Cities lists. The Orders list has a CustomerName field that looks up to a Customers list, and that the Customers list has a CityName field that looks up to a Cities list.

Let's see how we can return all orders from an Orders list where the customer’s city is London.

let v = view(
        viewFields("CustomerCity"),
        joins(
            join(JoinType.LEFT, "customers", "CustomerName"),
            join(JoinType.LEFT, "customerCities", "CityName", [{ Name: "CustomerCity", Field: "Title" }],"customers"),
        ),
        query(
            where(
                textField("CustomerCity").equalTo("London")
            )
        )
    )

this will give us the following

<View>
    <ViewFields>
        <FieldRef Name="CustomerCity"/>
    </ViewFields>
    <Joins>
        <Join Type="LEFT" ListAlias="Customers">
            <Eq>
                <FieldRef Name="CustomerName" RefType="Id" />
                <FieldRef Name="ID" List="Customers"/>
            </Eq>
        </Join>
        <Join Type="LEFT" ListAlias="Cities">
            <Eq>
                <FieldRef Name="CityName" RefType="Id"/>
                <FieldRef Name="ID" List="Cities"/>
            </Eq>
        </Join>
    </Joins>
    <ProjectedFields>
        <Field Name="CustomerCity" Type="Lookup" List="Cities" ShowField="Title"/>
    </ProjectedFields>
    <Query>
        <Where>
            <Eq>
                <FieldRef Name="CustomerCity"/>
                <Value Type="Text">London</Value>
            </Eq>
        </Where>
    </Query>
</View>

Dynamic Where (WhereBuilder)

You can use WhereBuilder to generate a dynamic WHERE element.

    const builder = whereBuilder();
    builder.addQuery(booleanField("Enabled").isTrue());

    //somewhere in the code
    builder.addQuery(
        or(
            userField("Audience").includes(100),
            userField("Audience").includes(101)
        )
    );

    let v = view(
                viewFields("Name","Population"),
                query(
                    builder.toWhere()
                )
        );

this will give us

<View>
    <Query>
        <Where>
            <And>
                <Eq>
                    <FieldRef Name="Enabled"/>
                    <Value Type="Integer">1</Value>
                </Eq>
                <Or>
                    <Eq>
                        <FieldRef Name="Audience"/>
                        <Value Type="UserMulti">100</Value>
                    </Eq>
                    <Eq>
                        <FieldRef Name="Audience"/>
                        <Value Type="UserMulti">101</Value>
                    </Eq>
                </Or>
            </And>
        </Where>
    </Query>
</View>

WhereBuilder can be chained

builder.addQuery(booleanField("Enabled").isTrue())
        .addQuery(userField("Audience").includes(101))

Utility functions

encodeAsCDATA(s:string)

This function is used to encode textual data as CDATA that should not be parsed by an XML parser. The characters "<" and "&" are not allowed within XML elements. The "<" character will throw an error because the parser will interpret it as the start of a new element. The "&" character will throw an error because the parser will interpret it as the start of a character entity.

sanitizeQuery(s:string)

Removes line breaks from supplied query string

About

A declarative JavaScript library for creating SharePoint client-side CAML queries

Resources

License

Stars

Watchers

Forks

Packages

No packages published