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

feat: add template base signals and equality check #2484

Merged
merged 9 commits into from
Oct 28, 2024

Conversation

chrismclarke
Copy link
Member

@chrismclarke chrismclarke commented Oct 24, 2024

PR Checklist

  • PR title descriptive (can be used in release notes)

Description

Add signals to template base component for more fine-grained updates to row value, parameter_list and action_list updates

This will allow components to refer directly to value() property (instead of _row.value) and also apply computed effects

Review Notes

It isn't expected for this to have much impact as the properties are newly added. The only issues are with any components that provide their own definition for value, parameterList or actionList (instead of accessing via _row.value)

Looking at the tests there were a few components that did have conflicts and have been updated accordingly (would be good to confirm timer working as expected as that was slightly less trivial to update).

Dev Notes

This is a bit of a forward-looking update to provide devs the option of directly linking to row value or parameter list updates using signals. These should be more performant and reliable than responding to _row changes more generally.

The changes also include a custom equality checker, as the default for angular signals is the Object.is function which only matches equality for strict object equivalence e.g. same array but not two arrays with same entries so that

Object.is([],[])  // false

https://angular.dev/guide/signals#signal-equality-functions

I have only included the parameterList, actionList and value properties for now as I think these are the main columns likely to be updated dynamically (e.g. containing dynamic refs). Note that the condition column will always be interpreted outside of the template component itself (and not render the component if false), and hidden column dealt with at the container level.

I had wanted to also include a more general row() signal (if wanting to trigger changes generally when full row updates), however this would conflict with the existing row setter functions.

In the future it might be good to start refactoring some components to use the signals (particularly ones that use @Input() set row bindings), however this should be done on a case-by-case basis to check for any side-effects.

Git Issues

Closes #

Screenshots/Videos

If useful, provide screenshot or capture to highlight main changes

@chrismclarke chrismclarke marked this pull request as ready for review October 24, 2024 18:53
@github-actions github-actions bot added feature Work on app features/modules and removed feature Work on app features/modules labels Oct 24, 2024
Copy link
Collaborator

@jfmcquade jfmcquade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have for sure. The timer component code is a bit confusing to me but I'm not keen to refactor it now – everything seems to be working as expected from a functional perspective. I also added a minor code tidy in a63fdb1 to clean up a line that was throwing a typescript warning/error.

See my inline suggestion for the equality checker.

// handle deep comparison for arrays
if (Array.isArray(a)) {
const differentEl = a.find((v, i) => !isEqual(v, b[i]));
return differentEl ? false : true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit(blocking): This will throw a false positive if a differentEl is found that is falsey (e.g. if a contains a 0 element at an index that b has a different element, e.g. a = [0] and b = [1]).

How about this instead:

if (Array.isArray(a)) {
  if (a.length !== b.length) return false;
  return a.every((v, i) => isEqual(v, b[i]));
}

Copy link
Member Author

@chrismclarke chrismclarke Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice catch, you're right isEqual([true],[false]) would a true value.

I tend to not like every operators as there's really no reason to continue processing once you've found an element that differs (can add on a bit of time if dealing with very large arrays). Instead I've added 3a16bc9 which uses the findIndex operator to instead focus on the index of the element that differs and ignoring the specific value. I've also updated the spec test to catch this example

Update code and spec to handle case where arrays differ but the different value is falsy (and the `find` operator returns as such). Instead prefer to use `findIndex` to ignore the value and just look for index of mismatch
// find the first element index where there is a mismatch
// if all elements are the same `findIndex` returns value -1
const differentIndex = a.findIndex((v, i) => !isEqual(v, b[i]));
return differentIndex === -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this seems like a good way to do it. However, we still need a check that the arrays are of equal length first, otherwise it will return a false positive if b contains the same elements as a plus some additional elements. E.g. isEqual([1],[1,2]) will return true currently

Copy link
Member Author

@chrismclarke chrismclarke Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another good case, thanks for pointing out.
Added length check and updated test with 261a7d9

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also realised that will need to explicitly check b is an array as relying on previous typeof check could match object type for both [] or {} (or even dates or null objects)

Updated with b43d534

Copy link
Collaborator

@jfmcquade jfmcquade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, just a merge conflict to resolve now...

@chrismclarke chrismclarke merged commit 883280d into master Oct 28, 2024
6 checks passed
@chrismclarke chrismclarke deleted the feat/template-signals branch October 28, 2024 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Work on app features/modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants