Skip to content
Snippets Groups Projects
Commit f83e31a1 authored by Kristof De Langhe's avatar Kristof De Langhe
Browse files

Merge remote-tracking branch 'dspace/master' into w2p-62589_Item-mapper-update

Conflicts:
	src/app/core/data/collection-data.service.ts
parents a87dc89f eea25066
Branches
Tags
No related merge requests found
Showing
with 232 additions and 64 deletions
......@@ -3,7 +3,8 @@
<div>
<img class="mb-4 login-logo" src="assets/images/dspace-logo.png">
<h1 class="h3 mb-0 font-weight-normal">{{"login.form.header" | translate}}</h1>
<ds-log-in></ds-log-in>
<ds-log-in
[isStandalonePage]="true"></ds-log-in>
</div>
</div>
</div>
......@@ -81,7 +81,6 @@ const components = [
SearchFilterService,
SearchFixedFilterService,
ConfigurationSearchPageGuard,
SearchFilterService,
SearchConfigurationService
],
entryComponents: [
......
......@@ -23,11 +23,14 @@ import { AppState } from '../../app.reducer';
import { ClientCookieService } from '../services/client-cookie.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
import { routeServiceStub } from '../../shared/testing/route-service-stub';
import { RouteService } from '../services/route.service';
describe('AuthService test', () => {
let mockStore: Store<AuthState>;
let authService: AuthService;
let routeServiceMock: RouteService;
let authRequest;
let window;
let routerStub;
......@@ -74,6 +77,7 @@ describe('AuthService test', () => {
{ provide: NativeWindowService, useValue: window },
{ provide: REQUEST, useValue: {} },
{ provide: Router, useValue: routerStub },
{ provide: RouteService, useValue: routeServiceStub },
{ provide: ActivatedRoute, useValue: routeStub },
{ provide: Store, useValue: mockStore },
{ provide: RemoteDataBuildService, useValue: rdbService },
......@@ -138,6 +142,7 @@ describe('AuthService test', () => {
{ provide: AuthRequestService, useValue: authRequest },
{ provide: REQUEST, useValue: {} },
{ provide: Router, useValue: routerStub },
{ provide: RouteService, useValue: routeServiceStub },
{ provide: RemoteDataBuildService, useValue: rdbService },
CookieService,
AuthService
......@@ -145,13 +150,13 @@ describe('AuthService test', () => {
}).compileComponents();
}));
beforeEach(inject([CookieService, AuthRequestService, Store, Router], (cookieService: CookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router) => {
beforeEach(inject([CookieService, AuthRequestService, Store, Router, RouteService], (cookieService: CookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router, routeService: RouteService) => {
store
.subscribe((state) => {
(state as any).core = Object.create({});
(state as any).core.auth = authenticatedState;
});
authService = new AuthService({}, window, undefined, authReqService, router, cookieService, store, rdbService);
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
}));
it('should return true when user is logged in', () => {
......@@ -189,6 +194,7 @@ describe('AuthService test', () => {
{ provide: AuthRequestService, useValue: authRequest },
{ provide: REQUEST, useValue: {} },
{ provide: Router, useValue: routerStub },
{ provide: RouteService, useValue: routeServiceStub },
{ provide: RemoteDataBuildService, useValue: rdbService },
ClientCookieService,
CookieService,
......@@ -197,7 +203,7 @@ describe('AuthService test', () => {
}).compileComponents();
}));
beforeEach(inject([ClientCookieService, AuthRequestService, Store, Router], (cookieService: ClientCookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router) => {
beforeEach(inject([ClientCookieService, AuthRequestService, Store, Router, RouteService], (cookieService: ClientCookieService, authReqService: AuthRequestService, store: Store<AppState>, router: Router, routeService: RouteService) => {
const expiredToken: AuthTokenInfo = new AuthTokenInfo('test_token');
expiredToken.expires = Date.now() - (1000 * 60 * 60);
authenticatedState = {
......@@ -212,11 +218,14 @@ describe('AuthService test', () => {
(state as any).core = Object.create({});
(state as any).core.auth = authenticatedState;
});
authService = new AuthService({}, window, undefined, authReqService, router, cookieService, store, rdbService);
authService = new AuthService({}, window, undefined, authReqService, router, routeService, cookieService, store, rdbService);
storage = (authService as any).storage;
routeServiceMock = TestBed.get(RouteService);
routerStub = TestBed.get(Router);
spyOn(storage, 'get');
spyOn(storage, 'remove');
spyOn(storage, 'set');
}));
it('should throw false when token is not valid', () => {
......@@ -238,5 +247,32 @@ describe('AuthService test', () => {
expect(storage.remove).toHaveBeenCalled();
});
it ('should set redirect url to previous page', () => {
spyOn(routeServiceMock, 'getHistory').and.callThrough();
authService.redirectAfterLoginSuccess(true);
expect(routeServiceMock.getHistory).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith(['/collection/123']);
});
it ('should set redirect url to current page', () => {
spyOn(routeServiceMock, 'getHistory').and.callThrough();
authService.redirectAfterLoginSuccess(false);
expect(routeServiceMock.getHistory).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith(['/home']);
});
it ('should redirect to / and not to /login', () => {
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['/login', '/login']));
authService.redirectAfterLoginSuccess(true);
expect(routeServiceMock.getHistory).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith(['/']);
});
it ('should redirect to / when no redirect url is found', () => {
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['']));
authService.redirectAfterLoginSuccess(true);
expect(routeServiceMock.getHistory).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith(['/']);
});
});
});
......@@ -22,6 +22,7 @@ import { ResetAuthenticationMessagesAction, SetRedirectUrlAction } from './auth.
import { NativeWindowRef, NativeWindowService } from '../services/window.service';
import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import {RouteService} from '../services/route.service';
export const LOGIN_ROUTE = '/login';
export const LOGOUT_ROUTE = '/logout';
......@@ -45,6 +46,7 @@ export class AuthService {
protected authRequestService: AuthRequestService,
@Optional() @Inject(RESPONSE) private response: any,
protected router: Router,
protected routeService: RouteService,
protected storage: CookieService,
protected store: Store<AppState>,
protected rdbService: RemoteDataBuildService
......@@ -337,7 +339,7 @@ export class AuthService {
/**
* Redirect to the route navigated before the login
*/
public redirectToPreviousUrl() {
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
this.getRedirectUrl().pipe(
take(1))
.subscribe((redirectUrl) => {
......@@ -346,18 +348,39 @@ export class AuthService {
this.clearRedirectUrl();
this.router.onSameUrlNavigation = 'reload';
const url = decodeURIComponent(redirectUrl);
this.router.navigateByUrl(url);
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
// this._window.nativeWindow.location.href = url;
this.navigateToRedirectUrl(url);
} else {
this.router.navigate(['/']);
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
// this._window.nativeWindow.location.href = '/';
// If redirectUrl is empty use history.
this.routeService.getHistory().pipe(
take(1)
).subscribe((history) => {
let redirUrl;
if (isStandalonePage) {
// For standalone login pages, use the previous route.
redirUrl = history[history.length - 2] || '';
} else {
redirUrl = history[history.length - 1] || '';
}
this.navigateToRedirectUrl(redirUrl);
});
}
})
});
}
protected navigateToRedirectUrl(url: string) {
// in case the user navigates directly to /login (via bookmark, etc), or the route history is not found.
if (isEmpty(url) || url.startsWith(LOGIN_ROUTE)) {
this.router.navigate(['/']);
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
// this._window.nativeWindow.location.href = '/';
} else {
/* TODO Reenable hard redirect when REST API can handle x-forwarded-for, see https://github.com/DSpace/DSpace/pull/2207 */
// this._window.nativeWindow.location.href = url;
this.router.navigate([url]);
}
}
/**
* Refresh route navigated
*/
......@@ -400,4 +423,5 @@ export class AuthService {
this.store.dispatch(new SetRedirectUrlAction(''));
this.storage.remove(REDIRECT_COOKIE);
}
}
import { map, switchMap, take } from 'rxjs/operators';
import { filter, map, switchMap, take } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpHeaders } from '@angular/common/http';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { AuthStatus } from './models/auth-status.model';
import { isNotEmpty } from '../../shared/empty.util';
import { AuthService } from './auth.service';
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
import { AuthService, LOGIN_ROUTE } from './auth.service';
import { AuthTokenInfo } from './models/auth-token-info.model';
import { CheckAuthenticationTokenAction } from './auth.actions';
import { EPerson } from '../eperson/models/eperson.model';
......@@ -54,7 +54,7 @@ export class ServerAuthService extends AuthService {
/**
* Redirect to the route navigated before the login
*/
public redirectToPreviousUrl() {
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
this.getRedirectUrl().pipe(
take(1))
.subscribe((redirectUrl) => {
......@@ -67,10 +67,15 @@ export class ServerAuthService extends AuthService {
const url = decodeURIComponent(redirectUrl);
this.router.navigateByUrl(url);
} else {
this.router.navigate(['/']);
// If redirectUrl is empty use history. For ssr the history array should contain the requested url.
this.routeService.getHistory().pipe(
filter((history) => history.length > 0),
take(1)
).subscribe((history) => {
this.navigateToRedirectUrl(history[history.length - 1] || '');
});
}
})
}
}
......@@ -26,6 +26,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
import { hasValue, isNotEmptyOperator } from '../../shared/empty.util';
import { DSpaceObject } from '../shared/dspace-object.model';
import { PaginatedSearchOptions } from '../../+search-page/paginated-search-options.model';
import { SearchParam } from '../cache/models/search-param.model';
@Injectable()
export class CollectionDataService extends ComColDataService<Collection> {
......@@ -47,6 +48,36 @@ export class CollectionDataService extends ComColDataService<Collection> {
super();
}
/**
* Get all collections the user is authorized to submit to
*
* @param options The [[FindAllOptions]] object
* @return Observable<RemoteData<PaginatedList<Collection>>>
* collection list
*/
getAuthorizedCollection(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
const searchHref = 'findAuthorized';
return this.searchBy(searchHref, options).pipe(
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
}
/**
* Get all collections the user is authorized to submit to, by community
*
* @param communityId The community id
* @param options The [[FindAllOptions]] object
* @return Observable<RemoteData<PaginatedList<Collection>>>
* collection list
*/
getAuthorizedCollectionByCommunity(communityId: string, options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Collection>>> {
const searchHref = 'findAuthorizedByCommunity';
options.searchParams = [new SearchParam('uuid', communityId)];
return this.searchBy(searchHref, options).pipe(
filter((collections: RemoteData<PaginatedList<Collection>>) => !collections.isResponsePending));
}
/**
* Find whether there is a collection whom user has authorization to submit to
*
......
......@@ -3,7 +3,7 @@ import { HttpHeaders } from '@angular/common/http';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { Observable, race as observableRace } from 'rxjs';
import { filter, find, map, mergeMap, take } from 'rxjs/operators';
import { filter, map, mergeMap, take } from 'rxjs/operators';
import { cloneDeep, remove } from 'lodash';
import { AppState } from '../../app.reducer';
......@@ -262,12 +262,13 @@ export class RequestService {
*/
private clearRequestsOnTheirWayToTheStore(request: GetRequest) {
this.getByHref(request.href).pipe(
find((re: RequestEntry) => hasValue(re)))
.subscribe((re: RequestEntry) => {
if (!re.responsePending) {
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
}
});
filter((re: RequestEntry) => hasValue(re)),
take(1)
).subscribe((re: RequestEntry) => {
if (!re.responsePending) {
remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href);
}
});
}
/**
......
......@@ -128,7 +128,10 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService
// Iterate over all workspaceitem's sections
Object.keys(item.sections)
.forEach((sectionId) => {
if (typeof item.sections[sectionId] === 'object' && isNotEmpty(item.sections[sectionId])) {
if (typeof item.sections[sectionId] === 'object' && (isNotEmpty(item.sections[sectionId]) &&
// When Upload section is disabled, add to submission only if there are files
(!item.sections[sectionId].hasOwnProperty('files') || isNotEmpty((item.sections[sectionId] as any).files)))) {
const normalizedSectionData = Object.create({});
// Iterate over all sections property
Object.keys(item.sections[sectionId])
......
......@@ -3,7 +3,8 @@
<div ngbDropdown placement="bottom-right" class="d-inline-block" @fadeInOut>
<a href="#" id="dropdownLogin" (click)="$event.preventDefault()" ngbDropdownToggle class="px-1">{{ 'nav.login' | translate }}</a>
<div id="loginDropdownMenu" [ngClass]="{'pl-3 pr-3': (loading | async)}" ngbDropdownMenu aria-labelledby="dropdownLogin">
<ds-log-in></ds-log-in>
<ds-log-in
[isStandalonePage]="false"></ds-log-in>
</div>
</div>
</li>
......
......@@ -2,6 +2,7 @@ import { isObject, uniqueId } from 'lodash';
import { hasValue, isNotEmpty } from '../../empty.util';
import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
import { ConfidenceType } from '../../../core/integration/models/confidence-type';
import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model';
export interface ChipsItemIcon {
metadata: string;
......@@ -62,7 +63,7 @@ export class ChipsItem {
if (this._item.hasOwnProperty(icon.metadata)
&& (((typeof this._item[icon.metadata] === 'string') && hasValue(this._item[icon.metadata]))
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).hasValue())
&& !(this._item[icon.metadata] as FormFieldMetadataValueObject).hasPlaceholder()) {
&& !this.hasPlaceholder(this._item[icon.metadata])) {
if ((icon.visibleWhenAuthorityEmpty
|| (this._item[icon.metadata] as FormFieldMetadataValueObject).confidence !== ConfidenceType.CF_UNSET)
&& isNotEmpty(icon.style)) {
......@@ -109,4 +110,9 @@ export class ChipsItem {
this.display = value;
}
private hasPlaceholder(value: any) {
return (typeof value === 'string') ? (value === PLACEHOLDER_PARENT_METADATA) :
(value as FormFieldMetadataValueObject).hasPlaceholder()
}
}
......@@ -14,7 +14,8 @@
<ng-container #componentViewContainer></ng-container>
<small *ngIf="hasHint" class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<small *ngIf="hasHint && (!showErrorMessages || errorMessages.length === 0)"
class="text-muted" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<div *ngIf="showErrorMessages" [ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate:model.validators }}</small>
......
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicFormGroupModelConfig, serializable } from '@ng-dynamic-forms/core';
import { Subject } from 'rxjs';
import { isNotEmpty } from '../../../../empty.util';
import { DsDynamicInputModel } from './ds-dynamic-input.model';
import { FormFieldMetadataValueObject } from '../../models/form-field-metadata-value.model';
......@@ -16,12 +19,16 @@ export class DynamicConcatModel extends DynamicFormGroupModel {
@serializable() separator: string;
@serializable() hasLanguages = false;
isCustomGroup = true;
valueUpdates: Subject<string>;
constructor(config: DynamicConcatModelConfig, layout?: DynamicFormControlLayout) {
super(config, layout);
this.separator = config.separator + ' ';
this.valueUpdates = new Subject<string>();
this.valueUpdates.subscribe((value: string) => this.value = value);
}
get value() {
......
......@@ -28,6 +28,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) {
super(config, layout);
this.hint = config.hint;
this.readOnly = config.readOnly;
this.value = config.value;
this.language = config.language;
......@@ -57,11 +58,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
}
get hasLanguages(): boolean {
if (this.languageCodes && this.languageCodes.length > 1) {
return true;
} else {
return false;
}
return this.languageCodes && this.languageCodes.length > 1;
}
get language(): string {
......
import { DynamicFormControlLayout, DynamicFormGroupModel, DynamicInputModelConfig, serializable } from '@ng-dynamic-forms/core';
import { DsDynamicInputModel, DsDynamicInputModelConfig } from './ds-dynamic-input.model';
import { DynamicFormControlLayout, DynamicFormGroupModel, serializable } from '@ng-dynamic-forms/core';
import { DsDynamicInputModel } from './ds-dynamic-input.model';
import { Subject } from 'rxjs';
import { DynamicFormGroupModelConfig } from '@ng-dynamic-forms/core/src/model/form-group/dynamic-form-group.model';
import { LanguageCode } from '../../models/form-field-language-value.model';
......@@ -12,6 +12,7 @@ export interface DsDynamicQualdropModelConfig extends DynamicFormGroupModelConfi
languageCodes?: LanguageCode[];
language?: string;
readOnly: boolean;
hint?: string;
}
export class DynamicQualdropModel extends DynamicFormGroupModel {
......@@ -20,6 +21,7 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
@serializable() languageUpdates: Subject<string>;
@serializable() hasLanguages = false;
@serializable() readOnly: boolean;
@serializable() hint: string;
isCustomGroup = true;
constructor(config: DsDynamicQualdropModelConfig, layout?: DynamicFormControlLayout) {
......@@ -33,6 +35,8 @@ export class DynamicQualdropModel extends DynamicFormGroupModel {
this.languageUpdates.subscribe((lang: string) => {
this.language = lang;
});
this.hint = config.hint;
}
get value() {
......
......@@ -20,11 +20,10 @@
[disabled]="isInputDisabled()"
[placeholder]="model.placeholder | translate"
[readonly]="model.readOnly"
(change)="$event.preventDefault()"
(change)="onChange($event)"
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();"
(input)="onInput($event)">
(click)="$event.stopPropagation(); $event.stopPropagation(); sdRef.close();">
</div>
<!--Lookup-name, second field-->
......@@ -40,11 +39,10 @@
[disabled]="firstInputValue.length === 0 || isInputDisabled()"
[placeholder]="model.secondPlaceholder | translate"
[readonly]="model.readOnly"
(change)="$event.preventDefault()"
(change)="onChange($event)"
(blur)="onBlurEvent($event); $event.stopPropagation(); sdRef.close();"
(focus)="onFocusEvent($event); $event.stopPropagation(); sdRef.close();"
(click)="$event.stopPropagation(); sdRef.close();"
(input)="onInput($event)">
(click)="$event.stopPropagation(); sdRef.close();">
</div>
<div class="col-auto text-center">
<button ngbDropdownAnchor
......
......@@ -237,6 +237,12 @@ describe('Dynamic Lookup component', () => {
it('should init component properly', () => {
expect(lookupComp.firstInputValue).toBe('');
const de = lookupFixture.debugElement.queryAll(By.css('button'));
const searchBtnEl = de[0].nativeElement;
const editBtnEl = de[1].nativeElement;
expect(searchBtnEl.disabled).toBe(true);
expect(editBtnEl.disabled).toBe(true);
expect(editBtnEl.textContent.trim()).toBe('form.edit');
});
it('should return search results', fakeAsync(() => {
......@@ -283,7 +289,7 @@ describe('Dynamic Lookup component', () => {
lookupComp.firstInputValue = 'test';
lookupFixture.detectChanges();
lookupComp.onInput(new Event('input'));
lookupComp.onChange(new Event('change'));
expect(lookupComp.model.value).toEqual(new FormFieldMetadataValueObject('test'))
}));
......@@ -293,10 +299,11 @@ describe('Dynamic Lookup component', () => {
lookupComp.firstInputValue = 'test';
lookupFixture.detectChanges();
lookupComp.onInput(new Event('input'));
lookupComp.onChange(new Event('change'));
expect(lookupComp.model.value).not.toBeDefined();
});
});
describe('and init model value is not empty', () => {
......@@ -318,6 +325,19 @@ describe('Dynamic Lookup component', () => {
it('should init component properly', () => {
expect(lookupComp.firstInputValue).toBe('test');
});
it('should have search button disabled on edit mode', () => {
lookupComp.editMode = true;
lookupFixture.detectChanges();
const de = lookupFixture.debugElement.queryAll(By.css('button'));
const searchBtnEl = de[0].nativeElement;
const saveBtnEl = de[1].nativeElement;
expect(searchBtnEl.disabled).toBe(true);
expect(saveBtnEl.disabled).toBe(false);
expect(saveBtnEl.textContent.trim()).toBe('form.save');
});
});
});
......@@ -340,7 +360,14 @@ describe('Dynamic Lookup component', () => {
});
it('should render two input element', () => {
const de = lookupFixture.debugElement.queryAll(By.css('input.form-control'));
const deBtn = lookupFixture.debugElement.queryAll(By.css('button'));
const searchBtnEl = deBtn[0].nativeElement;
const editBtnEl = deBtn[1].nativeElement;
expect(de.length).toBe(2);
expect(searchBtnEl.disabled).toBe(true);
expect(editBtnEl.disabled).toBe(true);
expect(editBtnEl.textContent.trim()).toBe('form.edit');
});
});
......@@ -418,6 +445,19 @@ describe('Dynamic Lookup component', () => {
expect(lookupComp.firstInputValue).toBe('Name');
expect(lookupComp.secondInputValue).toBe('Lastname');
});
it('should have search button disabled on edit mode', () => {
lookupComp.editMode = true;
lookupFixture.detectChanges();
const de = lookupFixture.debugElement.queryAll(By.css('button'));
const searchBtnEl = de[0].nativeElement;
const saveBtnEl = de[1].nativeElement;
expect(searchBtnEl.disabled).toBe(true);
expect(saveBtnEl.disabled).toBe(false);
expect(saveBtnEl.textContent.trim()).toBe('form.save');
});
});
});
});
......
......@@ -123,6 +123,15 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
}
}
protected updateModel(value) {
this.group.markAsDirty();
this.model.valueUpdates.next(value);
this.setInputsValue(value);
this.change.emit(value);
this.optionsList = null;
this.pageInfo = null;
}
public formatItemForInput(item: any, field: number): string {
if (isUndefined(item) || isNull(item)) {
return '';
......@@ -159,7 +168,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
}
public isSearchDisabled() {
return isEmpty(this.firstInputValue);
return isEmpty(this.firstInputValue) || this.editMode;
}
public onBlurEvent(event: Event) {
......@@ -170,12 +179,13 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
this.focus.emit(event);
}
public onInput(event) {
public onChange(event) {
event.preventDefault();
if (!this.model.authorityOptions.closed) {
if (isNotEmpty(this.getCurrentValue())) {
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
if (!this.editMode) {
this.onSelect(currentValue);
this.updateModel(currentValue);
}
} else {
this.remove();
......@@ -191,12 +201,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
}
public onSelect(event) {
this.group.markAsDirty();
this.model.valueUpdates.next(event);
this.setInputsValue(event);
this.change.emit(event);
this.optionsList = null;
this.pageInfo = null;
this.updateModel(event);
}
public openChange(isOpened: boolean) {
......@@ -219,7 +224,7 @@ export class DsDynamicLookupComponent extends DynamicFormControlComponent implem
display: this.getCurrentValue(),
value: this.getCurrentValue()
});
this.onSelect(newValue);
this.updateModel(newValue);
} else {
this.remove();
}
......
......@@ -129,9 +129,11 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
|| this.selectedChipItem.item[model.name].value === PLACEHOLDER_PARENT_METADATA)
? null
: this.selectedChipItem.item[model.name];
if (isNotNull(value)) {
model.valueUpdates.next(this.formBuilderService.isInputModel(model) ? value.value : value);
}
const nextValue = (this.formBuilderService.isInputModel(model) && isNotNull(value) && (typeof value !== 'string')) ?
value.value : value;
model.valueUpdates.next(nextValue);
});
});
......@@ -229,7 +231,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
flatMap((valueModel) => {
const returnList: Array<Observable<any>> = [];
valueModel.forEach((valueObj) => {
const returnObj = Object.keys(valueObj).map((fieldName) => {
const returnObj = Object.keys(valueObj).map((fieldName) => {
let return$: Observable<any>;
if (isObject(valueObj[fieldName]) && valueObj[fieldName].hasAuthority() && isNotEmpty(valueObj[fieldName].authority)) {
const fieldId = fieldName.replace(/\./g, '_');
......@@ -253,7 +255,7 @@ export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent
} else {
return$ = observableOf(valueObj[fieldName]);
}
return return$.pipe(map((entry) => ({[fieldName]: entry})));
return return$.pipe(map((entry) => ({ [fieldName]: entry })));
});
returnList.push(combineLatest(returnObj));
......
......@@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro
import { FormGroup } from '@angular/forms';
import { Observable, of as observableOf } from 'rxjs';
import { catchError, first, tap } from 'rxjs/operators';
import { catchError, distinctUntilChanged, first, tap } from 'rxjs/operators';
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
import {
DynamicFormControlComponent,
......@@ -71,7 +71,13 @@ export class DsDynamicScrollableDropdownComponent extends DynamicFormControlComp
}
this.pageInfo = object.pageInfo;
this.cdr.detectChanges();
})
});
this.group.get(this.model.id).valueChanges.pipe(distinctUntilChanged())
.subscribe((value) => {
this.setCurrentValue(value);
});
}
inputFormatter = (x: AuthorityValue): string => x.display || x.value;
......
......@@ -28,7 +28,8 @@
aria-hidden="true"
[authorityValue]="currentValue"
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
<input class="form-control"
<input #instance="ngbTypeahead"
class="form-control"
[attr.autoComplete]="model.autoComplete"
[class.is-invalid]="showErrorMessages"
[dynamicId]="bindId && model.id"
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment