2.48.0 • Published 4 months ago

@memberjunction/ng-join-grid v2.48.0

Weekly downloads
-
License
ISC
Repository
-
Last release
4 months ago

@memberjunction/ng-join-grid

The Join Grid component is a powerful Angular grid that allows you to display and edit relationships between two entities, typically in a many-to-many relationship. It provides a checkbox-based interface for mapping relationships between records.

Overview

This package provides the JoinGridComponent - a flexible grid component designed to manage entity relationships within the MemberJunction framework. It supports both many-to-many relationships through junction entities and direct field editing in related records.

Features

  • Two Operation Modes:
    • Entity Mode: Creates/deletes records in a junction entity for many-to-many relationships
    • Fields Mode: Updates fields directly in related records
  • Flexible Data Sources: Load data from full entities, views, or arrays
  • Automatic Grid Generation: Builds grid structure based on provided entity relationships
  • Integrated with MemberJunction: Works seamlessly with the MJ metadata system and BaseEntity objects
  • Transaction Support: Manages pending changes with transaction groups
  • Form Integration: Can be integrated with parent form components and respond to form events
  • Checkbox Value Modes: Support for both record existence and field value checkboxes
  • Built-in Save/Cancel: Configurable buttons for managing changes

Installation

npm install @memberjunction/ng-join-grid

Prerequisites

This package requires the following peer dependencies:

  • @angular/common: ^18.0.2
  • @angular/core: ^18.0.2
  • @angular/forms: ^18.0.2
  • @angular/router: ^18.0.2

Dependencies

The component integrates with these MemberJunction packages:

  • @memberjunction/core-entities
  • @memberjunction/global
  • @memberjunction/core
  • @memberjunction/ng-base-types
  • @memberjunction/ng-container-directives
  • @memberjunction/ng-shared

It also uses Kendo UI components:

  • @progress/kendo-angular-buttons
  • @progress/kendo-angular-dialog
  • @progress/kendo-angular-layout
  • @progress/kendo-angular-grid
  • @progress/kendo-angular-inputs
  • @progress/kendo-angular-indicators

Usage

Import the Module

import { JoinGridModule } from '@memberjunction/ng-join-grid';

@NgModule({
  imports: [
    JoinGridModule,
    // other imports
  ],
  // ...
})
export class YourModule { }

Basic Component Usage (Entity Mode)

Use this mode when you want to manage relationships between two entities by creating or deleting records in a junction entity.

<mj-join-grid
  [RowsEntityName]="'Users'"
  [RowsEntityDisplayField]="'UserName'"
  [ColumnsEntityName]="'Roles'"
  [ColumnsEntityDisplayField]="'RoleName'"
  [JoinEntityName]="'UserRoles'"
  [JoinEntityRowForeignKey]="'UserID'"
  [JoinEntityColumnForeignKey]="'RoleID'"
  [CheckBoxValueMode]="'RecordExists'"
  [ShowSaveButton]="true"
  [ShowCancelButton]="true">
</mj-join-grid>

Fields Mode

Used when you want to edit fields in a related entity:

<mj-join-grid
  [RowsEntityName]="'Users'"
  [RowsEntityDisplayField]="'UserName'"
  [ColumnsMode]="'Fields'"
  [JoinEntityName]="'UserPreferences'"
  [JoinEntityRowForeignKey]="'UserID'"
  [JoinEntityDisplayColumns]="['PreferenceType', 'PreferenceValue']"
  [ShowSaveButton]="true"
  [ShowCancelButton]="true">
</mj-join-grid>

API Reference

Component Selector

<mj-join-grid></mj-join-grid>

Inputs

General Configuration

InputTypeDefaultDescription
ShowSaveButtonbooleantrueShow/hide the save button
ShowCancelButtonbooleantrueShow/hide the cancel button
EditMode'None' \| 'Save' \| 'Queue''None'Control editing mode. Use when embedding in parent forms
NewRecordDefaultValues{ [key: string]: any }-Default values for new junction records

Row Configuration

InputTypeDefaultRequiredDescription
RowsEntityNamestring-Name of the entity for rows
RowsEntityDisplayNamestring--Display name to show instead of entity name
RowsEntityDisplayFieldstring-Field to display in the first column
RowsEntityDataSource'FullEntity' \| 'ViewName' \| 'Array''FullEntity'-Data source type for rows
RowsEntityViewNamestring-When DataSource='ViewName'User View name to run
RowsExtraFilterstring--Additional SQL filter for rows
RowsOrderBystring--SQL order by clause for rows
RowsEntityArrayBaseEntity[]-When DataSource='Array'Array of entity objects

Column Configuration

InputTypeDefaultRequiredDescription
ColumnsMode'Entity' \| 'Fields''Entity'-Mode for column generation
ColumnsEntityNamestring-When Mode='Entity'Name of entity for columns
ColumnsEntityDisplayFieldstring-When Mode='Entity'Field to display as column headers
ColumnsEntityDataSource'FullEntity' \| 'ViewName' \| 'Array''FullEntity'-Data source type for columns
ColumnsEntityViewNamestring-When DataSource='ViewName'User View name to run
ColumnsExtraFilterstring--Additional SQL filter for columns
ColumnsOrderBystring--SQL order by clause for columns
ColumnsEntityArrayBaseEntity[]-When DataSource='Array'Array of entity objects

Join Entity Configuration

InputTypeDefaultRequiredDescription
JoinEntityNamestring-Name of the junction/join entity
JoinEntityRowForeignKeystring-Foreign key field linking to rows
JoinEntityColumnForeignKeystring-Foreign key field linking to columns
JoinEntityDisplayColumnsstring[]-When ColumnsMode='Fields'Columns to display from join entity
JoinEntityExtraFilterstring--Additional filter for join entity
CheckBoxValueMode'RecordExists' \| 'ColumnValue''RecordExists'-How checkbox state is determined
CheckBoxValueFieldstring-When CheckBoxValueMode='ColumnValue'Field storing checkbox value

Public Methods

MethodParametersReturnsDescription
Refresh()-Promise<void>Reload all grid data
Save()-Promise<boolean>Save all pending changes
CancelEdit()-voidCancel all pending changes
UpdateCellValueDirect()row: JoinGridRow, colIndex: number, newValue: anyvoidUpdate cell value (Fields mode)
AddJoinEntityRecord()row: JoinGridRowPromise<void>Add new join record (Fields mode)
RemoveJoinEntityRecord()row: JoinGridRowPromise<void>Remove join record (Fields mode)

Exported Classes

JoinGridCell

export class JoinGridCell {
  index: number;
  RowForeignKeyValue: any;
  ColumnForeignKeyValue?: any;
  data?: BaseEntity;        // Used in Entity mode
  value?: any;              // Used in Fields mode
}

JoinGridRow

export class JoinGridRow {
  FirstColValue: any;
  JoinExists: boolean;
  RowForeignKeyValue: any;
  ColumnData: JoinGridCell[];
  
  GetColumnValue(colIndex: number): any;
  constructor(data: any);
}

Examples

User-Role Assignment Grid

