Added core code to HomeController and DataService

This commit is contained in:
dhartman 2016-10-11 18:37:17 -04:00
parent 428e422cc3
commit 7d50472046
8 changed files with 220 additions and 26 deletions

View File

@ -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 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 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 ### Building the code

View File

@ -13,7 +13,8 @@
"description": { "default": "AngularSearch description" }, "description": { "default": "AngularSearch description" },
"officeFabricIconFontName": "Page", "officeFabricIconFontName": "Page",
"properties": { "properties": {
"description": "AngularSearch" "description": "AngularSearch",
"contentTypes": ""
} }
}] }]
} }

View File

@ -16,6 +16,7 @@ import { IAngularSearchWebPartProps } from './IAngularSearchWebPartProps';
import * as angular from 'angular'; import * as angular from 'angular';
import HomeController from './app/HomeController'; import HomeController from './app/HomeController';
import DataService from './app/DataService';
import MockHttpClient from './MockHttpClient'; import MockHttpClient from './MockHttpClient';
import 'ng-office-ui-fabric'; import 'ng-office-ui-fabric';
@ -35,7 +36,7 @@ export interface ISPStrVal {
export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngularSearchWebPartProps> { export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngularSearchWebPartProps> {
private $injector: ng.auto.IInjectorService; private $injector: ng.auto.IInjectorService;
private _listsInThisSite: IPropertyPaneDropdownOption[] = []; private _CTypesInThisSite: IPropertyPaneDropdownOption[] = [];
get baseUrl(): string { return '$BASEURL$'; } get baseUrl(): string { return '$BASEURL$'; }
@ -50,12 +51,12 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
//Determine if we are in a local environment //Determine if we are in a local environment
if (this.context.environment.type == EnvironmentType.Local) { if (this.context.environment.type == EnvironmentType.Local) {
this._getMockOptions().then((data) => { this._getMockOptions().then((data) => {
this._listsInThisSite = data; this._CTypesInThisSite = data;
}); });
} }
else { else {
this._getOptions().then((data) => { this._getOptions().then((data) => {
this._listsInThisSite = data; this._CTypesInThisSite = data;
}); });
} }
@ -66,7 +67,7 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
if (this.renderedOnce === false) { if (this.renderedOnce === false) {
const wp: AngularSearchWebPart = this; const wp: AngularSearchWebPart = this;
this.domElement.innerHTML = `<angularsearch web="${this.context.pageContext.web.absoluteUrl}" hello="${wp.title}" style='${angular.toJson(styles)}'></angularsearch>`; this.domElement.innerHTML = `<angularsearch web="${this.context.pageContext.web.absoluteUrl}" style='${angular.toJson(styles)}' contentType='${this.properties.contentTypes}'></angularsearch>`;
let sce: ng.ISCEDelegateService; let sce: ng.ISCEDelegateService;
angular.module('angularsearchapp', [ angular.module('angularsearchapp', [
@ -77,11 +78,12 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
controllerAs: 'vm', controllerAs: 'vm',
bindings: { bindings: {
web: '@', web: '@',
hello: '@', style: '<',
style: '<' contentType: '@'
}, },
templateUrl: `${this.baseUrl}home-template.html` templateUrl: `${this.baseUrl}home-template.html`
}) })
.service('DataService', DataService)
.config(function ($sceDelegateProvider: ng.ISCEDelegateProvider): void { .config(function ($sceDelegateProvider: ng.ISCEDelegateProvider): void {
$sceDelegateProvider.resourceUrlWhitelist([ $sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads. // Allow same origin resource loads.
@ -93,6 +95,10 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
this.$injector = angular.bootstrap(this.domElement, ['angularsearchapp']); this.$injector = angular.bootstrap(this.domElement, ['angularsearchapp']);
} }
this.$injector.get('$rootScope').$broadcast('configurationChanged', {
contentType: this.properties.contentTypes
});
} }
private _getCTypes(url: string): Promise<ISPCTypeLists> { private _getCTypes(url: string): Promise<ISPCTypeLists> {
@ -130,14 +136,14 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
} }
private _getOptions(): Promise<IPropertyPaneDropdownOption[]> { private _getOptions(): Promise<IPropertyPaneDropdownOption[]> {
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) => { return this._getCTypes(url).then((response) => {
var options: Array<IPropertyPaneDropdownOption> = new Array<IPropertyPaneDropdownOption>(); var options: Array<IPropertyPaneDropdownOption> = new Array<IPropertyPaneDropdownOption>();
var lists: ISPCType[] = response.value; var lists: ISPCType[] = response.value;
lists.forEach((list: ISPCType) => { lists.forEach((list: ISPCType) => {
console.log("Found Content Type(s)"); 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; return options;
@ -160,9 +166,9 @@ export default class AngularSearchWebPart extends BaseClientSideWebPart<IAngular
PropertyPaneTextField('description', { PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel label: strings.DescriptionFieldLabel
}), }),
PropertyPaneDropdown('test', { PropertyPaneDropdown('contentTypes', {
label: 'Dropdown', label: 'Dropdown',
options: this._listsInThisSite options: this._CTypesInThisSite,
}) })
] ]
} }

View File

@ -1,3 +1,4 @@
export interface IAngularSearchWebPartProps { export interface IAngularSearchWebPartProps {
description: string; description: string;
contentTypes: string;
} }

View File

@ -0,0 +1,62 @@
import { ISearchResults } from './../models/ISearchResults'
export interface IDataService {
getSearchResults(webUrl: string, contentType: string): ng.IPromise<ISearchResults>;
}
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<ISearchResults> {
const deferred: ng.IDeferred<ISearchResults> = 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<any>): 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<string> {
const deferred: ng.IDeferred<string> = 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;
}
}

View File

@ -1,14 +1,85 @@
import { IDataService } from './DataService';
import { ISearchResults, ICells, ICellValue } from './../models/ISearchResults';
interface IConfigurationChangeArgs {
contentType: string;
}
export default class HomeController { export default class HomeController {
public static $inject: string[] = ['$rootScope', '$scope', '$attrs']; public static $inject: string[] = ['DataService', '$rootScope', '$scope', '$attrs'];
public styles: any = null;
public hello: string = null; public status: string = undefined;
private web: string = null; 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.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 [];
}
} }
} }

View File

@ -1,14 +1,30 @@
<div class="{{::vm.styles.angularTemplate}}"> <div class="{{::vm.styles.angularTemplate}}">
<div class="{{::vm.styles.container}}"> <div class="{{::vm.styles.container}}">
<div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white {{::vm.styles.row}}"> <div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white {{::vm.styles.row}}">
<div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1"> <div class="ms-Grid-col ms-u-lg10 ms-u-x-18 ms-u-xlPush2 ms-u-lgPush1">
<span class="ms-font-xl ms-fontColor-white">Welcome to SharePoint!</span> <span class="ms-font-xl ms-fontColor-white">
<p class="ms-font-l ms-fontColor-white">Customize SharePoint experiences using Web Parts.</p> Sample SharePoint Search in Angular
<p class="ms-font-l ms-fontColor-white">{{::vm.hello}}</p> </span>
<a href="https://github.com/SharePoint/sp-dev-docs/wiki" class="ms-Button {{::vm.styles.button}}">
<span class="ms-Button-label">Learn more</span>
</a>
</div> </div>
</div> </div>
<div class="ms-Grid-row ms-bgColor-themeDark mg-fontColor-white ${::vm.styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-x-18 ms-u-xlPush2 ms-u-lgPush1">
<uif-button ng-click="vm.getSearchResults()" ng-disabled="vm.searchNotConfigured">
Refresh Search Results
</uif-button>
</div>
</div>
<div class="ms-Grid-row ms-bgColor-themeDark mg-fontColor-white ${::vm.styles.row}">
<div class="ms-Grid-col ms-u-lg10 ms-u-x-18 ms-u-xlPush2 ms-u-lgPush1">
<div class="ms-fontColor-white">{{vm.status}}</div>
</div>
</div>
<uif-list>
<uif-list-item ng-repeat="item in vm.items">
<uif-list-item-primary-text>{{item.Title}}</uif-list-item-primary-text>
<uif-list-item-secondary-text>By: {{item.Author}}</uif-list-item-secondary-text>
<uif-list-item-tertiary-text>{{item.HitHighlightedSummary}}</uif-list-item-tertiary-text>
</uif-list>
</div> </div>
</div> </div>

View File

@ -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<ICells>;
}
export interface ICells {
Cells: IResultValues;
}
export interface IResultValues {
results: Array<ICellValue>;
}
export interface ICellValue {
Key: string;
Value: string;
ValueType: string;
}