From 7d5047204652f9293d69ca55ac5951a3357dfff6 Mon Sep 17 00:00:00 2001 From: dhartman Date: Tue, 11 Oct 2016 18:37:17 -0400 Subject: [PATCH] Added core code to HomeController and DataService --- samples/angular-search/README.md | 4 + .../AngularSearchWebPart.manifest.json | 3 +- .../angularSearch/AngularSearchWebPart.ts | 26 +++--- .../IAngularSearchWebPartProps.ts | 1 + .../webparts/angularSearch/app/DataService.ts | 62 +++++++++++++ .../angularSearch/app/HomeController.ts | 87 +++++++++++++++++-- .../angularSearch/app/home-template.html | 30 +++++-- .../angularSearch/models/ISearchResults.ts | 33 +++++++ 8 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 samples/angular-search/src/webparts/angularSearch/app/DataService.ts create mode 100644 samples/angular-search/src/webparts/angularSearch/models/ISearchResults.ts diff --git a/samples/angular-search/README.md b/samples/angular-search/README.md index abff8e9d3..7a8766af6 100644 --- a/samples/angular-search/README.md +++ b/samples/angular-search/README.md @@ -9,6 +9,10 @@ through SharePoint's REST API. The logic for querying the SharePoint Content Types in the properties of the webpart was in part due to Chris O'Brien and this blog post http://www.sharepointnutsandbolts.com/2016/09/sharepoint-framework-spfx-web-part-properties-dynamic-dropdown.html?m=0 +Environment: + Enable publishing features on site collection + Enable publishing features on site + ### Building the code diff --git a/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.manifest.json b/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.manifest.json index 67f19ae63..aa58acd48 100644 --- a/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.manifest.json +++ b/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.manifest.json @@ -13,7 +13,8 @@ "description": { "default": "AngularSearch description" }, "officeFabricIconFontName": "Page", "properties": { - "description": "AngularSearch" + "description": "AngularSearch", + "contentTypes": "" } }] } diff --git a/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.ts b/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.ts index 46d712316..3c1b70a1e 100644 --- a/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.ts +++ b/samples/angular-search/src/webparts/angularSearch/AngularSearchWebPart.ts @@ -16,6 +16,7 @@ import { IAngularSearchWebPartProps } from './IAngularSearchWebPartProps'; import * as angular from 'angular'; import HomeController from './app/HomeController'; +import DataService from './app/DataService'; import MockHttpClient from './MockHttpClient'; import 'ng-office-ui-fabric'; @@ -35,7 +36,7 @@ export interface ISPStrVal { export default class AngularSearchWebPart extends BaseClientSideWebPart { private $injector: ng.auto.IInjectorService; - private _listsInThisSite: IPropertyPaneDropdownOption[] = []; + private _CTypesInThisSite: IPropertyPaneDropdownOption[] = []; get baseUrl(): string { return '$BASEURL$'; } @@ -50,12 +51,12 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart { - this._listsInThisSite = data; + this._CTypesInThisSite = data; }); } else { this._getOptions().then((data) => { - this._listsInThisSite = data; + this._CTypesInThisSite = data; }); } @@ -66,7 +67,7 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart`; + this.domElement.innerHTML = ``; let sce: ng.ISCEDelegateService; angular.module('angularsearchapp', [ @@ -77,11 +78,12 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart { @@ -130,14 +136,14 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart { - var url = this.context.pageContext.web.absoluteUrl + '/_api/web/AvailableContentTypes?&filter=Group eq \'Document Content Types\''; + var url = this.context.pageContext.web.absoluteUrl + '/_api/web/AvailableContentTypes?$filter=Group eq \'Page Layout Content Types\''; return this._getCTypes(url).then((response) => { var options: Array = new Array(); var lists: ISPCType[] = response.value; lists.forEach((list: ISPCType) => { console.log("Found Content Type(s)"); - options.push({ key: list.Id.StringValue, text: list.Name }); + options.push({ key: list.Name, text: list.Name }); }); return options; @@ -160,9 +166,9 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart; +} + +export default class DataService implements IDataService { + public static $inject: string[] = ['$q', '$http']; + + constructor(private $q: ng.IQService, private $http: ng.IHttpService) { } + + public getSearchResults(webUrl: string, contentType: string): ng.IPromise { + const deferred: ng.IDeferred = this.$q.defer(); + + this.$http({ + url: `${webUrl}/_api/search/query?queryText='ContentType:"${contentType}"'`, + method: 'GET', + headers: { + 'Accept': 'application/json;odata=verbose' + } + }).then((response: ng.IHttpPromiseCallbackArg): void => { + if (response != null && response.data != null) { + const result: ISearchResults = response.data.d.query; + if (typeof result.PrimaryQueryResult !== 'undefined' && + typeof result.PrimaryQueryResult.RelevantResults !== 'undefined' && + typeof result.PrimaryQueryResult.RelevantResults.Table !== 'undefined' && + typeof result.PrimaryQueryResult.RelevantResults.Table.Rows !== 'undefined') { + deferred.resolve(result); + } + else { + deferred.reject("problem getting search results"); + } + } + else { + deferred.reject("problem getting search results"); + } + }, (error: any): void => { + deferred.reject(error); + }); + + return deferred.promise; + } + + private getRequestDigest(webUrl: string): ng.IPromise { + const deferred: ng.IDeferred = this.$q.defer(); + + this.$http({ + url: webUrl + '/_api/contextinfo', + method: 'POST', + headers: { + 'Accept': 'application/json;odata=nometedata' + } + }) + .then((digestResult: ng.IHttpPromiseCallbackArg<{ FormDigestValue: string }>): void => { + deferred.resolve(digestResult.data.FormDigestValue); + }, (error: any): void => { + deferred.reject(error); + }); + + return deferred.promise; + } +} \ No newline at end of file diff --git a/samples/angular-search/src/webparts/angularSearch/app/HomeController.ts b/samples/angular-search/src/webparts/angularSearch/app/HomeController.ts index 25d74506d..3d5283e4c 100644 --- a/samples/angular-search/src/webparts/angularSearch/app/HomeController.ts +++ b/samples/angular-search/src/webparts/angularSearch/app/HomeController.ts @@ -1,14 +1,85 @@ +import { IDataService } from './DataService'; +import { ISearchResults, ICells, ICellValue } from './../models/ISearchResults'; + +interface IConfigurationChangeArgs { + contentType: string; +} export default class HomeController { - public static $inject: string[] = ['$rootScope', '$scope', '$attrs']; - public styles: any = null; - public hello: string = null; - private web: string = null; + public static $inject: string[] = ['DataService', '$rootScope', '$scope', '$attrs']; + + public status: string = undefined; + public styles: any = null; + public searchNotConfigured: boolean = true; + public items: any[] = []; + + private _web: string = null; + private _contentType: string = undefined; + + constructor(private dataService: IDataService, private $rootScope: ng.IRootScopeService, private $scope: ng.IScope, private $attrs: ng.IAttributes) { + const vm: HomeController = this; - constructor(private $rootScope: ng.IRootScopeService, private $scope: ng.IScope, private $attrs: ng.IAttributes) { - let vm: HomeController = this; vm.styles = $attrs['style']; - vm.hello = $attrs['hello']; - vm.web = $attrs['web']; + vm._web = $attrs['web']; + vm._contentType = $attrs['contenttype']; + + if (this._contentType !== undefined) { + this._init(this._contentType, vm.$scope); + } + else { + this._init(undefined, undefined); + } + + $rootScope.$on('configurationChanged', + (event: ng.IAngularEvent, args: IConfigurationChangeArgs): void => { + vm._init(args.contentType, vm.$scope); + }); + } + + private _init(ctype: string, $scope: ng.IScope): void { + if (ctype !== undefined && ctype.length > 0) { + this._contentType = ctype; + this.searchNotConfigured = false; + } + else { + this.searchNotConfigured = true; + } + + this.status = this.searchNotConfigured ? 'Please configure the search settings in the Web Part properties' : 'Ready'; + if ($scope) { + //$scope.$digest(); + + //get search results + this.getSearchResults(); + } + } + + public getSearchResults(): void { + this.status = 'Loading search results...'; + this.dataService.getSearchResults(this._web, this._contentType) + .then((results: ISearchResults): void => { + this.items = this._setSearchResults(results.PrimaryQueryResult.RelevantResults.Table.Rows.results); + console.log(this.items); + }); + + } + + private _setSearchResults(searchResults: ICells[]): any[] { + if (searchResults.length > 0) { + const temp: any[] = []; + searchResults.forEach((result: ICells) => { + var val: Object = {}; + + result.Cells.results.forEach((cell: ICellValue) => { + val[cell.Key] = cell.Value; + }); + + temp.push(val); + }); + return temp; + } + else { + return []; + } } } \ No newline at end of file diff --git a/samples/angular-search/src/webparts/angularSearch/app/home-template.html b/samples/angular-search/src/webparts/angularSearch/app/home-template.html index 849a14fd5..8f9719419 100644 --- a/samples/angular-search/src/webparts/angularSearch/app/home-template.html +++ b/samples/angular-search/src/webparts/angularSearch/app/home-template.html @@ -1,14 +1,30 @@ +
-
- Welcome to SharePoint! -

