BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
856 stars 130 forks source link

Update data at upper level on low level event #358

Closed in32bit closed 7 years ago

in32bit commented 7 years ago

What is the correct way to link the lower level event to the upper one like in this case:

http://jsfiddle.net/in32bit/HDZ5u/56/

Currently i'm using $.observable(parent).refresh(parent) to do update the label and the visible attribute.

Thank you.

Paul-Martin commented 7 years ago

There are several ways, but the simplest for your specific example is to use a 'depends' function

myHelpers.redBallsCount.depends = myHelpers.greenBallsCount.depends = function() {
  return [this.balls, 'length'];
}

And then update your template so that the red and green counts are data-linked

<div data-link="visible{:~redBallsCount() > 0}">Red Balls: {^{:~redBallsCount()}}</div>
<div data-link="visible{:~greenBallsCount() > 0}">Green Balls: {^{:~greenBallsCount()}}</div>
in32bit commented 7 years ago

Thank you for your answer.

This solution work. But it make the code less clean (important in my case of a huge single page app). I wish it was possible to define the dependency at the helper function scope.

Thank you.

Paul-Martin commented 7 years ago

How about this - use a converter.

<div data-link="visible{:~redBallsCount() > 0}">Red Balls: <span data-link="{count:'red' balls balls.length}"></span></div> 
<div data-link="visible{:~greenBallsCount() > 0}">Green Balls: <span data-link="{count:'green' balls balls.length}"></span></div> 

then register global converter

$.views.converters({
    count : function(color, balls) {
    return (balls||[]).filter(function (a) {
                return a.color == color;
            }).length;  
  }
});

This works without declaring a dependency by passing 3rd parameter that is unused 'balls.length' to get the necessary data-linking on changes to array elements.

There are many ways to achieve this. Perhaps if you create a more real world example we can give you more specific solution. I have an extremely large single page app based on jsviews. I'm pretty confident there is an elegant solution to what you are trying to do.

BorisMoore commented 7 years ago

Yes, there are several ways you can do it.

If you use the depends, you can make it simpler:

redBallsCount.depends = "balls.length";

But you can also use the helper approach without depends if you instead pass parameters like Paul suggested with the converter approach - :

{^{:redBallsCount(balls.length)}}

or

{^{:count(color, balls, balls.length}}
BorisMoore commented 7 years ago

Incidentally, I don't see an encapsulation problem in declaring the depends. You associate the depends with the function itself, not in the context of its use as helper:

function foo() { ... }
foo.depends = ....

Then you use the foo function where you want. You can even use the same function as a converter and as a helper:

myHelpers = {useFoo: foo};
$.converters(fo: foo});

then

{^{:~useFoo(...)}}
{^{fo: someExpression}}
in32bit commented 7 years ago

Thanks you for your help and sorry for the late respond. I finally declared the depends to the helpers method.

Thanks you again.