Skip to content

Commit

Permalink
Angular SDK: address editor perf issues (#3461)
Browse files Browse the repository at this point in the history
  • Loading branch information
samijaber committed Aug 8, 2024
1 parent c215cf8 commit 22a3865
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/seven-bears-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/sdk-angular': patch
---

Fix: Symbol infinite re-render and editor overlay not showing up.
60 changes: 51 additions & 9 deletions packages/sdks/mitosis.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,49 @@ const filterActionAttrBindings = (json, item) => {
});
};

/**
* @type {Plugin}
*/
const ANGULAR_ADD_UNUSED_PROP_TYPES = () => ({
json: {
post: (json) => {
if (json.name === 'BuilderImage' || json.name === 'BuilderSymbol') {
json.hooks.onMount = json.hooks.onMount.filter(
(hook) =>
!hook.code.includes(
'/** this is a hack to include the input in angular */'
)
);
}
return json;
},
},
});

/**
* @type {Plugin}
* We explicitly add the builder-id attribute to the symbol component for Angular,
* because mitosis doesn't support spreading `props.attributes` yet.
*
*/
const ANGULAR_FIX_SYMBOL_BUILDER_ID_ATTRIBUTE = () => ({
json: {
post: (json) => {
if (json.name === 'BuilderSymbol') {
json.children[0].bindings['builder-id'] = {
code: "props.attributes['builder-id']",
type: 'single',
};
}
return json;
},
},
});