Customize SharePoint experiences using Web Parts.

-

{{::vm.hello}}

- - Learn more - +
+ + Sample SharePoint Search in Angular +
+
+
+ + Refresh Search Results + +
+
+
+
+
{{vm.status}}
+
+
+ + + {{item.Title}} + By: {{item.Author}} + {{item.HitHighlightedSummary}} +
\ No newline at end of file diff --git a/samples/angular-search/src/webparts/angularSearch/models/ISearchResults.ts b/samples/angular-search/src/webparts/angularSearch/models/ISearchResults.ts new file mode 100644 index 000000000..6110991bb --- /dev/null +++ b/samples/angular-search/src/webparts/angularSearch/models/ISearchResults.ts @@ -0,0 +1,33 @@ +export interface ISearchResults { + PrimaryQueryResult: IPrimaryQueryResult; +} + +export interface IPrimaryQueryResult { + RelevantResults: IRelevantResults; +} + +export interface IRelevantResults { + Table: ITable; +} + +export interface ITable { + Rows: IResults; +} + +export interface IResults { + results: Array; +} + +export interface ICells { + Cells: IResultValues; +} + +export interface IResultValues { + results: Array; +} + +export interface ICellValue { + Key: string; + Value: string; + ValueType: string; +} \ No newline at end of file