// Component
@Component({
  selector: 'app-user-roles',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Users'"
      [RowsEntityDisplayField]="'UserName'"
      [ColumnsEntityName]="'Roles'"
      [ColumnsEntityDisplayField]="'RoleName'"
      [JoinEntityName]="'UserRoles'"
      [JoinEntityRowForeignKey]="'UserID'"
      [JoinEntityColumnForeignKey]="'RoleID'"
      [RowsExtraFilter]="'IsActive = 1'"
      [ColumnsExtraFilter]="'IsActive = 1'"
      [RowsOrderBy]="'UserName ASC'"
      [ColumnsOrderBy]="'RoleName ASC'"
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class UserRolesComponent { }

Product Category Assignment with Custom Values

// Component with defaults for new junction records
@Component({
  selector: 'app-product-categories',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Products'"
      [RowsEntityDisplayField]="'ProductName'"
      [ColumnsEntityName]="'Categories'"
      [ColumnsEntityDisplayField]="'CategoryName'"
      [JoinEntityName]="'ProductCategories'"
      [JoinEntityRowForeignKey]="'ProductID'"
      [JoinEntityColumnForeignKey]="'CategoryID'"
      [NewRecordDefaultValues]="defaultValues"
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class ProductCategoriesComponent {
  defaultValues = {
    'IsPrimary': false,
    'CreatedAt': new Date()
  };
}

Fields Mode Example - User Preferences

// Edit fields directly in the join entity
@Component({
  selector: 'app-user-preferences',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Users'"
      [RowsEntityDisplayField]="'FullName'"
      [ColumnsMode]="'Fields'"
      [JoinEntityName]="'UserPreferences'"
      [JoinEntityRowForeignKey]="'UserID'"
      [JoinEntityDisplayColumns]="['PreferenceName', 'Value', 'IsEnabled']"
      [JoinEntityExtraFilter]="'CategoryID = 5'"
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class UserPreferencesComponent { }

Using Views and Filters

// Component using views and filters for data sources
@Component({
  selector: 'app-active-user-permissions',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Users'"
      [RowsEntityDisplayField]="'UserName'"
      [RowsEntityDataSource]="'ViewName'"
      [RowsEntityViewName]="'Active Users'"
      [RowsOrderBy]="'LastLogin DESC'"
      
      [ColumnsEntityName]="'Permissions'"
      [ColumnsEntityDisplayField]="'PermissionName'"
      [ColumnsExtraFilter]="'IsActive = 1 AND CategoryID IN (1,2,3)'"
      [ColumnsOrderBy]="'DisplayOrder ASC, PermissionName ASC'"
      
      [JoinEntityName]="'UserPermissions'"
      [JoinEntityRowForeignKey]="'UserID'"
      [JoinEntityColumnForeignKey]="'PermissionID'"
      [JoinEntityExtraFilter]="'GrantedDate IS NOT NULL'"
      
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class ActiveUserPermissionsComponent { }

Using Array Data Sources

// Component using pre-loaded arrays
@Component({
  selector: 'app-team-skills',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Employees'"
      [RowsEntityDisplayField]="'Name'"
      [RowsEntityDataSource]="'Array'"
      [RowsEntityArray]="teamMembers"
      
      [ColumnsEntityName]="'Skills'"
      [ColumnsEntityDisplayField]="'SkillName'"
      [ColumnsEntityDataSource]="'Array'"
      [ColumnsEntityArray]="relevantSkills"
      
      [JoinEntityName]="'EmployeeSkills'"
      [JoinEntityRowForeignKey]="'EmployeeID'"
      [JoinEntityColumnForeignKey]="'SkillID'"
      
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class TeamSkillsComponent implements OnInit {
  teamMembers: BaseEntity[] = [];
  relevantSkills: BaseEntity[] = [];

  async ngOnInit() {
    // Load team members and skills
    const md = new Metadata();
    
    // Load team members
    const employeeEntity = await md.GetEntityObject<BaseEntity>('Employees');
    const teamRv = new RunView();
    const teamResult = await teamRv.RunView({
      EntityName: 'Employees',
      ExtraFilter: 'DepartmentID = 5',
      ResultType: 'entity_object'
    });
    this.teamMembers = teamResult.Results;
    
    // Load relevant skills
    const skillsRv = new RunView();
    const skillsResult = await skillsRv.RunView({
      EntityName: 'Skills',
      ExtraFilter: 'CategoryID IN (1,2,3)',
      ResultType: 'entity_object'
    });
    this.relevantSkills = skillsResult.Results;
  }
}

Embedded in Parent Form with Edit Mode Control

// Component embedded in a larger form
@Component({
  selector: 'app-employee-form',
  template: `
    <form>
      <!-- Other form fields -->
      
      <mj-join-grid
        #skillsGrid
        [RowsEntityName]="'Employees'"
        [RowsEntityDisplayField]="'Name'"
        [RowsEntityArray]="[currentEmployee]"
        
        [ColumnsEntityName]="'Skills'"
        [ColumnsEntityDisplayField]="'SkillName'"
        
        [JoinEntityName]="'EmployeeSkills'"
        [JoinEntityRowForeignKey]="'EmployeeID'"
        [JoinEntityColumnForeignKey]="'SkillID'"
        
        [EditMode]="editMode"
        [ShowSaveButton]="false"
        [ShowCancelButton]="false">
      </mj-join-grid>
      
      <button (click)="saveAll()">Save Employee</button>
      <button (click)="cancel()">Cancel</button>
    </form>
  `
})
export class EmployeeFormComponent {
  @ViewChild('skillsGrid') skillsGrid!: JoinGridComponent;
  
  currentEmployee: BaseEntity;
  editMode: 'None' | 'Save' | 'Queue' = 'Queue';
  
  async saveAll() {
    // Save the grid changes
    const gridSaved = await this.skillsGrid.Save();
    
    if (gridSaved) {
      // Save other form data
      await this.currentEmployee.Save();
    }
  }
  
  cancel() {
    this.skillsGrid.CancelEdit();
    // Cancel other form changes
  }
}

Checkbox Value Mode Example

// Using checkbox to update a field value instead of record existence
@Component({
  selector: 'app-feature-access',
  template: `
    <mj-join-grid
      [RowsEntityName]="'Users'"
      [RowsEntityDisplayField]="'UserName'"
      
      [ColumnsEntityName]="'Features'"
      [ColumnsEntityDisplayField]="'FeatureName'"
      
      [JoinEntityName]="'UserFeatureAccess'"
      [JoinEntityRowForeignKey]="'UserID'"
      [JoinEntityColumnForeignKey]="'FeatureID'"
      
      [CheckBoxValueMode]="'ColumnValue'"
      [CheckBoxValueField]="'IsEnabled'"
      
      [ShowSaveButton]="true"
      [ShowCancelButton]="true">
    </mj-join-grid>
  `
})
export class FeatureAccessComponent { }

Advanced Usage

Integration with MemberJunction Forms

The Join Grid component can be integrated with MemberJunction's form system and responds to form events:

import { BaseFormComponent } from '@memberjunction/ng-base-forms';

@Component({
  template: `
    <mj-join-grid
      [parentForm]="parentFormComponent"
      ...other properties>
    </mj-join-grid>
  `
})
export class MyComponent {
  parentFormComponent: BaseFormComponent;
}

Transaction Support

The component automatically manages transactions when editing:

  • Creates transaction groups for batch operations
  • Rolls back changes on save failure
  • Supports both immediate save and queued edit modes

Event Handling

The component emits events through the MemberJunction event system:

  • Form editing complete events
  • Save/cancel events
  • Data refresh events

Building

To build the package:

cd packages/Angular/Generic/join-grid
npm run build

Notes

  • The component requires proper entity metadata configuration in MemberJunction
  • Junction entities must have appropriate foreign key relationships defined
  • When using Fields mode, ensure the join entity has the display columns configured
  • The component respects entity permissions and validation rules
2.27.1

8 months ago

2.23.2

8 months ago

2.46.0

5 months ago

2.23.1

8 months ago

2.27.0

8 months ago

2.34.0

6 months ago

2.30.0

7 months ago

2.19.4

9 months ago

2.19.5

9 months ago

2.19.2

9 months ago

2.19.3

9 months ago

2.19.0

9 months ago

2.19.1

9 months ago

2.15.2

9 months ago

2.34.2

6 months ago

2.34.1

6 months ago

2.15.1

9 months ago

2.38.0

5 months ago

2.45.0

5 months ago

2.22.1

9 months ago

2.22.0

9 months ago

2.41.0

5 months ago

2.22.2

8 months ago

2.26.1

8 months ago

2.26.0

8 months ago

2.33.0

6 months ago

2.18.3

9 months ago

2.18.1

9 months ago

2.18.2

9 months ago

2.18.0

9 months ago

2.37.1

5 months ago

2.37.0

6 months ago

2.14.0

10 months ago

2.21.0

9 months ago

2.44.0

5 months ago

2.40.0

5 months ago

2.29.0

8 months ago

2.29.2

7 months ago

2.29.1

7 months ago

2.25.0

8 months ago

2.48.0

4 months ago

2.32.0

7 months ago

2.32.2

7 months ago

2.32.1

7 months ago

2.17.0

9 months ago

2.13.4

10 months ago

2.36.0

6 months ago

2.13.2

11 months ago

2.13.3

10 months ago

2.13.0

11 months ago

2.36.1

6 months ago

2.13.1

11 months ago

2.43.0

5 months ago

2.20.2

9 months ago

2.20.3

9 months ago

2.20.0

9 months ago

2.20.1

9 months ago

2.28.0

8 months ago

2.47.0

4 months ago

2.24.1

8 months ago

2.24.0

8 months ago

2.31.0

7 months ago

2.12.0

12 months ago

2.39.0

5 months ago

2.16.1

9 months ago

2.35.1

6 months ago

2.35.0

6 months ago

2.16.0

9 months ago

2.42.1

5 months ago

2.42.0

5 months ago

2.23.0

8 months ago

2.11.0

12 months ago

2.10.0

12 months ago

2.9.0

12 months ago

2.8.0

1 year ago

2.7.0

1 year ago

2.6.1

1 year ago

2.5.2

1 year ago

2.6.0

1 year ago

2.7.1

1 year ago

2.5.1

1 year ago

2.5.0

1 year ago

2.4.1

1 year ago

2.4.0

1 year ago

2.3.3

1 year ago

2.3.2

1 year ago

2.3.1

1 year ago

2.3.0

1 year ago

2.2.1

1 year ago

2.2.0

1 year ago

2.1.5

1 year ago

2.1.4

1 year ago

2.1.3

1 year ago

2.1.2

1 year ago

2.1.1

1 year ago

2.0.0

1 year ago

1.8.1

1 year ago

1.8.0

1 year ago

1.7.1

1 year ago

1.7.0

1 year ago

1.6.1

1 year ago

1.6.0

1 year ago

1.5.3

1 year ago

1.5.2

1 year ago

1.5.1

1 year ago

1.5.0

1 year ago

1.4.1

1 year ago