From b2c002057e720aef2f37026a13e3a5768779f2ec Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Wed, 23 Jun 2021 11:05:14 +0200 Subject: [PATCH 01/16] 80195: Replace input suggestions with collection selector --- .../item-move/item-move.component.html | 23 ++++++++----------- .../item-move/item-move.component.ts | 20 +++------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.html b/src/app/+item-page/edit-item-page/item-move/item-move.component.html index 74ca9aae4e..f2a420204f 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.html +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.html @@ -5,19 +5,16 @@

{{'item.edit.move.description' | translate}}

- - - +
+
{{'dso-selector.placeholder' | translate: { type: 'collection' } }}
+
+ + +
+
+
diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts index b1ed121b40..2e2d94b80d 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts @@ -53,6 +53,8 @@ export class ItemMoveComponent implements OnInit { */ itemPageRoute$: Observable; + COLLECTIONS = [DSpaceObjectType.COLLECTION]; + constructor(private route: ActivatedRoute, private router: Router, private notificationsService: NotificationsService, @@ -75,14 +77,6 @@ export class ItemMoveComponent implements OnInit { this.loadSuggestions(''); } - /** - * Find suggestions based on entered query - * @param query - Search query - */ - findSuggestions(query): void { - this.loadSuggestions(query); - } - /** * Load all available collections to move the item to. * TODO: When the API support it, only fetch collections where user has ADD rights to. @@ -107,7 +101,7 @@ export class ItemMoveComponent implements OnInit { * Set the collection name and id based on the selected value * @param data - obtained from the ds-input-suggestions component */ - onClick(data: any): void { + selectDso(data: any): void { this.selectedCollection = data; this.selectedCollectionName = data.name; this.canSubmit = true; @@ -137,12 +131,4 @@ export class ItemMoveComponent implements OnInit { } ); } - - /** - * Resets the can submit when the user changes the content of the input field - * @param data - */ - resetCollection(data: any) { - this.canSubmit = false; - } } From 1407ea85d72f7adab191c66c38b30b81ebe574fc Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Wed, 23 Jun 2021 11:29:11 +0200 Subject: [PATCH 02/16] 80195: Fix move request handling --- .../item-move/item-move.component.ts | 40 +++---------------- src/app/core/data/item-data.service.ts | 26 +++++++----- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts index 2e2d94b80d..c80b0f7477 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts @@ -1,24 +1,18 @@ import { Component, OnInit } from '@angular/core'; -import { first, map } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; import { RemoteData } from '../../../core/data/remote-data'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Item } from '../../../core/shared/item.model'; import { ActivatedRoute, Router } from '@angular/router'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { - getFirstSucceededRemoteData, - getFirstCompletedRemoteData, getAllSucceededRemoteDataPayload + getAllSucceededRemoteDataPayload, getFirstCompletedRemoteData, getFirstSucceededRemoteData, } from '../../../core/shared/operators'; import { ItemDataService } from '../../../core/data/item-data.service'; import { Observable, of as observableOf } from 'rxjs'; import { Collection } from '../../../core/shared/collection.model'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchService } from '../../../core/shared/search/search.service'; -import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; -import { SearchResult } from '../../../shared/search/search-result.model'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; @Component({ @@ -46,8 +40,6 @@ export class ItemMoveComponent implements OnInit { item: Item; processing = false; - pagination = new PaginationComponentOptions(); - /** * Route to the item's page */ @@ -73,28 +65,6 @@ export class ItemMoveComponent implements OnInit { this.item = rd.payload; } ); - this.pagination.pageSize = 5; - this.loadSuggestions(''); - } - - /** - * Load all available collections to move the item to. - * TODO: When the API support it, only fetch collections where user has ADD rights to. - */ - loadSuggestions(query): void { - this.collectionSearchResults = this.searchService.search(new PaginatedSearchOptions({ - pagination: this.pagination, - dsoTypes: [DSpaceObjectType.COLLECTION], - query: query - })).pipe( - first(), - map((rd: RemoteData>>) => { - return rd.payload.page.map((searchResult) => { - return searchResult.indexableObject; - }); - }) , - ); - } /** @@ -119,8 +89,10 @@ export class ItemMoveComponent implements OnInit { */ moveCollection() { this.processing = true; - this.itemDataService.moveToCollection(this.item.id, this.selectedCollection).pipe(getFirstCompletedRemoteData()).subscribe( - (response: RemoteData) => { + this.itemDataService.moveToCollection(this.item.id, this.selectedCollection) + .pipe(getFirstCompletedRemoteData()) + .subscribe( + (response: RemoteData) => { this.router.navigate([getItemEditRoute(this.item)]); if (response.hasSucceeded) { this.notificationsService.success(this.translateService.get('item.edit.move.success')); diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index e56f9f2b0c..7a0116fe86 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -23,14 +23,7 @@ import { DataService } from './data.service'; import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; import { PaginatedList } from './paginated-list.model'; import { RemoteData } from './remote-data'; -import { - DeleteRequest, - FindListOptions, - GetRequest, - PostRequest, - PutRequest, - RestRequest -} from './request.models'; +import { DeleteRequest, FindListOptions, GetRequest, PostRequest, PutRequest, RestRequest } from './request.models'; import { RequestService } from './request.service'; import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model'; import { Bundle } from '../shared/bundle.model'; @@ -38,6 +31,9 @@ import { MetadataMap } from '../shared/metadata.models'; import { BundleDataService } from './bundle-data.service'; import { Operation } from 'fast-json-patch'; import { NoContent } from '../shared/NoContent.model'; +import { GenericConstructor } from '../shared/generic-constructor'; +import { ResponseParsingService } from './parsing.service'; +import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service'; @Injectable() @dataService(ITEM) @@ -229,7 +225,7 @@ export class ItemDataService extends DataService { * @param itemId * @param collection */ - public moveToCollection(itemId: string, collection: Collection): Observable> { + public moveToCollection(itemId: string, collection: Collection): Observable> { const options: HttpOptions = Object.create({}); let headers = new HttpHeaders(); headers = headers.append('Content-Type', 'text/uri-list'); @@ -242,9 +238,17 @@ export class ItemDataService extends DataService { find((href: string) => hasValue(href)), map((href: string) => { const request = new PutRequest(requestId, href, collection._links.self.href, options); - this.requestService.send(request); + Object.assign(request, { + // TODO: for now, the move Item endpoint returns a malformed collection -- only look at the status code + getResponseParser(): GenericConstructor { + return StatusCodeOnlyResponseParsingService; + } + }); + return request; }) - ).subscribe(); + ).subscribe((request) => { + this.requestService.send(request); + }); return this.rdbService.buildFromRequestUUID(requestId); } From 0ac9d581948c808d0408f6fd0083ec5327412075 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Wed, 23 Jun 2021 11:33:25 +0200 Subject: [PATCH 03/16] 80195: Update ItemMoveComponent unit tests --- .../item-move/item-move.component.spec.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts index dd91c65e1e..b3178793e3 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts @@ -98,24 +98,14 @@ describe('ItemMoveComponent', () => { comp = fixture.componentInstance; fixture.detectChanges(); }); - it('should load suggestions', () => { - const expected = [ - collection1, - collection2 - ]; - comp.collectionSearchResults.subscribe((value) => { - expect(value).toEqual(expected); - } - ); - }); it('should get current url ', () => { expect(comp.getCurrentUrl()).toEqual('fake-url/fake-id/edit'); }); - it('should on click select the correct collection name and id', () => { + it('should select the correct collection name and id on click', () => { const data = collection1; - comp.onClick(data); + comp.selectDso(data); expect(comp.selectedCollectionName).toEqual('Test collection 1'); expect(comp.selectedCollection).toEqual(collection1); From 2c19d80a16ffb25bad4b07d10b3dcbbe1ff34b8b Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Wed, 23 Jun 2021 11:57:16 +0200 Subject: [PATCH 04/16] 80195: Refresh moved Item's owningCollection before redirecting --- .../edit-item-page/item-move/item-move.component.spec.ts | 6 ++++-- .../edit-item-page/item-move/item-move.component.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts index b3178793e3..2a51dcc06d 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts @@ -48,11 +48,13 @@ describe('ItemMoveComponent', () => { }); const mockItemDataService = jasmine.createSpyObj({ - moveToCollection: createSuccessfulRemoteDataObject$(collection1) + moveToCollection: createSuccessfulRemoteDataObject$(collection1), + findById: createSuccessfulRemoteDataObject$(mockItem), }); const mockItemDataServiceFail = jasmine.createSpyObj({ - moveToCollection: createFailedRemoteDataObject$('Internal server error', 500) + moveToCollection: createFailedRemoteDataObject$('Internal server error', 500), + findById: createSuccessfulRemoteDataObject$(mockItem), }); const routeStub = { diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts index c80b0f7477..9c6670ef26 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts @@ -14,6 +14,7 @@ import { Observable, of as observableOf } from 'rxjs'; import { Collection } from '../../../core/shared/collection.model'; import { SearchService } from '../../../core/shared/search/search.service'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; +import { followLink } from '../../../shared/utils/follow-link-config.model'; @Component({ selector: 'ds-item-move', @@ -93,13 +94,17 @@ export class ItemMoveComponent implements OnInit { .pipe(getFirstCompletedRemoteData()) .subscribe( (response: RemoteData) => { - this.router.navigate([getItemEditRoute(this.item)]); + this.itemDataService.findById( + this.item.id, false, true, followLink('owningCollection', undefined, true, false) + ).subscribe(() => { + this.processing = false; + this.router.navigate([getItemEditRoute(this.item)]); + }); if (response.hasSucceeded) { this.notificationsService.success(this.translateService.get('item.edit.move.success')); } else { this.notificationsService.error(this.translateService.get('item.edit.move.error')); } - this.processing = false; } ); } From f683d1219e4a3415f18b2eb8e19112d2ccd14728 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 23 Jun 2021 13:43:10 +0200 Subject: [PATCH 05/16] make truncatable css global to ensure it adapts to the theme --- .../truncatable-part.component.scss | 79 ------------------- src/styles/_truncatable-part.component.scss | 78 ++++++++++++++++++ src/styles/base-theme.scss | 1 + src/themes/custom/styles/theme.scss | 1 + src/themes/dspace/styles/theme.scss | 1 + 5 files changed, 81 insertions(+), 79 deletions(-) create mode 100644 src/styles/_truncatable-part.component.scss diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss index 186bb9be48..e69de29bb2 100644 --- a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss @@ -1,79 +0,0 @@ -//TODO switch to css variables -@mixin clamp($lines, $bg, $size-factor: 1, $line-height: $line-height-base) { - $height: $line-height * $font-size-base * $size-factor; - &.fixedHeight { - height: $lines * $height; - } - .content { - max-height: $lines * $height; - position: relative; - overflow: hidden; - line-height: $line-height; - overflow-wrap: break-word; - &:after { - content: ""; - position: absolute; - padding-right: 15px; - top: ($lines - 1) * $height; - right: 0; - width: 30%; - min-width: 75px; - max-width: 150px; - height: $height; - background: linear-gradient(to right, rgba(255, 255, 255, 0), $bg 70%); - pointer-events: none; - } - } - -} - -@mixin min($lines, $size-factor: 1, $line-height: $line-height-base) { - $height: $line-height * $font-size-base * $size-factor; - min-height: $lines * $height; -} - -$h4-factor: strip-unit($h4-font-size); - -@mixin clamp-with-titles($i, $bg) { - transition: height 1s; - @include clamp($i, $bg); - &.title { - @include clamp($i, $bg, 1.25); - } - &.h4 { - @include clamp($i, $bg, $h4-factor, $headings-line-height); - } -} - -@for $i from 1 through 15 { - .clamp-default-#{$i} { - @include clamp-with-titles($i, $body-bg); - } - :host-context(.ds-hover) .clamp-default-#{$i} { - @include clamp-with-titles($i, $list-group-hover-bg); - } - - .clamp-primary-#{$i} { - @include clamp-with-titles($i, $primary); - } - - :host-context(.ds-hover) .clamp-primary-#{$i} { - @include clamp-with-titles($i, darken($primary, 10%)); - } -} - -.clamp-none { - overflow: hidden; - @for $i from 1 through 15 { - &.fixedHeight.min-#{$i} { - transition: height 1s; - @include min($i); - &.title { - @include min($i, 1.25); - } - &.h4 { - @include min($i, $h4-factor, $headings-line-height); - } - } - } -} diff --git a/src/styles/_truncatable-part.component.scss b/src/styles/_truncatable-part.component.scss new file mode 100644 index 0000000000..5c04700aa6 --- /dev/null +++ b/src/styles/_truncatable-part.component.scss @@ -0,0 +1,78 @@ +@mixin clamp($lines, $bg, $size-factor: 1, $line-height: $line-height-base) { + $height: $line-height * $font-size-base * $size-factor; + &.fixedHeight { + height: $lines * $height; + } + .content { + max-height: $lines * $height; + position: relative; + overflow: hidden; + line-height: $line-height; + overflow-wrap: break-word; + &:after { + content: ""; + position: absolute; + padding-right: 15px; + top: ($lines - 1) * $height; + right: 0; + width: 30%; + min-width: 75px; + max-width: 150px; + height: $height; + background: linear-gradient(to right, rgba(255, 255, 255, 0), $bg 70%); + pointer-events: none; + } + } + +} + +@mixin min($lines, $size-factor: 1, $line-height: $line-height-base) { + $height: $line-height * $font-size-base * $size-factor; + min-height: $lines * $height; +} + +$h4-factor: strip-unit($h4-font-size); + +@mixin clamp-with-titles($i, $bg) { + transition: height 1s; + @include clamp($i, $bg); + &.title { + @include clamp($i, $bg, 1.25); + } + &.h4 { + @include clamp($i, $bg, $h4-factor, $headings-line-height); + } +} + +@for $i from 1 through 15 { + .clamp-default-#{$i} { + @include clamp-with-titles($i, $body-bg); + } + .ds-hover .clamp-default-#{$i} { + @include clamp-with-titles($i, $list-group-hover-bg); + } + + .clamp-primary-#{$i} { + @include clamp-with-titles($i, $primary); + } + + .ds-hover .clamp-primary-#{$i} { + @include clamp-with-titles($i, darken($primary, 10%)); + } +} + +.clamp-none { + overflow: hidden; + @for $i from 1 through 15 { + &.fixedHeight.min-#{$i} { + transition: height 1s; + @include min($i); + &.title { + @include min($i, 1.25); + } + &.h4 { + @include min($i, $h4-factor, $headings-line-height); + } + } + } +} diff --git a/src/styles/base-theme.scss b/src/styles/base-theme.scss index 068c2ece26..bde50bcfd7 100644 --- a/src/styles/base-theme.scss +++ b/src/styles/base-theme.scss @@ -3,4 +3,5 @@ @import '../../node_modules/nouislider/distribute/nouislider.min'; @import './_custom_variables.scss'; @import './bootstrap_variables_mapping.scss'; +@import './_truncatable-part.component.scss'; @import './_global-styles.scss'; diff --git a/src/themes/custom/styles/theme.scss b/src/themes/custom/styles/theme.scss index e4cc9e45ed..35810b15a6 100644 --- a/src/themes/custom/styles/theme.scss +++ b/src/themes/custom/styles/theme.scss @@ -9,4 +9,5 @@ @import '../../../styles/_custom_variables.scss'; @import './_theme_css_variable_overrides.scss'; @import '../../../styles/bootstrap_variables_mapping.scss'; +@import '../../../styles/_truncatable-part.component.scss'; @import './_global-styles.scss'; diff --git a/src/themes/dspace/styles/theme.scss b/src/themes/dspace/styles/theme.scss index e4cc9e45ed..35810b15a6 100644 --- a/src/themes/dspace/styles/theme.scss +++ b/src/themes/dspace/styles/theme.scss @@ -9,4 +9,5 @@ @import '../../../styles/_custom_variables.scss'; @import './_theme_css_variable_overrides.scss'; @import '../../../styles/bootstrap_variables_mapping.scss'; +@import '../../../styles/_truncatable-part.component.scss'; @import './_global-styles.scss'; From 86cd7ba03f89e7d0a35582ce9abf40e7c75d3783 Mon Sep 17 00:00:00 2001 From: Yura Bondarenko Date: Wed, 23 Jun 2021 15:29:24 +0200 Subject: [PATCH 06/16] 80195: Select original owningCollection on load --- .../item-move/item-move.component.html | 4 ++-- .../item-move/item-move.component.ts | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.html b/src/app/+item-page/edit-item-page/item-move/item-move.component.html index f2a420204f..9c9d6e6ceb 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.html +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.html @@ -9,7 +9,7 @@
{{'dso-selector.placeholder' | translate: { type: 'collection' } }}
@@ -30,7 +30,7 @@
- - +
+
+ + + +
+
diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts index 918f9e9a2c..d7e44411a2 100644 --- a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts +++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts @@ -121,6 +121,11 @@ export class ItemMoveComponent implements OnInit { ); } + discard(): void { + this.selectedCollection = null; + this.canSubmit = false; + } + get canMove(): boolean { return this.canSubmit && this.selectedCollection?.id !== this.originalCollection.id; } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5bd08dfbc8..2d3c970638 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1623,7 +1623,11 @@ - "item.edit.move.cancel": "Cancel", + "item.edit.move.cancel": "Back", + + "item.edit.move.save-button": "Save", + + "item.edit.move.discard-button": "Discard", "item.edit.move.description": "Select the collection you wish to move this item to. To narrow down the list of displayed collections, you can enter a search query in the box.", From b586a264ca06de1ccbedb68fefa35323bf25d383 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 23 Jun 2021 17:27:01 +0200 Subject: [PATCH 08/16] ensure dynamic components are updated when their inputs change --- ...table-object-component-loader.component.ts | 77 ++++++++++++++----- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 30ad91c1e2..4c6206cb43 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -3,10 +3,14 @@ import { ComponentFactoryResolver, ElementRef, Input, - OnDestroy, OnInit, - Output, ViewChild -, - EventEmitter + OnDestroy, + OnInit, + Output, + ViewChild, + EventEmitter, + SimpleChanges, + OnChanges, + ComponentRef } from '@angular/core'; import { ListableObject } from '../listable-object.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; @@ -15,7 +19,7 @@ import { getListableObjectComponent } from './listable-object.decorator'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { ListableObjectDirective } from './listable-object.directive'; import { CollectionElementLinkType } from '../../collection-element-link.type'; -import { hasValue } from '../../../empty.util'; +import { hasValue, isNotEmpty } from '../../../empty.util'; import { Subscription } from 'rxjs/internal/Subscription'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { take } from 'rxjs/operators'; @@ -29,7 +33,7 @@ import { ThemeService } from '../../../theme-support/theme.service'; /** * Component for determining what component to use depending on the item's entity type (dspace.entity.type) */ -export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy { +export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges, OnDestroy { /** * The item or metadata to determine the component for */ @@ -107,6 +111,25 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy */ protected subs: Subscription[] = []; + /** + * The reference to the dynamic component + */ + protected compRef: ComponentRef; + + /** + * The list of input and output names for the dynamic component + */ + protected inAndOutputNames: string[] = [ + 'object', + 'index', + 'linkType', + 'listID', + 'showLabel', + 'context', + 'viewMode', + 'value', + ]; + constructor( private componentFactoryResolver: ComponentFactoryResolver, private themeService: ThemeService @@ -120,6 +143,15 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy this.instantiateComponent(this.object); } + /** + * Whenever the inputs change, update the inputs of the dynamic component + */ + ngOnChanges(changes: SimpleChanges): void { + if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + this.connectInputsAndOutputs(); + } + } + ngOnDestroy() { this.subs .filter((subscription) => hasValue(subscription)) @@ -137,28 +169,22 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy const viewContainerRef = this.listableObjectDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent( + this.compRef = viewContainerRef.createComponent( componentFactory, 0, undefined, [ [this.badges.nativeElement], ]); - (componentRef.instance as any).object = object; - (componentRef.instance as any).index = this.index; - (componentRef.instance as any).linkType = this.linkType; - (componentRef.instance as any).listID = this.listID; - (componentRef.instance as any).showLabel = this.showLabel; - (componentRef.instance as any).context = this.context; - (componentRef.instance as any).viewMode = this.viewMode; - (componentRef.instance as any).value = this.value; - if ((componentRef.instance as any).reloadedObject) { - (componentRef.instance as any).reloadedObject.pipe(take(1)).subscribe((reloadedObject: DSpaceObject) => { + this.connectInputsAndOutputs(); + + if ((this.compRef.instance as any).reloadedObject) { + (this.compRef.instance as any).reloadedObject.pipe(take(1)).subscribe((reloadedObject: DSpaceObject) => { if (reloadedObject) { - componentRef.destroy(); + this.compRef.destroy(); this.object = reloadedObject; - this.instantiateComponent(reloadedObject); + this.connectInputsAndOutputs(); this.contentChange.emit(reloadedObject); } }); @@ -187,4 +213,17 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnDestroy context: Context): GenericConstructor { return getListableObjectComponent(renderTypes, viewMode, context, this.themeService.getThemeName()); } + + /** + * Connect the in and outputs of this component to the dynamic component, + * to ensure they're in sync + */ + protected connectInputsAndOutputs(): void { + if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.inAndOutputNames.forEach((name: any) => { + this.compRef.instance[name] = this[name]; + }); + } + } + } From 2599068ccd1eb511b51c4afc4639e7e388c89c4d Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 23 Jun 2021 17:27:23 +0200 Subject: [PATCH 09/16] add a focus style for truncatable parts --- src/styles/_truncatable-part.component.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/styles/_truncatable-part.component.scss b/src/styles/_truncatable-part.component.scss index 5c04700aa6..b938f3a199 100644 --- a/src/styles/_truncatable-part.component.scss +++ b/src/styles/_truncatable-part.component.scss @@ -48,6 +48,7 @@ $h4-factor: strip-unit($h4-font-size); .clamp-default-#{$i} { @include clamp-with-titles($i, $body-bg); } + :focus .clamp-default-#{$i}, .ds-hover .clamp-default-#{$i} { @include clamp-with-titles($i, $list-group-hover-bg); } @@ -56,6 +57,7 @@ $h4-factor: strip-unit($h4-font-size); @include clamp-with-titles($i, $primary); } + :focus .clamp-primary-#{$i}, .ds-hover .clamp-primary-#{$i} { @include clamp-with-titles($i, darken($primary, 10%)); } From 04f4a2587035f1d1892e946a663071b2f8b6ca13 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 23 Jun 2021 17:28:36 +0200 Subject: [PATCH 10/16] fix an issue where the results of a dso-selector wouldn't update until you clicked anywhere on the page --- .../dso-selector/dso-selector.component.html | 6 +++--- .../dso-selector/dso-selector.component.ts | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 122f37b031..ab2ea6cd8b 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -14,12 +14,12 @@ [infiniteScrollContainer]="'.scrollable-menu'" [fromRoot]="true" (scrolled)="onScrollDown()"> - + - -