Skip to content

Commit

Permalink
Experimental refactor of BlocksWorkflowComponent to dynamically load …
Browse files Browse the repository at this point in the history
…block components
  • Loading branch information
lukestanley committed Mar 5, 2024
1 parent 30756a3 commit 4a516a0
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 119 deletions.
64 changes: 2 additions & 62 deletions src/app/components/blocks-workflow/blocks-workflow.component.html
Original file line number Diff line number Diff line change
@@ -1,62 +1,2 @@
<ng-container *ngFor="let block of blocks; let i = index">
<ng-container [ngSwitch]="block.type">
<app-form-block class="block-wrapper" *ngSwitchCase="'form'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-form-block>
<app-debug-block class="block-wrapper" *ngSwitchCase="'debug'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-debug-block>
<app-query-block class="block-wrapper" *ngSwitchCase="'query'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-query-block>
<app-mapping-block class="block-wrapper" *ngSwitchCase="'mapping'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-mapping-block>
<app-grid-block class="block-wrapper" *ngSwitchCase="'grid'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-grid-block>
<app-http-block class="block-wrapper" *ngSwitchCase="'http'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-http-block>
<app-chart-block class="block-wrapper" *ngSwitchCase="'chart'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-chart-block>
<app-init-block class="block-wrapper" *ngSwitchCase="'init'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-init-block>
<app-message-block class="block-wrapper" *ngSwitchCase="'message'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-message-block>
<app-image-block class="block-wrapper" *ngSwitchCase="'image'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-image-block>
<app-vimeo-block class="block-wrapper" *ngSwitchCase="'vimeoPlayer'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-vimeo-block>
<app-template-block class="block-wrapper" *ngSwitchCase="'template'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-template-block>
<app-button-block class="block-wrapper" *ngSwitchCase="'button'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-button-block>
<app-dialog-block class="block-wrapper" *ngSwitchCase="'dialog'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-dialog-block>
<app-actions-block class="block-wrapper" *ngSwitchCase="'actions'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-actions-block>
<app-variable-get class="block-wrapper" *ngSwitchCase="'variable-get'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-variable-get>
<app-variable-set class="block-wrapper" *ngSwitchCase="'variable-set'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-variable-set>
<app-switch-block class="block-wrapper" *ngSwitchCase="'switch'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-switch-block>
<app-batch-block class="block-wrapper" *ngSwitchCase="'batch'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-batch-block>
<app-multi-block class="block-wrapper" *ngSwitchCase="'multi'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-multi-block>
<app-event-dispatch-block class="block-wrapper" *ngSwitchCase="'dispatch'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-event-dispatch-block>
<app-csv-import-block class="block-wrapper" *ngSwitchCase="'csv-import'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-csv-import-block>
<app-csv-export-block class="block-wrapper" *ngSwitchCase="'csv-export'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-csv-export-block>
<app-card-block class="block-wrapper" *ngSwitchCase="'card'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-card-block>
<app-faker-block class="block-wrapper" *ngSwitchCase="'faker'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-faker-block>
<app-data-import-block class="block-wrapper" *ngSwitchCase="'import'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-data-import-block>
<app-data-export-block class="block-wrapper" *ngSwitchCase="'export'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-data-export-block>
<app-file-input-block class="block-wrapper" *ngSwitchCase="'file-input'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-file-input-block>
<app-parse-data-block class="block-wrapper" *ngSwitchCase="'parse-data'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-parse-data-block>
<app-file-export-block class="block-wrapper" *ngSwitchCase="'file-export'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-file-export-block>
<app-serialize-data-block class="block-wrapper" *ngSwitchCase="'serialize'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-serialize-data-block>
<app-launch-block class="block-wrapper" *ngSwitchCase="'launch'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-launch-block>
<app-db-block class="block-wrapper" *ngSwitchCase="'db'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-db-block>
<app-reference-block class="block-wrapper" *ngSwitchCase="'reference'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-reference-block>
<app-context-block class="block-wrapper" *ngSwitchCase="'context'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-context-block>
<app-auth0-profile-data-block class="block-wrapper" *ngSwitchCase="'auth0'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-auth0-profile-data-block>
<app-adapter-list-block class="block-wrapper" *ngSwitchCase="'adapter-list'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-adapter-list-block>
<app-adapter-info-block class="block-wrapper" *ngSwitchCase="'adapter-info'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-adapter-info-block>
<app-read-file-block class="block-wrapper" *ngSwitchCase="'read-file'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-read-file-block>
<app-context-save-block class="block-wrapper" *ngSwitchCase="'context-save'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-context-save-block>
<app-video-upload-block class="block-wrapper" *ngSwitchCase="'video-upload'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-video-upload-block>
<app-gosub-block class="block-wrapper" *ngSwitchCase="'gosub'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-gosub-block>
<app-gsheet-block class="block-wrapper" *ngSwitchCase="'gsheet'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-gsheet-block>
<app-map-block class="block-wrapper" *ngSwitchCase="'map'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-map-block>
<app-graphql-block class="block-wrapper" *ngSwitchCase="'graphql'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-graphql-block>
<app-validator-block class="block-wrapper" *ngSwitchCase="'validator'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-validator-block>
<app-load-auth-block class="block-wrapper" *ngSwitchCase="'load-auth'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-load-auth-block>
<app-xlsx-template-block class="block-wrapper" *ngSwitchCase="'xlsx-template'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-xlsx-template-block>
<app-validate-block class="block-wrapper" *ngSwitchCase="'validate'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-validate-block>
<app-audio-player-block class="block-wrapper" *ngSwitchCase="'player'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-audio-player-block>
<app-web-money-block class="block-wrapper" *ngSwitchCase="'web-money'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-web-money-block>
<app-wallet-block class="block-wrapper" *ngSwitchCase="'wallet'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-wallet-block>
<app-load-schema-block class="block-wrapper" *ngSwitchCase="'load-schema'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-load-schema-block>
<app-app-layout-block class="block-wrapper" *ngSwitchCase="'app-layout'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-app-layout-block>
<app-rename-fields-block class="block-wrapper" *ngSwitchCase="'rename-fields'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-rename-fields-block>
<app-mermaid-block class="block-wrapper" *ngSwitchCase="'mermaid'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-mermaid-block>
<app-comparison class="block-wrapper" *ngSwitchCase="'comparison'" [context]="context" [config]="block" [model]="models[i]" (output)="updateModel(i + 1, $event)"></app-comparison>
</ng-container>
</ng-container>
<!-- <pre>workblock: {{ context | json }}</pre> -->
<!-- blocks-workflow.component.html -->
<ng-container #blockHost></ng-container>
117 changes: 60 additions & 57 deletions src/app/components/blocks-workflow/blocks-workflow.component.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,82 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {clone} from 'lodash-es';
import { Component, OnInit, ViewChild, ViewContainerRef, ComponentFactoryResolver, Input, Output, EventEmitter, OnChanges } from '@angular/core';

