Sivarajan's Blog

SharePoint | Office 365 | Azure | JavaScript

SharePoint 2013 / SharePoint Online : Search REST API with AngularJs (Alternative to Content Search Web Part)

In my previous two posts (post 1 and post 2), I have explained how to develop a search driven development using external data with Content Search WebPart.  In this post, we will see how to build the search driven development using SharePoint 2013 Search REST API with AngularJs (Alternative to Content Search WebPart). 

In another reason for writing this post is, currently I am developing a public portal like Mavention website. Mainly the portal users are anonymous users and they will not do any CRUD operations. So I have customized the master page looks like Mavention website. If the page request from anonymous users then, I will remove all the SharePoint script files and server side control on each page response. But the authenticated users (Web Administrator), the site will be render with as usual SharePoint features like Ribbon, Navigation, etc. I will write more about this in my next post. But the Content Search WebPart will not work without core.js and sp.js file. So I plan to achieve the similar one using Search REST API with AngularJs framework. 

Advantages and Disadvantages of Content Search WebPart

The main advantage with Content Search WebPart is that we can develop the search driven pages with minimal efforts and can customize the display templates using SharePoint designer. On successful save, equivalent JavaScript template file will be generated. This script files will be used for UI rendering.

The main disadvantages of this approach is that, this web part will not be available in SharePoint online and will available only for on-premises development. SharePoint scripts files (for example core.js, sp.js) are mandatory for Content Search WebPart. So we should carefully customize the master page. Customizing the display templates are also not easy like custom development. it will generate lot of html and script contents inline with each page requests.

Working with SharePoint REST API with AngularJs

Now a days, we have a lot of web application frameworks are available freely and can develop a single page kind of application (SPA) quickly.  AngularJs is not a mandatory here and you can use a plain JavaScript or different client frameworks like jQuery or KnockoutJs. In the post, I am not going to explain the AngularJs features and you can find the detailed documentations in the AngularJs website.

SharePoint REST API service will allow a user to query the crawled data with different options. Easily we can learn the Search API options using SharePoint 2013 Search Query Tool

This is the end results of this post,

1-end-results

Search Data Source

To bring the SQL database to SharePoint search results, you can follow my previous post.

Solution Structure

Here I have developed a simple Visual WebPart with the following solution structure. This is not generic one like Content Search WebPart and also this code is not normalized one. Currently I am developing generic WebParts for Search REST API for both SharePoint on-premise or SharePoint online development and will share the solution shortly via CodePlex.

1-solution-structure

Search Results Web Part

I am using the AngularJs external template for complete implementation. So the SearchResults.ascx webpart will have only the template reference.

<SharePoint:ScriptLink ID="Scriptlink8" runat="server" Name="~Site/_layouts/15/SearchREST/js/angular.min.js"></SharePoint:ScriptLink>
<SharePoint:ScriptLink ID="Scriptlink2" runat="server" Name="~Site/_layouts/15/SearchREST/js/jquery-1.9.1.min.js"></SharePoint:ScriptLink>
<SharePoint:ScriptLink ID="Scriptlink3" runat="server" Name="~Site/_layouts/15/SearchREST/js/SearchController.js"></SharePoint:ScriptLink>
<div ng-app="appSearch" >
    <div ng-include src="'_layouts/15/SearchREST/template/search.html'"></div>
     </div>

 

Display Templates

Like mentioned earlier, we can easily customize the search.html file based on our requirement. This file will have the search refinements and results block.

<div ng-controller="searchController" ng-init="init()">
<table style="width: 100%;">
    <tr>
        <td style="width: 10%; border: solid 1px red; vertical-align:top;">
            <label>Production Companies</label>
            <select ng-model="genres" ng-options="c.RefinementName as c.RefinementName + ' (' + c.RefinementCount + ')'   for c in refiners[1].values" ng-change="change()"></select>
            <br />
            <br />
            <label>Genres</label>
            
            <ul>
                <li ng-repeat="value in refiners[1].values"><a href='#'  ng-click="refreshSearch(value.RefinementName)">{{value.RefinementName}} ({{value.RefinementCount}})</a></li>
            </ul>
        </td>
        <td style="width: 90%; vertical-align: top; border: solid 1px red;">
            <table>
                <tr ng-repeat="movie in movies">
                    <td>
                       <img style="width:50px;height:60px" ng-src="{{movie.MoviePosterPath}}" />
                    </td>
                    <td>
                        {{movie.MovieTitle}}
                    </td>
                    <td>
                        {{movie.MovieReleaseDate | date: 'dd-MMM-yyyy'}}
                    </td>
                    <td>
                        <a ng-href="{{movie.Path}}">Path</a>
                    </td>                     
                </tr>
            </table>
        </td>
    </tr>
</table>
<input type="button" ng-disabled="currentPage == 0" ng-click="decreasePage()" value="Previous"/>
{{currentPage+1}}/{{numberOfPages()}}
<input type="button" ng-disabled="currentPage >= dataRows/pageSize - 1" ng-click="increasePage()" value="Next"/>
Current Page : {{currentPage}}
</div>

 

Search Data

The SearchController.js file will have the complete data retrieval related implementation.

var appSearch = angular.module('appSearch', []);

appSearch.controller("searchController", function ($scope, $http) {
    $scope.movies = [];
    $scope.refiners = [];

    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.dataRows = 0;

    $scope.numberOfPages = function () {
        return Math.ceil($scope.dataRows / $scope.pageSize);
    }

    $scope.startrow = 0;

    $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'" +
                "&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";

    $scope.view = {
        getView: function () {
            return "template/search.html";
        }
    };

    $scope.decreasePage = function()
    {
        $scope.currentPage = $scope.currentPage - 1;
        $scope.startrow = $scope.currentPage * $scope.pageSize;
       
        $scope.callService();
    }
    $scope.increasePage = function()
    {
        $scope.currentPage = $scope.currentPage + 1;
        $scope.startrow = $scope.currentPage * $scope.pageSize;
        $scope.callService();
    }

    $scope.init = function () {
        $scope.callService();
    };

    $scope.change = function () {
        $scope.startrow = 0;
        $scope.currentPage = 0;
   

        $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'&refinementfilters='MovieGenres:equals(" +
            $scope.genres + ")'&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";
        $scope.callService();
    };

    $scope.refreshSearch = function (value)
    {
        $scope.startrow = 0;
        $scope.currentPage = 0;

        $scope.query = "/sites/publishing/_api/search/query?querytext='*'&trimduplicates=true&selectproperties='Path,MoviePosterPath,MovieTitle,MovieReleaseDate,MovieGenres'&refiners='MovieGenres,MovieProductionCompanies'&refinementfilters='MovieGenres:equals(" +
            value + ")'&sourceid='0bc7c264-6e00-48bf-84fc-a7bb9e9f75b2'";
        $scope.callService();
    }

    $scope.callService = function () {
        $http({
            method: 'GET', url: $scope.query + "&startrow="+ $scope.startrow +"&rowsperpage=0",
            headers: { "Accept": "application/json;odata=verbose" }
        }).
        success(function (data, status, headers, config) {
            $scope.movies = [];
            //Results
            angular.forEach(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (movie) {

                angular.forEach(movie.Cells, function (movieRow) {
                    var obj = {};

                    angular.forEach(movieRow, function (movieData) {

                        obj[movieData.Key] = movieData.Value;
                    });

                    $scope.movies.push(obj);
                });
            });

            //Refiners        
            angular.forEach(data.d.query.PrimaryQueryResult.RefinementResults.Refiners.results, function (refiner) {
                var objRefiner = {};

                objRefiner["name"] = refiner.Name;
                objRefiner["values"] = refiner.Entries.results;
                $scope.refiners.push(objRefiner);
            });

            $scope.dataRows = data.d.query.PrimaryQueryResult.RelevantResults.TotalRows;

            console.log('Movie', $scope.movies);

        }).
        error(function (data, status, headers, config) {
            // called asynchronously if an error occurs
            // or server returns response with an error status.
        });
    };
});

 

Summary

Here I just explained how to develop the search driven pages using Search REST API with AngularJs. Please get back me if you have any clarifications regarding this post.

Update : I have referred the AngularJs and jQuery files from 15 hive. This will work in SharePoint on-premises development and you can use Script Box WebPart for SharePoint online.

Download Source

Comments (6) -

  • Roman Rozinov

    11/9/2013 6:33:15 PM | Reply

    Hey,
    Great post. I am wondering how would this work in the SharePoint Online scenario where web parts are not allowed to be deployed.

  • Douglas

    2/12/2014 5:40:16 AM | Reply

    Do you have more simple example like show 2 list fields from a sharepoint list and add a new item in a list using Post?

    • Sivarajan Raju

      2/12/2014 5:45:59 PM | Reply

      Currently I do not have that will update you once done the demo.

  • Douglas

    5/17/2014 5:43:14 AM | Reply

    Nice post

  • Naresh

    1/5/2016 3:41:46 AM | Reply

    Hi Sivarajan Raju,
                      I want to develop CRUD operation for sharepoint list using AngularJS with RestAPI.

    Could you please provide me some examples.

    Thanks,
    Naresh K.

Add comment

Loading