// for fixing circular dependencies
/**
* @type {Plugin}
*/
const ANGULAR_FIX_CIRCULAR_DEPENDENCIES_OF_COMPONENTS = () => ({
code: {
post: (code) => {
Expand All @@ -235,14 +277,12 @@ const ANGULAR_OVERRIDE_COMPONENT_REF_PLUGIN = () => ({
code: {
post: (code) => {
if (code.includes('component-ref, ComponentRef')) {
code = code.replace(
'<ng-container *ngFor="let child of blockChildren; trackBy: trackByChild0">',
'<ng-container *ngIf="componentRef">\n<ng-container *ngFor="let child of blockChildren; trackBy: trackByChild0">'
);
code = code.replace(
'</ng-container>',
'</ng-container>\n</ng-container>'
);
code = code
.replace(
'<ng-container *ngFor="let child of blockChildren; trackBy: trackByChild0">',
'<ng-container *ngIf="componentRef">\n<ng-container *ngFor="let child of blockChildren; trackBy: trackByChild0">'
)
.replace('</ng-container>', '</ng-container>\n</ng-container>');
}
return code;
},
Expand All @@ -254,7 +294,7 @@ const ANGULAR_BLOCKS_WRAPPER_MERGED_INPUT_REACTIVITY_PLUGIN = () => ({
post: (code) => {
if (code?.includes('blocks-wrapper, BlocksWrapper')) {
const mergedInputsCode = code.match(/this.mergedInputs_.* = \{.*\};/s);
code = code.replace('ngOnInit', 'ngAfterViewInit');
code = code.replace('ngOnInit', 'ngAfterContentInit');
code = code.replace(
/}\n\s*$/,
`
Expand Down Expand Up @@ -532,13 +572,15 @@ module.exports = {
state: 'class-properties',
plugins: [
ANGULAR_FIX_CIRCULAR_DEPENDENCIES_OF_COMPONENTS,
ANGULAR_FIX_SYMBOL_BUILDER_ID_ATTRIBUTE,
ANGULAR_OVERRIDE_COMPONENT_REF_PLUGIN,
ANGULAR_COMPONENT_NAMES_HAVING_HTML_TAG_NAMES,
INJECT_ENABLE_EDITOR_ON_EVENT_HOOKS_PLUGIN,
ANGULAR_INITIALIZE_PROP_ON_NG_ONINIT,
ANGULAR_BIND_THIS_FOR_WINDOW_EVENTS,
ANGULAR_WRAP_SYMBOLS_FETCH_AROUND_CHANGES_DEPS,
ANGULAR_BLOCKS_WRAPPER_MERGED_INPUT_REACTIVITY_PLUGIN,
ANGULAR_ADD_UNUSED_PROP_TYPES,
],
},
solid: {
Expand Down
25 changes: 17 additions & 8 deletions packages/sdks/output/angular/scripts/multi-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,24 @@ const alias = getEvaluatorPathAlias();
const folderName = alias['placeholder-runtime'];
transformFile(chooseEvalFile, folderName);

// Run the Angular build command
execSync(`ng build --project sdk-angular`, { stdio: 'inherit' });
let caughtError = undefined;
try {
// Run the Angular build command
execSync(`ng build --project sdk-angular`, { stdio: 'inherit' });

// Remove any `package.json` generated inside the `lib/*` folder
const packageJsonPath = path.resolve(outputPath, 'package.json');
// Remove any `package.json` generated inside the `lib/*` folder
const packageJsonPath = path.resolve(outputPath, 'package.json');

if (fs.existsSync(packageJsonPath)) {
fs.unlinkSync(packageJsonPath);
if (fs.existsSync(packageJsonPath)) {
fs.unlinkSync(packageJsonPath);
}
} catch (error) {
caughtError = error;
} finally {
// Revert the choose-eval.ts file to its original state i.e, "placeholder-runtime"
transformFile(chooseEvalFile, folderName, true);
}

// Revert the choose-eval.ts file to its original state i.e, "placeholder-runtime"
transformFile(chooseEvalFile, folderName, true);
if (caughtError) {
throw caughtError;
}
32 changes: 21 additions & 11 deletions packages/sdks/overrides/angular/src/components/dynamic-div.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,31 @@ export default class DynamicDiv {
this.setAttribute(el, 'aria-hidden', this.ariaHidden);
}

ngOnChanges() {
ngOnChanges(changes) {
const el = this.v && this.v.nativeElement;
if (!el) {
return;
}
this.setAttributes(el, this.attributes);
this.setAttributes(el, this.showContentProps);
this.setAttribute(el, 'class', this.classProp);
this.setAttribute(el, 'style', this.style);
this.setAttribute(el, 'builder-parent-id', this.builderParentId);
this.setAttribute(el, 'builder-path', this.builderPath);
this.setAttribute(el, 'builder-model', this.builderModel);
this.setAttribute(el, 'builder-content-id', this.builderContentId);
this.setAttribute(el, 'hidden', this.hidden);
this.setAttribute(el, 'aria-hidden', this.ariaHidden);

if (Object.keys(changes).length === 0) {
return;
}

if (changes.attributes) this.setAttributes(el, this.attributes);
if (changes.showContentProps) this.setAttributes(el, this.showContentProps);
if (changes.classProp) this.setAttribute(el, 'class', this.classProp);
if (changes.style) this.setAttribute(el, 'style', this.style);
if (changes.builderParentId)
this.setAttribute(el, 'builder-parent-id', this.builderParentId);
if (changes.builderPath)
this.setAttribute(el, 'builder-path', this.builderPath);
if (changes.builderModel)
this.setAttribute(el, 'builder-model', this.builderModel);
if (changes.builderContentId)
this.setAttribute(el, 'builder-content-id', this.builderContentId);
if (changes.hidden) this.setAttribute(el, 'hidden', this.hidden);
if (changes.ariaHidden)
this.setAttribute(el, 'aria-hidden', this.ariaHidden);
}

private setAttributes(el: HTMLElement, attributes: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ElementRef,
Input,
Renderer2,
SimpleChanges,
TemplateRef,
ViewChild,
ViewContainerRef,
Expand Down Expand Up @@ -133,7 +134,9 @@ export class DynamicImage {
}
}

ngOnChanges() {
ngOnChanges(changes: SimpleChanges) {
if (!changes.attributes) return;

const el = this.v && this.v.nativeElement;
if (el && this.attributes) {
Object.keys(this.attributes).forEach((key) => {
Expand Down
23 changes: 22 additions & 1 deletion packages/sdks/src/blocks/image/image.lite.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Show, useMetadata, useStore } from '@builder.io/mitosis';
import {
onMount,
Show,
useMetadata,
useStore,
useTarget,
} from '@builder.io/mitosis';
import type { JSX } from '@builder.io/mitosis/jsx-runtime';
import { getSrcSet } from './image.helpers.js';
import type { ImageProps } from './image.types.js';
Expand Down Expand Up @@ -62,6 +68,21 @@ export default function Image(props: ImageProps) {
return out;
},
});

onMount(() => {
useTarget({
angular: () => {
/** this is a hack to include the input in angular */
const _ = {
a: props.lockAspectRatio,
b: props.width,
c: props.height,
d: props.lazy,
e: props.attributes,
};
},
});
});
return (
<>
<picture>
Expand Down
2 changes: 2 additions & 0 deletions packages/sdks/src/blocks/image/image.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { BuilderBlock } from '../../types/builder-block.js';

export interface ImageProps {
attributes?: string;
highPriority?: boolean;
className?: string;
image: string;
Expand All @@ -13,6 +14,7 @@ export interface ImageProps {
backgroundPosition?: string;
srcset?: string;
aspectRatio?: number;
lockAspectRatio?: boolean;
children?: any;
fitContent?: boolean;
builderBlock?: BuilderBlock;
Expand Down
9 changes: 8 additions & 1 deletion packages/sdks/src/blocks/symbol/symbol.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ export default function Symbol(props: SymbolProps) {
react: () => {},
reactNative: () => {},
solid: () => {},
angular: () => {},
angular: () => {
/** this is a hack to include the input in angular */
const _ = {
a: props.dataOnly,
b: props.inheritState,
c: props.renderToLiquid,
};
},

default: () => {
state.setContent();
Expand Down
1 change: 1 addition & 0 deletions packages/sdks/src/blocks/symbol/symbol.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export interface SymbolProps
dynamic?: boolean;
attributes?: any;
inheritState?: boolean;
renderToLiquid?: boolean;
}

0 comments on commit 22a3865

Please sign in to comment.