Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The ngOnChanges #12

Open
ezzabuzaid opened this issue Aug 3, 2021 · 0 comments
Open

The ngOnChanges #12

ezzabuzaid opened this issue Aug 3, 2021 · 0 comments

Comments

@ezzabuzaid
Copy link
Owner

ezzabuzaid commented Aug 3, 2021

So far we can completely create dynamic components, but we can't use ngOnChanges lifecycle since it doesn't react to @Input changes therefore we have to do this manually.

Another way to do this is to change the @Input field that concerned you to have getter and setter, so you can know when a change happens, but it is not a favorable option so let's stick ngOnChanges.

Let's start with creating changes object for the component.
Basically, do a loop over new inputs (currentInputs) and compare each input with the previous one, in case of change we add it as changed input to the changes object

private makeComponentChanges(inputsChange: SimpleChange, firstChange: boolean): Record<string, SimpleChange> {
  const previuosInputs = inputsChange?.previousValue ?? {};
  const currentInputs = inputsChange?.currentValue ?? {};
  return Object.keys(currentInputs).reduce((acc, inputName) => {
  const currentInputValue = currentInputs[inputName];
  const previuosInputValue = previuosInputs[inputName];
  if (currentInputValue !== previuosInputValue) {
      acc[inputName] = new SimpleChange(firstChange ? undefined : previuosInputValue, currentInputValue, firstChange);
  }
  return acc;
  }, {} as Record<string, SimpleChange>);
}

Now, we have to manually call the ngOnChanges from the component instance if the component declared it and pass changes as an argument.
Changing the previous function to have the functionality

ngOnChanges(changes: SimpleChanges): void {
    // ensure component is defined
  assertNotNullOrUndefined(this.component);
  
  let componentChanges: Record<string, SimpleChange>;
  const shouldCreateNewComponent =
      changes.component?.previousValue !== changes.component?.currentValue
      ||
      changes.injector?.previousValue !== changes.injector?.currentValue;
  
  if (shouldCreateNewComponent) {
      this.destroyComponent();
      this.createComponent();
      // (1) 
      componentChanges = this.makeComponentChanges(changes.inputs, true);
  }
  // (2)
  componentChanges ??= this.makeComponentChanges(changes.inputs, false);
  
  assertNotNullOrUndefined(this.componentFactory);
  assertNotNullOrUndefined(this.componentRef);
  
  this.validateOutputs(this.componentFactory.outputs, this.outputs ?? {}, this.componentRef.instance);
  this.validateInputs(this.componentFactory.inputs, this.inputs ?? {});

  // (3)
  if (changes.inputs) {
      this.bindInputs(this.componentFactory.inputs, this.inputs ?? {}, this.componentRef.instance);
  }

  // (4)
  if (changes.outputs) {
      this.subscription.next(); // to remove old subscription
      this.bindOutputs(this.componentFactory.outputs, this.outputs ?? {}, this.componentRef.instance);
  }

  // (5)
  if ((this.componentRef.instance as OnChanges).ngOnChanges) {
      this.componentRef.instance.ngOnChanges(componentChanges);
  }
}
  1. Create changes object with firstChange as true after creating the component.
  2. In case the component didn't change that means only the inputs or outputs did change so we create changes object with firstChange as false.
  3. Rebind the inputs only if they did change.
  4. Rebind the outputs only if they did change.
  5. Calling component ngOnChanges lifecycle with the possible inputs changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant