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 two-way binding of Parse object properties with Parse.Object.bind.key = value #1484

Open
wants to merge 60 commits into
base: alpha
Choose a base branch
from

Conversation

dblythy
Copy link
Member

@dblythy dblythy commented May 24, 2022

New Pull Request Checklist

Issue Description

When using VueJS, it becomes difficult to manage Parse Objects via their native .get and .set syntax. Also, for newer developers who are use to standard JSON dot notation, the custom Parse Object getters and setters can be convoluted.

Related issue: #1313

Approach

Adds object.bind option that allows for getting and setting of Parse Object properties via dot notation.

TODOs before merging

  • Add tests
  • Add entry to changelog
  • Add changes to documentation (guides, repository pages, in-code descriptions)

@parse-github-assistant
Copy link

parse-github-assistant bot commented May 24, 2022

Thanks for opening this pull request!

  • ❌ Please link an issue that describes the reason for this pull request, otherwise your pull request will be closed. Make sure to write it as Closes: #123 in the PR description, so I can recognize it.

@dblythy
Copy link
Member Author

dblythy commented May 24, 2022

For those unfamiliar with VueJS, you can bind object properties and they are automatically updated by their respective component.

<template>
  <div id="app">
    <div>
      <input type="text" placeholder="Name" v-model="object.name"/>
      <br>
      Name: {{object.name}}
      <br>
      <button @click="save()">Save</button>
    </div>
  </div>
</template>
<script>
import { Parse } from 'parse';
export default {
  name: 'App',
  data() {
    return {
      object: new Parse.Object('Monster'),
    }
  },
  methods: {
    async save() {
      await this.object.save();
    },
  },
}
</script>
Screen.Recording.2022-05-24.at.9.15.39.pm.mov

@mtrezza mtrezza linked an issue May 24, 2022 that may be closed by this pull request
@codecov
Copy link

codecov bot commented May 24, 2022

Codecov Report

Patch coverage: 98.64% and project coverage change: -0.02 ⚠️

Comparison is base (010ed6c) 99.90% compared to head (afacddb) 99.88%.

❗ Current head afacddb differs from pull request most recent head 9de2ac5. Consider uploading reports for the commit 9de2ac5 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##            alpha    #1484      +/-   ##
==========================================
- Coverage   99.90%   99.88%   -0.02%     
==========================================
  Files          61       62       +1     
  Lines        6104     6176      +72     
  Branches     1482     1494      +12     
==========================================
+ Hits         6098     6169      +71     
- Misses          6        7       +1     
Impacted Files Coverage Δ
src/proxy.js 98.36% <98.36%> (ø)
src/ParseObject.js 99.89% <100.00%> (+<0.01%) ⬆️

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

@dblythy dblythy requested a review from a team May 25, 2022 21:24
@dblythy
Copy link
Member Author

dblythy commented May 26, 2022

As this feature matures we could perhaps ship it as a default with a new major version of Parse Server. I think it is a welcome improvement of usability to Parse. What do you think @mtrezza @Moumouls

@mtrezza
Copy link
Member

mtrezza commented May 26, 2022

This seems like a convenient feature, but I'm not sure this would work. How would you prevent collisions between a native JS object property and a Parse.Object property of the same name?

Related to this is parse-community/parse-server#7130 where the discussion goes into the contrary direction to allow any field name, because a developer shouldn't have to worry about or be limited by collisions with Parse Server internal or JS native field names.

It might be best to roll this out as experimental as it might have side effects with other Parse Objects.

We abandoned the concept of "experimental" features. If a feature is merged, it has the alpha / beta pre-release periods to mature, effectively 2 months minimum. If that's not enough because the feature is still too unstable or we haven't fully explored potential side effects, it won't be merged into the codebase for general stability and security reasons.

@dblythy
Copy link
Member Author

dblythy commented May 26, 2022

This seems like a convenient feature

I think convenience and ease of use are important - especially for newer developers. There is a learning curve with Parse and this feature makes Parse objects behave intuitively instead of the convoluted .get().get().get().get() syntax.

How would you prevent collisions between a native JS object property and a Parse.Object property of the same name

Can you give an example? I'm sure it would be easy enough to check if key === Object.getOwnPropertyNames() or something like that. The set/get behaviour of a Proxy can be completely customized.

Well, if it's not suitable for default usage, it would be good to have it as an option so Parse becomes a lot more usable with Vue. The only way to be assured that dot notation works for every single use case, would be to re-write every test with the additional dot notation syntax. I think for a new feature it's expected that it matures over time.

@mtrezza
Copy link
Member

mtrezza commented May 26, 2022

if it's not suitable for default usage, it would be good to have it as an option

As I said, we don't have the concept of experimental codebase changes at this time, even if part of the feature is a new Parse Server option. The feature would need to already have a certain level of maturity to enter the alpha branch, to a degree where it's reasonable to believe that it will become production ready within 1 month, before it moves to beta.

@dblythy
Copy link
Member Author

dblythy commented May 26, 2022

Well any initial release of software is expected to become more stable over time. All I’m saying is that there may be errors thrown specifically relating to the dot notation that might need refining / future PRs, which is expected of new features in an open source environment.

I’m still not convinced there’s enough reason not to consider this optional feature, especially as it dramatically increases usability with VueJS. If it’s a feature you wouldn’t use in your Parse environment, you don’t have to, but I don’t see why other developers should be restricted.

@mtrezza
Copy link
Member

mtrezza commented May 26, 2022

Sure, if there are no open concerns this PR will be merged like any other. What I'm saying is that at this time we don't merge a PR if we do not believe in best conscience that it is production ready. I'm not saying this PR is or is not production ready, that's a different question.

I've opened parse-community/parse-server#8016 to hopefully give you more insight into the challenges of "experimental" features.

@dblythy
Copy link
Member Author

dblythy commented May 27, 2022

Ahh, ok. Sorry we got a bit carried away. I'll continue to test this feature on my servers / sdks and let you know when I feel it's ready.

@mtrezza
Copy link
Member

mtrezza commented May 27, 2022

Always good to have a healthy debate. I think if this is technically feasible, it would be a nice feature to have, not just for Vue developers. I will pin the issue to hopefully give it more exposure and have more eyes on it.

Just thinking, if you enabled the feature in a branch and did a regex replace for all tests from .get(...) to dot notation, it may give you an indication whether there are any obvious issues.

The reason I think this is a PR we have to look at very carefully is because it removes the containment of Parse.Object attributes. Currently they are stored in Parse.Object.attributes and .get() is merely a shortcut. This helps to clearly distinguish between attributes and any other JS object property. Once this is short-circuited, it creates ambiguity.

@dblythy
Copy link
Member Author

dblythy commented Jun 2, 2022

if you enabled the feature in a branch and did a regex replace for all tests from .get(...) to dot notation, it may give you an indication whether there are any obvious issues.

That's a good idea, thanks.

The reason I think this is a PR we have to look at very carefully is because it removes the containment of Parse.Object attributes.

Well I think this could be a documented side-effect of this feature. The same functionally happens in the Swift SDK without any troubles. Proxy objects merely intercept gets and sets too, no data is stored on the respective keys.

For example, console.log on Object.keys(obj) only returns ['id', '_localId', '_objCount', 'className'], even when object.name = 'foo' is called (I just added a test for this).

@mtrezza
Copy link
Member

mtrezza commented Jun 2, 2022

Well I think this could be a documented side-effect of this feature.

What do you mean by "side-effect"? How should a developer interpret this? Are there any circumstances under which this change could break something?

@mtrezza
Copy link
Member

mtrezza commented Mar 6, 2023

Maybe the changelog entry could mention some keywords like "vue"? How about:

