diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts index f0fc80574cb9..7c8fa4d22344 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts @@ -406,7 +406,12 @@ export class ControllerServicesEffects { selectPropertyVerificationStatus ); - const goTo = (commands: string[], destination: string, commandBoundary?: string[]): void => { + const goTo = ( + commands: string[], + destination: string, + commandBoundary?: string[], + additionalState?: Record + ): void => { if (editDialogReference.componentInstance.editControllerServiceForm.dirty) { const saveChangesDialogReference = this.dialog.open(YesNoDialog, { ...SMALL_DIALOG, @@ -434,11 +439,14 @@ export class ControllerServicesEffects { ], routeBoundary: commandBoundary, context: 'Controller Service' - } as BackNavigation + } as BackNavigation, + ...additionalState } }); } else { - this.router.navigate(commands); + this.router.navigate(commands, { + state: { ...additionalState } + }); } }); } else { @@ -455,11 +463,14 @@ export class ControllerServicesEffects { ], routeBoundary: commandBoundary, context: 'Controller Service' - } as BackNavigation + } as BackNavigation, + ...additionalState } }); } else { - this.router.navigate(commands); + this.router.navigate(commands, { + state: { ...additionalState } + }); } } }; @@ -473,12 +484,12 @@ export class ControllerServicesEffects { if (parameterContext != null) { editDialogReference.componentInstance.parameterContext = parameterContext; - editDialogReference.componentInstance.goToParameter = () => { + editDialogReference.componentInstance.goToParameter = (parameterName: string) => { this.storage.setItem(NiFiCommon.EDIT_PARAMETER_CONTEXT_DIALOG_ID, 1); const commandBoundary: string[] = ['/parameter-contexts']; const commands: string[] = [...commandBoundary, parameterContext.id, 'edit']; - goTo(commands, 'Parameter', commandBoundary); + goTo(commands, 'Parameter', commandBoundary, { parameterName }); }; editDialogReference.componentInstance.convertToParameter = diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts index a66564ab5bc6..cab067529cbb 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts @@ -1564,7 +1564,12 @@ export class FlowEffects { selectPropertyVerificationStatus ); - const goTo = (commands: string[], commandBoundary: string[], destination: string): void => { + const goTo = ( + commands: string[], + commandBoundary: string[], + destination: string, + additionalState?: Record + ): void => { if (editDialogReference.componentInstance.editProcessorForm.dirty) { const saveChangesDialogReference = this.dialog.open(YesNoDialog, { ...SMALL_DIALOG, @@ -1591,7 +1596,8 @@ export class FlowEffects { ], routeBoundary: commandBoundary, context: 'Processor' - } as BackNavigation + } as BackNavigation, + ...additionalState } }); }); @@ -1608,7 +1614,8 @@ export class FlowEffects { ], routeBoundary: commandBoundary, context: 'Processor' - } as BackNavigation + } as BackNavigation, + ...additionalState } }); } @@ -1616,12 +1623,12 @@ export class FlowEffects { if (parameterContext != null) { editDialogReference.componentInstance.parameterContext = parameterContext; - editDialogReference.componentInstance.goToParameter = () => { + editDialogReference.componentInstance.goToParameter = (parameterName: string) => { this.storage.setItem(NiFiCommon.EDIT_PARAMETER_CONTEXT_DIALOG_ID, 1); const commandBoundary: string[] = ['/parameter-contexts']; const commands: string[] = [...commandBoundary, parameterContext.id, 'edit']; - goTo(commands, commandBoundary, 'Parameter'); + goTo(commands, commandBoundary, 'Parameter', { parameterName }); }; editDialogReference.componentInstance.convertToParameter = diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts index 74b9a005cb8b..5cd46d246eb5 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts @@ -16,6 +16,7 @@ */ import { Injectable, inject } from '@angular/core'; +import { Location } from '@angular/common'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { concatLatestFrom } from '@ngrx/operators'; import * as ParameterContextListingActions from './parameter-context-listing.actions'; @@ -67,6 +68,7 @@ export class ParameterContextListingEffects { private parameterContextService = inject(ParameterContextService); private dialog = inject(MatDialog); private router = inject(Router); + private location = inject(Location); private errorHelper = inject(ErrorHelper); loadParameterContexts$ = createEffect(() => @@ -323,6 +325,9 @@ export class ParameterContextListingEffects { this.storage.setItem(NiFiCommon.EDIT_PARAMETER_CONTEXT_DIALOG_ID, 1); + const navigationState = this.location.getState() as { parameterName?: string } | null; + const selectedParameterName = navigationState?.parameterName; + const editDialogReference = this.dialog.open(EditParameterContext, { ...XL_DIALOG, data: { @@ -330,6 +335,12 @@ export class ParameterContextListingEffects { } }); + if (selectedParameterName) { + // Parameters tab + editDialogReference.componentInstance.selectedIndex = 1; + editDialogReference.componentInstance.selectedParameterName = selectedParameterName; + } + editDialogReference.componentInstance.updateRequest = this.store.select(selectUpdateRequest); editDialogReference.componentInstance.availableParameterContexts$ = this.store diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html index eadeaaf17e93..8de520312d7e 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html @@ -172,7 +172,8 @@ (click)="selectParameter(row)" (dblclick)="doubleClicked(row)" [class.selected]="isSelected(row)" - [class.even]="even"> + [class.even]="even" + [attr.data-parameter-name]="row.originalEntity.parameter.name"> diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts index 70f615a1e710..a45002d79fa6 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, inject } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Input, inject } from '@angular/core'; import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; @@ -74,11 +74,13 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor { private store = inject>(Store); private changeDetector = inject(ChangeDetectorRef); private nifiCommon = inject(NiFiCommon); + private elementRef = inject(ElementRef); @Input() createNewParameter!: (existingParameters: string[]) => Observable; @Input() editParameter!: (parameter: Parameter) => Observable; @Input() canAddParameters = true; @Input() inheritsParameters = false; + @Input() selectedParameterName?: string; protected readonly TextTip = TextTip; @@ -165,6 +167,28 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor { private setPropertyItems(parameterItems: ParameterItem[]): void { this.dataSource.data = this.sortEntities(parameterItems, this.activeSort); this.initFilter(); + this.selectAndScrollToParameter(this.selectedParameterName); + } + + private selectAndScrollToParameter(parameterName: string | undefined): void { + if (!parameterName) { + return; + } + + const item = this.dataSource.data.find((i) => i.originalEntity.parameter.name === parameterName); + if (!item) { + return; + } + + this.selectParameter(item); + + // wait for the table rows to render before attempting to scroll + setTimeout(() => { + const row: HTMLElement | null = this.elementRef.nativeElement.querySelector( + `tr[data-parameter-name="${parameterName}"]` + ); + row?.scrollIntoView({ block: 'center' }); + }); } private sortEntities(parameters: ParameterItem[], sort: Sort): ParameterItem[] { diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.html b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.html index 9ef15caaf6de..a7fcecfd5827 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.html +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.html @@ -157,7 +157,8 @@

[canAddParameters]="!request.parameterContext?.component?.parameterProviderConfiguration" [inheritsParameters]="inheritsParameters(request.parameterContext?.component?.parameters)" [createNewParameter]="createNewParameter" - [editParameter]="editParameter"> + [editParameter]="editParameter" + [selectedParameterName]="selectedParameterName"> diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.ts index 59e3e75279d8..32d1a2525427 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-context/edit-parameter-context/edit-parameter-context.component.ts @@ -96,6 +96,7 @@ export class EditParameterContext extends TabbedDialog { @Input() updateRequest!: Observable; @Input() availableParameterContexts$!: Observable; @Input() saving$!: Observable; + @Input() selectedParameterName?: string; @Output() addParameterContext: EventEmitter = new EventEmitter(); @Output() editParameterContext: EventEmitter = new EventEmitter(); diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.spec.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.spec.ts index 20779e9a0f02..0dd5c547467e 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.spec.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.spec.ts @@ -74,13 +74,13 @@ describe('PropertyTable', () => { expect(() => component.goToParameterClicked(mockItem)).not.toThrow(); }); - it('should invoke goToParameter callback when supplied and item.value is non-null', () => { + it('should invoke goToParameter callback with the extracted parameter name when supplied and item.value is non-null', () => { const goToParameterSpy = vi.fn(); component.goToParameter = goToParameterSpy; component.goToParameterClicked(mockItem); - expect(goToParameterSpy).toHaveBeenCalledWith('#{some-param}'); + expect(goToParameterSpy).toHaveBeenCalledWith('some-param'); }); it('should not invoke goToParameter callback when item.value is null', () => { diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts index 4abc9dcc8310..ef4f47cd78c4 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/property-table.component.ts @@ -108,6 +108,7 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor { @Input() supportsParameters = true; private static readonly PARAM_REF_REGEX: RegExp = /#{(['"]?)[a-zA-Z0-9-_. ]+\1}/; + private static readonly PARAM_REF_NAME_REGEX: RegExp = /#{(['"]?)([a-zA-Z0-9-_. ]+)\1}/; private destroyRef = inject(DestroyRef); @@ -517,9 +518,6 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor { } canGoToParameter(item: PropertyItem): boolean { - // TODO - currently parameter context route does not support navigating - // directly to a specific parameter so the parameter context link - // is not item specific. if (this.parameterContext && this.goToParameter && item.value) { return this.parameterContext.permissions.canRead && PropertyTable.PARAM_REF_REGEX.test(item.value); } @@ -531,7 +529,16 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor { if (!this.goToParameter || item.value == null) { return; } - this.goToParameter(item.value); + + const parameterName = this.extractParameterName(item.value); + if (parameterName) { + this.goToParameter(parameterName); + } + } + + private extractParameterName(value: string): string | null { + const match = PropertyTable.PARAM_REF_NAME_REGEX.exec(value); + return match ? match[2] : null; } canConvertToParameter(item: PropertyItem): boolean {