2.48.0 • Published 6 months ago

@memberjunction/ng-join-grid v2.48.0

Weekly downloads
-
License
ISC
Repository
-
Last release
6 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

9 months ago

2.23.2

10 months ago

2.46.0

6 months ago

2.23.1

10 months ago

2.27.0

9 months ago

2.34.0

7 months ago

2.30.0

9 months ago

2.19.4

10 months ago

2.19.5

10 months ago

2.19.2

10 months ago

2.19.3

10 months ago

2.19.0

11 months ago

2.19.1

11 months ago

2.15.2

11 months ago

2.34.2

7 months ago

2.34.1

7 months ago

2.15.1

11 months ago

2.38.0

7 months ago

2.45.0

6 months ago

2.22.1

10 months ago

2.22.0

10 months ago

2.41.0

6 months ago

2.22.2

10 months ago

2.26.1

10 months ago

2.26.0

10 months ago

2.33.0

7 months ago

2.18.3

11 months ago

2.18.1

11 months ago

2.18.2

11 months ago

2.18.0

11 months ago

2.37.1

7 months ago

2.37.0

7 months ago

2.14.0

11 months ago

2.21.0

10 months ago

2.44.0

6 months ago

2.40.0

7 months ago

2.29.0

9 months ago

2.29.2

9 months ago

2.29.1

9 months ago

2.25.0

10 months ago

2.48.0

6 months ago

2.32.0

8 months ago

2.32.2

8 months ago

2.32.1

8 months ago

2.17.0

11 months ago

2.13.4

12 months ago

2.36.0

7 months ago

2.13.2

12 months ago

2.13.3

12 months ago

2.13.0

1 year ago

2.36.1

7 months ago

2.13.1

1 year ago

2.43.0

6 months ago

2.20.2

10 months ago

2.20.3

10 months ago

2.20.0

10 months ago

2.20.1

10 months ago

2.28.0

9 months ago

2.47.0

6 months ago

2.24.1

10 months ago

2.24.0

10 months ago

2.31.0

8 months ago

2.12.0

1 year ago

2.39.0

7 months ago

2.16.1

11 months ago

2.35.1

7 months ago

2.35.0

7 months ago

2.16.0

11 months ago

2.42.1

6 months ago

2.42.0

6 months ago

2.23.0

10 months ago

2.11.0

1 year ago

2.10.0

1 year ago

2.9.0

1 year 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