feat: Add two-way binding of Parse object properties with Parse.Object.bind.key = value for use with Vue.js

@dblythy
Copy link
Member Author

dblythy commented Mar 7, 2023

Let me have a quick look to see if applies to Svelte too

@dblythy
Copy link
Member Author

dblythy commented Mar 7, 2023

mov.mov

Feature is fully compatible with Svelte as well, with the only caveat being that to trigger reactivity on save, object = await object.save() must be called, whereas in Vue, await object.save() is enough.

Svelte demo: https://github.com/dblythy/Parse-Svelte-Binding

And for good measure, AngularJS demo: https://github.com/dblythy/Parse-Angular-Binding

@mtrezza
Copy link
Member

mtrezza commented Mar 7, 2023

Can the docs all be packed into the bind code comments? Or should we add a new section for Reactive Binding to the html docs?

@dblythy
Copy link
Member Author

dblythy commented Mar 8, 2023

I have added some JSDocs, but it should be part of the JS Guide as well

Copy link
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

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

Looks good, is this ready for merge?

@dblythy
Copy link
Member Author

dblythy commented Mar 8, 2023

Did you want to modify the docs at all? I didn't make too much effort with it as I figured you were likely to amend

@mtrezza
Copy link
Member

mtrezza commented Mar 9, 2023

Maybe this is something we could incorporate into the JSDoc:

Feature is fully compatible with Svelte as well, with the only caveat being that to trigger reactivity on save, object = await object.save() must be called, whereas in Vue, await object.save() is enough.

Could you formulate that in more generic terms?

Should we add code example snippets too for 2 way binding with vue, svelte and angular since you mention they use a different syntax?

@dblythy
Copy link
Member Author

dblythy commented Mar 10, 2023

Should we add code example snippets too for 2 way binding with vue, svelte and angular since you mention they use a different syntax?

Perhaps we can add this detail in the expanded Javascript Guide, rather than the JS Docs?

@mtrezza
Copy link
Member

mtrezza commented Mar 10, 2023

I'd only add to the docs what cannot go into JSDocs for some reason. I think keeping docs as close to the code as possible makes most sense, because:

  • Easier accessible to the developer in an IDE, right when and where they need it.
  • Easier to maintain than a separate document or even repository (as we know from the history of our docs).
  • Tends to encourage more concise docs rather that the often too verbose docs that we currently have.

A separate documentation may be more for in-depth explanations where necessary or more tutorial-style introductions. I personally believe that's what we should aim for, but we haven't introduced a definitive policy on that yet. Apple's own SDK docs are a good example.

So depending on what you have in mind and how extensive of an introduction you want to write, you can decide for one or the other. However, we definitely want to avoid duplicate docs in both places.

@@ -148,6 +150,17 @@ class ParseObject {
_objCount: number;
className: string;

/**
* Bind, used for two way directonal binding using
Copy link
Member

Choose a reason for hiding this comment

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

Needs completion

@dblythy
Copy link
Member Author

dblythy commented Mar 14, 2023

Working on improving Array support

@mtrezza
Copy link
Member

mtrezza commented Jun 22, 2023

@dblythy Couod you amend the docs? Then let's go ahead and merge this I'd say. Unless you found a weak spot in the meantime that should be fixed first.

@bpbastos
Copy link

Hello, do you have any news about this feature being released?

@mtrezza
Copy link
Member

mtrezza commented Jul 23, 2023

It seems that only the docs could benefit from some of the comments mentioned here, see #1484 (comment); then this could be merged.

@bpbastos
Copy link

It seems that only the docs could benefit from some of the comments mentioned here, see #1484 (comment); then this could be merged.

Great! I'm using the proxyHandler hack, but it seems to be causing side effects. I'm still new to Parse and Vue, and this feature will greatly ease the learning process.

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

Successfully merging this pull request may close these issues.

Add dot notation to get/set Parse.Object attributes
4 participants