mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-06-30 18:15:11 +00:00
[ADF-2150] improved queryBody mechanism (#2852)
* [ADF-2150] changed query body parameter to a function * [ADF-2150] added an example page where to try change query body * [ADF-2150] improved queryBody mechanism * [ADF-2150] fixed content node test * [ADF-2150] extended docs added another way to use the query node * [ADF-2150] fixed test for search on content node * [ADF-2150] added some improvements to service config * [ADF-2150] changed the documentation accordingly * [ADF-2150] added PR changes * [ADF-2150] fixed jdoc * [ADF-2150] added checkbox to switch from service approach to input object approach * [ADF-2150] fixed build error on demo shell
This commit is contained in:
parent
8788eaeb80
commit
a401ebd35d
@ -25,7 +25,12 @@
|
|||||||
"SOCIAL": "Social",
|
"SOCIAL": "Social",
|
||||||
"SETTINGS": "Settings",
|
"SETTINGS": "Settings",
|
||||||
"OVERLAY_VIEWER": "Overlay Viewer",
|
"OVERLAY_VIEWER": "Overlay Viewer",
|
||||||
"ABOUT": "About"
|
"ABOUT": "About",
|
||||||
|
"SEARCH": "Extended Search",
|
||||||
|
"EXTENDED_SEARCH_QUERY_BODY": "Extended Search with Query Body",
|
||||||
|
"WORD_TO_SEARCH":"Search Word",
|
||||||
|
"SEARCH_CREATED_BY" : "Created By",
|
||||||
|
"SEARCH_SERVICE_APPROACH": "Check this to disable the input property and use the service approach"
|
||||||
},
|
},
|
||||||
"DOCUMENT_LIST": {
|
"DOCUMENT_LIST": {
|
||||||
"MULTISELECT_CHECKBOXES" :"Multiselect (with checkboxes)",
|
"MULTISELECT_CHECKBOXES" :"Multiselect (with checkboxes)",
|
||||||
|
@ -16,6 +16,7 @@ import { AppLayoutComponent } from './components/app-layout/app-layout.component
|
|||||||
import { HomeComponent } from './components/home/home.component';
|
import { HomeComponent } from './components/home/home.component';
|
||||||
import { SearchBarComponent } from './components/search/search-bar.component';
|
import { SearchBarComponent } from './components/search/search-bar.component';
|
||||||
import { SearchResultComponent } from './components/search/search-result.component';
|
import { SearchResultComponent } from './components/search/search-result.component';
|
||||||
|
import { SearchExtendedComponent } from './components/search/search-extended.component';
|
||||||
import { AboutComponent } from './components/about/about.component';
|
import { AboutComponent } from './components/about/about.component';
|
||||||
import { FormComponent } from './components/form/form.component';
|
import { FormComponent } from './components/form/form.component';
|
||||||
import { FormListComponent } from './components/form/form-list.component';
|
import { FormListComponent } from './components/form/form-list.component';
|
||||||
@ -68,6 +69,7 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li
|
|||||||
HomeComponent,
|
HomeComponent,
|
||||||
SearchBarComponent,
|
SearchBarComponent,
|
||||||
SearchResultComponent,
|
SearchResultComponent,
|
||||||
|
SearchExtendedComponent,
|
||||||
AboutComponent,
|
AboutComponent,
|
||||||
ProcessServiceComponent,
|
ProcessServiceComponent,
|
||||||
ShowDiagramComponent,
|
ShowDiagramComponent,
|
||||||
|
@ -29,6 +29,7 @@ import { FormViewerComponent } from './components/process-service/form-viewer.co
|
|||||||
import { FormNodeViewerComponent } from './components/process-service/form-node-viewer.component';
|
import { FormNodeViewerComponent } from './components/process-service/form-node-viewer.component';
|
||||||
import { AppsViewComponent } from './components/process-service/apps-view.component';
|
import { AppsViewComponent } from './components/process-service/apps-view.component';
|
||||||
import { SearchResultComponent } from './components/search/search-result.component';
|
import { SearchResultComponent } from './components/search/search-result.component';
|
||||||
|
import { SearchExtendedComponent } from './components/search/search-extended.component';
|
||||||
|
|
||||||
import { DataTableComponent } from './components/datatable/datatable.component';
|
import { DataTableComponent } from './components/datatable/datatable.component';
|
||||||
import { WebscriptComponent } from './components/webscript/webscript.component';
|
import { WebscriptComponent } from './components/webscript/webscript.component';
|
||||||
@ -95,6 +96,11 @@ export const appRoutes: Routes = [
|
|||||||
component: SearchResultComponent,
|
component: SearchResultComponent,
|
||||||
canActivate: [AuthGuardEcm]
|
canActivate: [AuthGuardEcm]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'extendedSearch',
|
||||||
|
component: SearchExtendedComponent,
|
||||||
|
canActivate: [AuthGuardEcm]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'activiti',
|
path: 'activiti',
|
||||||
component: AppsViewComponent,
|
component: AppsViewComponent,
|
||||||
|
@ -41,6 +41,7 @@ export class AppLayoutComponent {
|
|||||||
{ href: '/tag', icon: 'local_offer', title: 'APP_LAYOUT.TAG' },
|
{ href: '/tag', icon: 'local_offer', title: 'APP_LAYOUT.TAG' },
|
||||||
{ href: '/social', icon: 'thumb_up', title: 'APP_LAYOUT.SOCIAL' },
|
{ href: '/social', icon: 'thumb_up', title: 'APP_LAYOUT.SOCIAL' },
|
||||||
{ href: '/settings', icon: 'settings', title: 'APP_LAYOUT.SETTINGS' },
|
{ href: '/settings', icon: 'settings', title: 'APP_LAYOUT.SETTINGS' },
|
||||||
|
{ href: '/extendedSearch', icon: 'search', title: 'APP_LAYOUT.SEARCH' },
|
||||||
{ href: '/overlay-viewer', icon: 'pageview', title: 'APP_LAYOUT.OVERLAY_VIEWER' },
|
{ href: '/overlay-viewer', icon: 'pageview', title: 'APP_LAYOUT.OVERLAY_VIEWER' },
|
||||||
{ href: '/about', icon: 'info_outline', title: 'APP_LAYOUT.ABOUT' }
|
{ href: '/about', icon: 'info_outline', title: 'APP_LAYOUT.ABOUT' }
|
||||||
];
|
];
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { QueryBody } from 'alfresco-js-api';
|
||||||
|
import { SearchConfigurationInterface } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
export class TestSearchConfigurationService implements SearchConfigurationInterface {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody {
|
||||||
|
const defaultQueryBody: QueryBody = {
|
||||||
|
query: {
|
||||||
|
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||||
|
},
|
||||||
|
include: ['path', 'allowableOperations'],
|
||||||
|
paging: {
|
||||||
|
maxItems: maxResults,
|
||||||
|
skipCount: skipCount
|
||||||
|
},
|
||||||
|
filterQueries: [
|
||||||
|
/*tslint:disable-next-line */
|
||||||
|
{ query: "TYPE:'cm:folder'" },
|
||||||
|
{ query: 'NOT cm:creator:System' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
return defaultQueryBody;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<div>
|
||||||
|
<h3>{{'APP_LAYOUT.EXTENDED_SEARCH_QUERY_BODY' | translate}}</h3>
|
||||||
|
<mat-checkbox [(ngModel)]="useServiceApproach">
|
||||||
|
{{'APP_LAYOUT.SEARCH_SERVICE_APPROACH' | translate}}
|
||||||
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<div id="container-for-custom-input" class="search-extended-input-containers">
|
||||||
|
<mat-form-field>
|
||||||
|
<label>{{'APP_LAYOUT.WORD_TO_SEARCH' | translate}}</label>
|
||||||
|
<input type="text" matInput
|
||||||
|
id="custom-input" [(ngModel)]="searchedWord" [searchAutocomplete]="auto">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<adf-search #auto="searchAutocomplete"
|
||||||
|
[queryBody]="generateQueryBody(searchedWord)"
|
||||||
|
class="example-card-search-container">
|
||||||
|
<ng-template let-data>
|
||||||
|
<mat-card class="example-card"
|
||||||
|
*ngFor="let item of data?.list?.entries; let idx = index" (click)="onClick(item)">
|
||||||
|
<mat-card-header>
|
||||||
|
<div mat-card-avatar class="example-header-image"></div>
|
||||||
|
<mat-card-title>{{ item?.entry.name }}</mat-card-title>
|
||||||
|
<mat-card-subtitle>{{ item?.entry.createdAt }}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<img mat-card-image [src]="getMimeTypeIcon(item)">
|
||||||
|
<mat-card-content>
|
||||||
|
<p>
|
||||||
|
{{'APP_LAYOUT.SEARCH_CREATED_BY' | translate}}: {{item?.entry.createdByUser?.displayName}}
|
||||||
|
</p>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
<mat-card class="example-card" id="search_no_result" *ngIf="data?.list?.entries.length === 0">
|
||||||
|
<p mat-line class="adf-search-fixed-text">{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</p>
|
||||||
|
</mat-card>
|
||||||
|
</ng-template>
|
||||||
|
</adf-search>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,62 @@
|
|||||||
|
div.search-results-container {
|
||||||
|
padding: 0 20px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-search-title {
|
||||||
|
font-size: 22px;
|
||||||
|
padding: 15px 0 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
:host .col-display-name {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
:host .col-modified-at, :host .col-modified-by {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:host div.search-results-container table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-search-results-content{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-extended-input-containers {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-extended-input-textarea {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-extended-label-error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-card {
|
||||||
|
width: 200px;
|
||||||
|
flex: 0 20%;
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-card-search-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-header-image {
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-search-input {
|
||||||
|
width: 100px;
|
||||||
|
border: 1 solid;
|
||||||
|
border-color: black;
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { NodePaging, Pagination, QueryBody, MinimalNodeEntity } from 'alfresco-js-api';
|
||||||
|
import { SearchComponent } from '@alfresco/adf-content-services';
|
||||||
|
import { ThumbnailService } from '@alfresco/adf-core';
|
||||||
|
import { SearchService, SearchConfigurationService } from '@alfresco/adf-core';
|
||||||
|
import { TestSearchConfigurationService } from './search-config-test.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search-extended-component',
|
||||||
|
templateUrl: './search-extended.component.html',
|
||||||
|
styleUrls: ['./search-extended.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
providers: [
|
||||||
|
{ provide: SearchConfigurationService, useClass: TestSearchConfigurationService },
|
||||||
|
SearchService
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class SearchExtendedComponent {
|
||||||
|
|
||||||
|
@ViewChild('search')
|
||||||
|
search: SearchComponent;
|
||||||
|
|
||||||
|
queryParamName = 'q';
|
||||||
|
searchedWord = '';
|
||||||
|
queryBodyString = '';
|
||||||
|
errorMessage = '';
|
||||||
|
resultNodePageList: NodePaging;
|
||||||
|
maxItems: number;
|
||||||
|
skipCount = 0;
|
||||||
|
pagination: Pagination;
|
||||||
|
queryBody: QueryBody;
|
||||||
|
useServiceApproach = false;
|
||||||
|
|
||||||
|
constructor(public thumbnailService: ThumbnailService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getMimeTypeIcon(node: MinimalNodeEntity): string {
|
||||||
|
let mimeType;
|
||||||
|
|
||||||
|
if (node.entry.content && node.entry.content.mimeType) {
|
||||||
|
mimeType = node.entry.content.mimeType;
|
||||||
|
}
|
||||||
|
if (node.entry.isFolder) {
|
||||||
|
mimeType = 'folder';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.thumbnailService.getMimeTypeIcon(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateQueryBody(searchTerm: string): QueryBody {
|
||||||
|
if (this.useServiceApproach) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
query: {
|
||||||
|
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||||
|
},
|
||||||
|
include: ['path', 'allowableOperations'],
|
||||||
|
filterQueries: [
|
||||||
|
/*tslint:disable-next-line */
|
||||||
|
{ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" },
|
||||||
|
{ query: 'NOT cm:creator:System' }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,8 @@ Displays a input text which shows find-as-you-type suggestions.
|
|||||||
| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. |
|
| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. |
|
||||||
| liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. |
|
| liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. |
|
||||||
| liveSearchMaxResults | number | 5 | Maximum number of results to show in the live search. |
|
| liveSearchMaxResults | number | 5 | Maximum number of results to show in the live search. |
|
||||||
| customQueryBody | [QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) | | object which allow you to perform more elaborated query from the search api |
|
| customQueryBody | [QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) | | object which allow you to perform more elaborated query from the search api. This input is deprecated, to use the extended query body function please refer to the suggested solution [here](./search.component.md#querybody)|
|
||||||
|
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Searches items for supplied search terms.
|
|||||||
| maxResults | number | 20 | Maximum number of results to show in the search. |
|
| maxResults | number | 20 | Maximum number of results to show in the search. |
|
||||||
| skipResults | number | 0 | Number of results to skip from the results pagination. |
|
| skipResults | number | 0 | Number of results to skip from the results pagination. |
|
||||||
| displayWith | function | | Function that maps an option's value to its display value in the trigger |
|
| displayWith | function | | Function that maps an option's value to its display value in the trigger |
|
||||||
| queryBody | [QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) | | object which allow you to perform more elaborated query from the search api |
|
| queryBody| [QueryBody](https://github.com/Alfresco/alfresco-js-api/blob/1.6.0/src/alfresco-search-rest-api/docs/QueryBody.md) | | object which allow you to perform more elaborated query from the search api. This input is deprecated, to use the extended query body function please refer to the suggested solution [here](./search.component.md#querybody) |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@ -113,3 +113,60 @@ Yuo can do this by exporting the adf-search panel instance into a local template
|
|||||||
```
|
```
|
||||||
|
|
||||||
In this way it is possible to fetch the results from the word typed into the input text straight into the adf-search component via the custom template variable.
|
In this way it is possible to fetch the results from the word typed into the input text straight into the adf-search component via the custom template variable.
|
||||||
|
|
||||||
|
## QueryBody
|
||||||
|
This is an example on how you can provide your own class to generate your custom query body without giving it in input to the search component.
|
||||||
|
|
||||||
|
1. Service Class
|
||||||
|
You need to create your own service class which will implement the SearchConfigurationInterface this will force you to create the method generateQueryBody that is the one which needs to return the QueryBody object.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { QueryBody } from 'alfresco-js-api';
|
||||||
|
import { SearchConfigurationInterface } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
export class TestSearchConfigurationService implements SearchConfigurationInterface {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody {
|
||||||
|
const defaultQueryBody: QueryBody = {
|
||||||
|
query: {
|
||||||
|
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||||
|
},
|
||||||
|
include: ['path', 'allowableOperations'],
|
||||||
|
paging: {
|
||||||
|
maxItems: maxResults,
|
||||||
|
skipCount: skipCount
|
||||||
|
},
|
||||||
|
filterQueries: [
|
||||||
|
{ query: "TYPE:'cm:folder'" },
|
||||||
|
{ query: 'NOT cm:creator:System' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
return defaultQueryBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Provide your service class to the module
|
||||||
|
Once you have created your service class to provide your custom query body you need to inform the component to use your class instead of the default one. This can be easily achieved via your component providers :
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { SearchService, SearchConfigurationService } from '@alfresco/adf-core';
|
||||||
|
import { TestSearchConfigurationService } from './search-config-test.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search-extended-component',
|
||||||
|
templateUrl: './search-extended.component.html',
|
||||||
|
styleUrls: ['./search-extended.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
providers: [
|
||||||
|
{ provide: SearchConfigurationService, useClass: TestSearchConfigurationService },
|
||||||
|
SearchService
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
You need to add as provider even the SearchService to avoid the override of the module instance. So this component will have his own instance of the SearchService that will use as configuration the class you have provided.
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
typeToSearchBox('kakarot');
|
typeToSearchBox('kakarot');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot'));
|
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot'), '25', '0');
|
||||||
done();
|
done();
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
@ -325,7 +325,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
component.siteChanged(<SiteEntry> { entry: { guid: 'namek' } });
|
||||||
|
|
||||||
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
|
||||||
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek')]);
|
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek'), '25', '0'] );
|
||||||
done();
|
done();
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
@ -523,7 +523,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
|
|
||||||
component.getNextPageOfSearch({ skipCount });
|
component.getNextPageOfSearch({ skipCount });
|
||||||
|
|
||||||
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot', undefined, skipCount));
|
expect(searchSpy).toHaveBeenCalledWith(defaultSearchOptions('kakarot', undefined, skipCount), '25', skipCount.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be shown when pagination\'s hasMoreItems is true', () => {
|
it('should be shown when pagination\'s hasMoreItems is true', () => {
|
||||||
|
@ -59,6 +59,6 @@ export class ContentNodeSelectorService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.searchService.search(defaultSearchNode);
|
return this.searchService.search(defaultSearchNode, maxItems.toString(), skipCount.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
<adf-search #auto="searchAutocomplete"
|
<adf-search #auto="searchAutocomplete"
|
||||||
class="adf-search-result-autocomplete"
|
class="adf-search-result-autocomplete"
|
||||||
[queryBody]="customQueryBody"
|
[maxResults]="liveSearchMaxResults"
|
||||||
[maxResults]="liveSearchMaxResults">
|
[queryBody]="customQueryBody">
|
||||||
<ng-template let-data>
|
<ng-template let-data>
|
||||||
<mat-list *ngIf="isSearchBarActive()" id="autocomplete-search-result-list">
|
<mat-list *ngIf="isSearchBarActive()" id="autocomplete-search-result-list">
|
||||||
<mat-list-item
|
<mat-list-item
|
||||||
|
@ -63,6 +63,7 @@ export class SearchControlComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
liveSearchMaxResults: number = 5;
|
liveSearchMaxResults: number = 5;
|
||||||
|
|
||||||
|
/** @deprecated in 2.1.0 */
|
||||||
@Input()
|
@Input()
|
||||||
customQueryBody: QueryBody;
|
customQueryBody: QueryBody;
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ import { SearchModule } from '../../index';
|
|||||||
import { differentResult, folderResult, result, SimpleSearchTestComponent } from '../../mock';
|
import { differentResult, folderResult, result, SimpleSearchTestComponent } from '../../mock';
|
||||||
|
|
||||||
function fakeNodeResultSearch(searchNode: QueryBody): Observable<any> {
|
function fakeNodeResultSearch(searchNode: QueryBody): Observable<any> {
|
||||||
if (searchNode.query.query === 'FAKE_SEARCH_EXMPL') {
|
if (searchNode && searchNode.query.query === 'FAKE_SEARCH_EXMPL') {
|
||||||
return Observable.of(differentResult);
|
return Observable.of(differentResult);
|
||||||
}
|
}
|
||||||
if (searchNode.filterQueries.length === 1 &&
|
if (searchNode && searchNode.filterQueries.length === 1 &&
|
||||||
searchNode.filterQueries[0].query === "TYPE:'cm:folder'") {
|
searchNode.filterQueries[0].query === "TYPE:'cm:folder'") {
|
||||||
return Observable.of(folderResult);
|
return Observable.of(folderResult);
|
||||||
}
|
}
|
||||||
@ -132,18 +132,18 @@ describe('SearchComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should perform a search based on the query node given', async(() => {
|
it('should perform a search based on the query node given', async(() => {
|
||||||
spyOn(searchService, 'search')
|
spyOn(searchService, 'searchByQueryBody')
|
||||||
.and.callFake((searchObj) => fakeNodeResultSearch(searchObj));
|
.and.callFake((searchObj) => fakeNodeResultSearch(searchObj));
|
||||||
let fakeSearchNode: QueryBody = {
|
let fakeSearchNode: QueryBody = {
|
||||||
query: {
|
query: {
|
||||||
query: ''
|
query: 'TEST-FAKE-NODE'
|
||||||
},
|
},
|
||||||
filterQueries: [
|
filterQueries: [
|
||||||
{ 'query': "TYPE:'cm:folder'" }
|
{ 'query': "TYPE:'cm:folder'" }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
component.setSearchNodeTo(fakeSearchNode);
|
|
||||||
component.setSearchWordTo('searchTerm');
|
component.setSearchWordTo('searchTerm');
|
||||||
|
component.setSearchNodeTo(fakeSearchNode);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -156,7 +156,7 @@ describe('SearchComponent', () => {
|
|||||||
|
|
||||||
it('should perform a search with a defaultNode if no searchnode is given', async(() => {
|
it('should perform a search with a defaultNode if no searchnode is given', async(() => {
|
||||||
spyOn(searchService, 'search')
|
spyOn(searchService, 'search')
|
||||||
.and.callFake((searchObj) => fakeNodeResultSearch(searchObj));
|
.and.returnValue(Observable.of(result));
|
||||||
component.setSearchWordTo('searchTerm');
|
component.setSearchWordTo('searchTerm');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
@ -169,7 +169,7 @@ describe('SearchComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should perform a search with the searchNode given', async(() => {
|
it('should perform a search with the searchNode given', async(() => {
|
||||||
spyOn(searchService, 'search')
|
spyOn(searchService, 'searchByQueryBody')
|
||||||
.and.callFake((searchObj) => fakeNodeResultSearch(searchObj));
|
.and.callFake((searchObj) => fakeNodeResultSearch(searchObj));
|
||||||
let fakeSearchNode: QueryBody = {
|
let fakeSearchNode: QueryBody = {
|
||||||
query: {
|
query: {
|
||||||
@ -179,6 +179,7 @@ describe('SearchComponent', () => {
|
|||||||
{ 'query': "TYPE:'cm:folder'" }
|
{ 'query': "TYPE:'cm:folder'" }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
component.setSearchWordTo('searchTerm');
|
||||||
component.setSearchNodeTo(fakeSearchNode);
|
component.setSearchNodeTo(fakeSearchNode);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
import { SearchService } from '@alfresco/adf-core';
|
import { SearchService } from '@alfresco/adf-core';
|
||||||
import {
|
import {
|
||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
ChangeDetectionStrategy,
|
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
Component,
|
||||||
ContentChild,
|
ContentChild,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
@ -40,7 +38,6 @@ import { Subject } from 'rxjs/Subject';
|
|||||||
styleUrls: ['./search.component.scss'],
|
styleUrls: ['./search.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
preserveWhitespaces: false,
|
preserveWhitespaces: false,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
exportAs: 'searchAutocomplete',
|
exportAs: 'searchAutocomplete',
|
||||||
host: {
|
host: {
|
||||||
'class': 'adf-search'
|
'class': 'adf-search'
|
||||||
@ -63,12 +60,13 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
skipResults: number = 0;
|
skipResults: number = 0;
|
||||||
|
|
||||||
@Input()
|
/** @deprecated in 2.1.0 */
|
||||||
searchTerm: string = '';
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
queryBody: QueryBody;
|
queryBody: QueryBody;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
searchTerm: string = '';
|
||||||
|
|
||||||
@Input('class')
|
@Input('class')
|
||||||
set classList(classList: string) {
|
set classList(classList: string) {
|
||||||
if (classList && classList.length) {
|
if (classList && classList.length) {
|
||||||
@ -101,13 +99,13 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
_classList: { [key: string]: boolean } = {};
|
_classList: { [key: string]: boolean } = {};
|
||||||
|
|
||||||
constructor(private searchService: SearchService,
|
constructor(private searchService: SearchService,
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
|
||||||
private _elementRef: ElementRef) {
|
private _elementRef: ElementRef) {
|
||||||
this.keyPressedStream.asObservable()
|
this.keyPressedStream.asObservable()
|
||||||
.debounceTime(200)
|
.debounceTime(200)
|
||||||
.subscribe((searchedWord: string) => {
|
.subscribe((searchedWord: string) => {
|
||||||
this.loadSearchResults(searchedWord);
|
this.loadSearchResults(searchedWord);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit() {
|
ngAfterContentInit() {
|
||||||
@ -115,14 +113,12 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes) {
|
ngOnChanges(changes) {
|
||||||
this.resetResults();
|
if (changes.queryBody &&
|
||||||
|
this.hasDifferentQueryBody(changes.queryBody.previousValue, changes.queryBody.currentValue)) {
|
||||||
|
this.loadSearchResults();
|
||||||
|
}
|
||||||
if (changes.searchTerm && changes.searchTerm.currentValue) {
|
if (changes.searchTerm && changes.searchTerm.currentValue) {
|
||||||
this.loadSearchResults(changes.searchTerm.currentValue);
|
this.loadSearchResults(changes.searchTerm.currentValue);
|
||||||
} else if (changes.queryBody && changes.queryBody.currentValue) {
|
|
||||||
this.loadSearchResults();
|
|
||||||
} else {
|
|
||||||
this.loadSearchResults(this.searchTerm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,23 +131,27 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
this.loadSearchResults(this.searchTerm);
|
this.loadSearchResults(this.searchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasDifferentQueryBody(previousQueryBody: QueryBody, currentQueryBody: QueryBody) {
|
||||||
|
return JSON.stringify(previousQueryBody) !== JSON.stringify(currentQueryBody);
|
||||||
|
}
|
||||||
|
|
||||||
private cleanResults() {
|
private cleanResults() {
|
||||||
if (this.results) {
|
if (this.results) {
|
||||||
this.results = {};
|
this.results = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasValidSearchQuery(searchOpts: QueryBody) {
|
|
||||||
return searchOpts && searchOpts.query && searchOpts.query.query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadSearchResults(searchTerm?: string) {
|
private loadSearchResults(searchTerm?: string) {
|
||||||
let searchOpts: QueryBody = this.getQueryBody(searchTerm);
|
this.resetResults();
|
||||||
|
if (searchTerm) {
|
||||||
if (this.hasValidSearchQuery(searchOpts)) {
|
let search$;
|
||||||
this.searchService
|
if (this.queryBody) {
|
||||||
.search(searchOpts)
|
search$ = this.searchService.searchByQueryBody(this.queryBody);
|
||||||
.subscribe(
|
} else {
|
||||||
|
search$ = this.searchService
|
||||||
|
.search(searchTerm, this.maxResults.toString(), this.skipResults.toString());
|
||||||
|
}
|
||||||
|
search$.subscribe(
|
||||||
results => {
|
results => {
|
||||||
this.results = <NodePaging> results;
|
this.results = <NodePaging> results;
|
||||||
this.resultLoaded.emit(this.results);
|
this.resultLoaded.emit(this.results);
|
||||||
@ -169,41 +169,11 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getQueryBody(searchTerm: string): QueryBody {
|
|
||||||
if (this.queryBody) {
|
|
||||||
if (!this.queryBody.query.query && searchTerm) {
|
|
||||||
this.queryBody.query.query = searchTerm;
|
|
||||||
}
|
|
||||||
return this.queryBody;
|
|
||||||
} else {
|
|
||||||
return this.generateDefaultSearchNode(searchTerm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateDefaultSearchNode(searchTerm: string): QueryBody {
|
|
||||||
let defaultQueryBody: QueryBody = {
|
|
||||||
query: {
|
|
||||||
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
|
||||||
},
|
|
||||||
include: ['path', 'allowableOperations'],
|
|
||||||
paging: {
|
|
||||||
maxItems: this.maxResults.toString(),
|
|
||||||
skipCount: this.skipResults.toString()
|
|
||||||
},
|
|
||||||
filterQueries: [
|
|
||||||
{ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" },
|
|
||||||
{ query: 'NOT cm:creator:System' }]
|
|
||||||
};
|
|
||||||
|
|
||||||
return defaultQueryBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
hidePanel() {
|
hidePanel() {
|
||||||
if (this.isOpen) {
|
if (this.isOpen) {
|
||||||
this._classList['adf-search-show'] = false;
|
this._classList['adf-search-show'] = false;
|
||||||
this._classList['adf-search-hide'] = true;
|
this._classList['adf-search-hide'] = true;
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
this.changeDetectorRef.markForCheck();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +181,5 @@ export class SearchComponent implements AfterContentInit, OnChanges {
|
|||||||
this.showPanel = !!this.results && !!this.results.list;
|
this.showPanel = !!this.results && !!this.results.list;
|
||||||
this._classList['adf-search-show'] = this.showPanel;
|
this._classList['adf-search-show'] = this.showPanel;
|
||||||
this._classList['adf-search-hide'] = !this.showPanel;
|
this._classList['adf-search-hide'] = !this.showPanel;
|
||||||
this.changeDetectorRef.markForCheck();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,3 +17,4 @@
|
|||||||
|
|
||||||
export * from './authentication.interface';
|
export * from './authentication.interface';
|
||||||
export * from './injection.tokens';
|
export * from './injection.tokens';
|
||||||
|
export * from './search-configuration.interface';
|
||||||
|
24
lib/core/interface/search-configuration.interface.ts
Normal file
24
lib/core/interface/search-configuration.interface.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { QueryBody } from 'alfresco-js-api';
|
||||||
|
|
||||||
|
export interface SearchConfigurationInterface {
|
||||||
|
|
||||||
|
generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody;
|
||||||
|
|
||||||
|
}
|
@ -46,3 +46,4 @@ export * from './shared-links-api.service';
|
|||||||
export * from './sites.service';
|
export * from './sites.service';
|
||||||
export * from './discovery-api.service';
|
export * from './discovery-api.service';
|
||||||
export * from './comment-process.service';
|
export * from './comment-process.service';
|
||||||
|
export * from './search-configuration.service';
|
||||||
|
45
lib/core/services/search-configuration.service.ts
Normal file
45
lib/core/services/search-configuration.service.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { QueryBody } from 'alfresco-js-api';
|
||||||
|
import { SearchConfigurationInterface } from '../interface/search-configuration.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SearchConfigurationService implements SearchConfigurationInterface {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateQueryBody(searchTerm: string, maxResults: string, skipCount: string): QueryBody {
|
||||||
|
let defaultQueryBody: QueryBody = {
|
||||||
|
query: {
|
||||||
|
query: searchTerm ? `${searchTerm}* OR name:${searchTerm}*` : searchTerm
|
||||||
|
},
|
||||||
|
include: ['path', 'allowableOperations'],
|
||||||
|
paging: {
|
||||||
|
maxItems: maxResults,
|
||||||
|
skipCount: skipCount
|
||||||
|
},
|
||||||
|
filterQueries: [
|
||||||
|
{ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" },
|
||||||
|
{ query: 'NOT cm:creator:System' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
return defaultQueryBody;
|
||||||
|
}
|
||||||
|
}
|
@ -21,15 +21,14 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
import { AlfrescoApiService } from './alfresco-api.service';
|
import { AlfrescoApiService } from './alfresco-api.service';
|
||||||
import { AuthenticationService } from './authentication.service';
|
import { AuthenticationService } from './authentication.service';
|
||||||
import 'rxjs/add/observable/throw';
|
import 'rxjs/add/observable/throw';
|
||||||
|
import { SearchConfigurationService } from './search-configuration.service';
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal service used by Document List component.
|
|
||||||
*/
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SearchService {
|
export class SearchService {
|
||||||
|
|
||||||
constructor(public authService: AuthenticationService,
|
constructor(public authService: AuthenticationService,
|
||||||
private apiService: AlfrescoApiService) {
|
private apiService: AlfrescoApiService,
|
||||||
|
private searchConfigurationService: SearchConfigurationService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> {
|
getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> {
|
||||||
@ -38,8 +37,8 @@ export class SearchService {
|
|||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query: QueryBody): Observable<NodePaging> {
|
search(searchTerm: string, maxResults: string, skipCount: string): Observable<NodePaging> {
|
||||||
const searchQuery = Object.assign(query);
|
const searchQuery = Object.assign(this.searchConfigurationService.generateQueryBody(searchTerm, maxResults, skipCount));
|
||||||
const promise = this.apiService.getInstance().search.searchApi.search(searchQuery);
|
const promise = this.apiService.getInstance().search.searchApi.search(searchQuery);
|
||||||
|
|
||||||
return Observable
|
return Observable
|
||||||
@ -47,6 +46,14 @@ export class SearchService {
|
|||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchByQueryBody(queryBody: QueryBody): Observable<NodePaging> {
|
||||||
|
const promise = this.apiService.getInstance().search.searchApi.search(queryBody);
|
||||||
|
|
||||||
|
return Observable
|
||||||
|
.fromPromise(promise)
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
|
}
|
||||||
|
|
||||||
private handleError(error: any): Observable<any> {
|
private handleError(error: any): Observable<any> {
|
||||||
return Observable.throw(error || 'Server error');
|
return Observable.throw(error || 'Server error');
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ import { TranslateLoaderService } from './translate-loader.service';
|
|||||||
import { TranslationService } from './translation.service';
|
import { TranslationService } from './translation.service';
|
||||||
import { UploadService } from './upload.service';
|
import { UploadService } from './upload.service';
|
||||||
import { UserPreferencesService } from './user-preferences.service';
|
import { UserPreferencesService } from './user-preferences.service';
|
||||||
|
import { SearchConfigurationService } from './search-configuration.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [],
|
imports: [],
|
||||||
@ -81,7 +82,8 @@ import { UserPreferencesService } from './user-preferences.service';
|
|||||||
SharedLinksApiService,
|
SharedLinksApiService,
|
||||||
SitesService,
|
SitesService,
|
||||||
DiscoveryApiService,
|
DiscoveryApiService,
|
||||||
CommentProcessService
|
CommentProcessService,
|
||||||
|
SearchConfigurationService
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user