import { FormBlockComponent } from 'src/app/blocks/form-block/form-block.component';
import { DebugBlockComponent } from 'src/app/blocks/debug-block/debug-block.component';
import { QueryBlockComponent } from 'src/app/blocks/query-block/query-block.component';
// Add other imports for other block components here

// Simple interface to describe the expected structure of dynamically loaded block components
interface DynamicBlockComponent {
output?: EventEmitter<any>;
// TODO: Add other common properties and methods here
}

// Mapping from block types to component classes
const blockComponentMapping = {
'form': FormBlockComponent,
'debug': DebugBlockComponent,
'query': QueryBlockComponent,
// TODO: Add other block types here
};

@Component({
selector: 'app-blocks-workflow',
templateUrl: './blocks-workflow.component.html',
styleUrls: ['./blocks-workflow.component.scss']
styleUrls: ['./blocks-workflow.component.scss'],
})
export class BlocksWorkflowComponent implements OnInit {
export class BlocksWorkflowComponent implements OnInit, OnChanges {
@ViewChild('blockHost', { read: ViewContainerRef }) blockHost: ViewContainerRef;

@Input() blocks = [];
@Input() models = [];
@Input() context = {};

@Output() workflowComplete = new EventEmitter();
// TODO: Use output events to emit data so that if this component is used in a parent component, the parent component can listen to the output events and get the data from the blocks

constructor() { }
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

ngOnInit() {
// Logging the blocks array for debugging purposes
console.log('BlocksWorkflowComponent init blocks:', this.blocks);
this.loadBlocks();
// let's log the blocks, models and context to see if they are being passed correctly
console.log('blocks:',this.blocks);
console.log('models:',this.models);
console.log('context:',this.context);
}

/**
* 🚨 AI generated documentation, TO BE VERIFIED:
*
* Purpose: Update the model state within the BlocksWorkflowComponent,
* essential for maintaining the application's current state as it processes various blocks.
*
* Parameters:
* - modelNumber (type: number): Index of the model to be updated (zero-based).
* - value (type: any): New value for the model at the specified index (modelNumber).
*
* Functionality:
* 1. Update Models Array: Creates a new array with updated model values using spread operator.
* - this.models.slice(0, modelNumber) creates a shallow copy up to the index modelNumber.
* - Adds 'value' at position modelNumber.
* - Adds the rest of the array elements after modelNumber.
* 2. Force Change Detection: Reassigns this.blocks to force Angular change detection.
* 3. Emit Workflow Completion: Emits workflowComplete event if updating process reaches final block.
*/
updateModel(modelNumber, value) {
// Logging the model index and new value for debugging purposes
console.log('BlocksWorkflowComponent updating modelNumber with value:', { modelNumber, value });
console.log('BlocksWorkflowComponent models:', this.models);

// Update the specified model in the models array:
// 1. Keep all models up to the specified index (modelNumber) unchanged.
// 2. Replace the model at the specified index (modelNumber) with the new value.
// 3. Retain all models after the specified index (modelNumber).
this.models = [
...this.models.slice(0, modelNumber), // Copy elements before modelNumber
value, // Insert the new value
...this.models.slice(modelNumber + 1) // Copy elements after modelNumber
];

// Force a change detection cycle in Angular by creating a new reference for the blocks array.
// This is necessary as Angular may not detect changes to the contents of an array.
this.blocks = [...this.blocks];

// Check if the current model update is for the final block in the workflow.
// If it is the final block, emit an event to signify the completion of the workflow.
if (modelNumber >= this.blocks.length) {
console.log('Emitting workflowComplete with value:', value);
this.workflowComplete.emit(value); // Emit workflow completion event

ngOnChanges(changes) {
if (changes.blocks || changes.models || changes.context) {
this.loadBlocks();
}
}
loadBlocks(this) {
console.log('loadBlocks is running with blocks:',this.blocks);
window['data'] = {"blocks": this.blocks, "models": this.models, "context":this.context};
this.blocks.forEach((block, index) => {
console.log('loadBlocks block:',block, 'index',index);
const blockComponent = blockComponentMapping[block.type];
if (blockComponent) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(blockComponent);
const componentRef = this.blockHost.createComponent(componentFactory);
console.log('loadBlocks blockComponent:',blockComponent, componentRef);

// Assign inputs to the component instance, adjust according to your block component's inputs
if (block.config) {
Object.assign(componentRef.instance, block.config);
}

// This function seems to be no longer used!
// src/app/blocks/actions-block/actions-block.component.ts does a similar thing though
// TODO: Verify that runWorkflow is no longer used and remove it
// Use type assertion with the DynamicBlockComponent interface so that TypeScript knows the type of the component instance
const instance = componentRef.instance as DynamicBlockComponent;

runWorkflow() {
if (this.models.length > 0) {
this.models[0] = clone(this.models[0]);
}
console.log(`Loading block: ${block.type}`, { config: block.config });

// Subscribe to the output EventEmitter if it exists
instance.output?.subscribe(output => {
console.log(`Output from block: ${block.type}`, output);
});
} else {
console.log(`No component mapped for type: ${block.type}`);
}
});
}
}

0 comments on commit 4a516a0

Please sign in to comment.