-
-
Notifications
You must be signed in to change notification settings - Fork 96
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(computedFrom): allow usage on method #624
Conversation
I apologize, I failed to close out the issue I had opened up long ago. It looks good. However, I'm not sure it solves any problems that couldn't already have been solved. See here for an updated demo: |
Putting parameters on the view to observe changes will be enough most of the time. Ability to add observers from view model will be a nice addition. It may help make the view clearer. For our examples, |
As far as I can tell, since both I guess my biggest concern is that it is a mix of the two strategies. If you have a method, you're likely passing an argument in already. One developer might write some sketchy code that another developer is forced to maintain, where a method takes a couple variables in, and computes from a couple variables as well. Perhaps that will introduce confusion to the second developer, wondering what the difference is between the two. As I said, they seem to be solving exactly the same problems with exactly the same tools, so I can't say it's a bad idea. It's only flaw is that its a bit late to the party. I probably won't use it, and I probably will continue to advise others to use the function parameter strategy. I don't think there is any reason not to include it, though. |
I don't think we should add the overhead to connect for such an edge case. There are so many better ways of doing this sort of thing. for example, this: class MyClass {
@computedFrom('firstName', 'lastName')
showWelcomeMessage(country) {
return `${messages[country].hello} to ${this.firstName} ${this.lastName}`;
}
} Is this in the view: <span>${messages[country].hello} to ${firstName} ${lastName}</span> |
The reason this remains a requirement is because by insisting that every dependency of a function be included in the view, we're polluting the view with implementation dependencies. |
I love what you've done in this article. But the one thing that is missing is my recommended strategy of passing dependencies to the function. For your "Uh-oh", your code would have become one of the following, value converter being my preference for this case: <div>delimit(fullName, ',')</div>
<div>fullName | delimit: ','</div> <!-- best practice --> delimit(text, delimiter) {
text.split(/\s/).join(delimiter)
}
I think this is the best argument. But even so, I see this really awkward push to take everything out of your view. I know your case is pedantic, but even so, I think the above examples are much more readable and usable than the alternative that would come out of this addition: <div>delimitFullName(')</div> @computedFrom('firstName', 'lastName')
delimitFullName(delimiter) {
return [this.firstName, this.lastName].join(delimiter);
}
It's a bit of a balancing act. If we took every request that I've seen, Aurelia would not be as clean and usable as it is today. Of course if we ignore every request, we won't stay relevant. As I mentioned above, I don't think this is horrible or even bad, and it wouldn't be a huge mistake to include it. I just think it's unnecessary and sub-optimal. I see this as more of a training issue than a gap in the existing tools. |
I was doing this PR with code reuse-ability in mind. By having a way to describe only the varying dependencies (parameters) in the view without losing observe-ability , I can easily call I think @jdanyow 's comment about performance is the strongest argument against this. |
@davismj I'm aware of why we prioritise or ignore change requests, and there are workarounds for problems like the one listed here. The case I'm describing is a real-world situation that crops up repeatedly, and moving logic into the view is not usually a well thought out option. In fact, indexes or any form of iterator are the worst culprit in this type of issue. <div repeat.for="p of perms" class="col-xs-6" if.bind="allowed($index)">
<div class="media clickable" click.trigger="togglePerm($index)">
<div class="media-left">
<button if.bind="enabledPerm($index)"
class="pill ${['pill--red','pill--green','pill--light-red','pill--light-green'][displayPerm($index, permissions, mask)]}">
${['Deny','Allow','(Inherit)','(Inherit)'][displayPerm($index, permissions, mask)]}
</button>
<button if.bind="!enabledPerm($index, permissions, mask)"
class="disabled pill ${['pill--red','pill--green','pill--light-red','pill--light-green'][displayPerm($index, permissions, mask)]}"
>
${['Deny','Allow','(Inherit)','(Inherit)'][displayPerm($index, permissions, mask)]}
</button>
</div>
</div>
</div> Examples of the functions bound to are:
In this case, Whether we like it or not, this is a situation that people hit, and we can work around it as shown, we can tell them they have to implement triggers manually, or we can implement dirty-checking. If finding a solution creates performance issues on all bindings then we obviously shouldn't do it that way, but if a solution can be found that only impacts on decorated function, I don't see any issue. |
How would you handle i18n with this approach? |
@jsobell It's hard for me to answer your post. Perhaps we can reach out and talk about it. My gut feeling is that there might be a better approach in a few ways for what you're doing above. For example, this code:
Might better be written:
Notice, I haven't made any changes to the logic or the variables available to either the view or the view-model. I'm just simply did the exact same thing while clearly writing down (a) there is a dependency on permissions and (b) I'm actually using that dependency. I've said multiple times that I don't think ${['Deny','Allow','(Inherit)','(Inherit)'][displayPerm($index)]} @computedFrom('perms', 'mask')
displayPerm(i: number) {
return this.getBitSeq(this.perms[i].bits);
} In this case there are a few problems. First of all, when looking at the view I have no idea that there are dependencies other than By the way, I think we solved the very same problem you're trying to solve here in this question. When we were done, permissions bindings looked like this: <div auth="disabled.bind: canEdit" disabled.bind="!editing"> It was a tricky problem and needed a tricky solution, but I think what we ended up with is extremely easy to develop with. |
At the nuts and bolts of the situation, we have a system that doesn't have a means of updating binds to The code link you posted is an interesting way of binding, but is still based on a single field, isDisabled or authorized. Try and include that code in a loop where <div auth="disabled.bind: canEditFn($index)" disabled.bind="!editingFn($index)"> or <div auth="disabled.bind: canEdit[$index]" disabled.bind="!editing[$index]"> (which opens up a whole new can of worms :) ) Since there are so many situation where this is unsuitable or unnecessarily complex, why don't we use the same polling option on functions that we provide by default on properties? After all, if it's really a once-only call to the function it will be decorated with |
This PR
computedFrom
decorator with error message when used on normal fieldUsage similar to
computedFrom
on getter:@EisenbergEffect @jdanyow @davismj @jsobell