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

props change not refresh dom when {^{prop}} {^{if}} #428

Closed sany98215 closed 4 years ago

sany98215 commented 5 years ago
<!DOCTYPE html>
<!-- To run the current sample code in your own environment, copy this to an html page. -->

<html>
<head>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script src="https://www.jsviews.com/download/jsviews.min.js"></script>
  <link href="https://www.jsviews.com/samples/samples.css" rel="stylesheet" />
</head>
<body>

<div id="team"></div>

<script id="teamTemplate" type="text/x-jsrender">
  <button data-link="{on addMember}">Add</button>
  <button data-link="{on replaceMembers}">Replace</button>
  <button data-link="{on changeMembers}">Change</button>
  <ol>
    {^{props members}}
      <li>
        {^{if prop.startsWith("Peter")}}
        <select data-link="prop"/>
        {{else}}
        <input data-link="prop"/>
        {{/if}}
        {^{>prop}}
        <span class="remove" data-link="{on ~root.removeMember key}"></span>
      </li>
    {{else}}
      There are no members
    {{/props}}
  </ol>
</script>

<script>
var team = {
  members: {m1: "Robert", m2: "Sarah"},
  addMember: function() {
    $.observable(this.members).setProperty("n" + cnt, "new" + cnt++);
  }, 
  removeMember: function(key) {
    $.observable(this.members).removeProperty(key);
  },
  replaceMembers: function() {
    $.observable(this).setProperty("members", {m1: "Peter", m2: "Octavia", m3: "Xavier"});
  },
  changeMembers: function() {
    for (var property in this.members) {
      if (property !== $.expando) {
        $.observable(this.members).setProperty(property, this.members[property] + cnt++);
      }
    }
  }
},
cnt = 1;

$.templates("#teamTemplate").link("#team", team);
</script>

</body>
</html>
sany98215 commented 5 years ago

version 1.0.2 . when prop change to Perter***, input element do not change to select

BorisMoore commented 5 years ago

Thanks for this. I'm travelling for the rest of June, so may not be able to look closer at this until I return...

johan-ohrn commented 5 years ago

I created this fiddle to give a shorter demonstration of the problem. Type into the textbox with the console open. You'll see that 'b' is logged each time you type something but 'a' is only logged on initial rendering. I believe that jsviews might incorrectly be treating the .startsWith... expression as a property path and is monitoring it for changes instead of the test property.

BorisMoore commented 5 years ago

Thanks very much for beginning to look into this!

Yes, I agree with your explanation, Johan, and in fact for it to work as intended you have to write test^startsWith(...).

I think that it actually makes sense to require that syntax if you want to 'observe' changes to test. For any property path a.b.c.d or a().b().c().d() or some mixture a.b().c.d() it should make no difference whether they are objects or strings and whether the 'members' are instance properties or prototype members. And those with parens may be simple functions, or computed properties, with depends etc.

Either way you are listening to leaf changes only, unless you use the ^ separator.

I appreciate that people may expect test.startsWith() to 'listen' to changes in the test string, but to introduce special behavior no longer based on the syntactical rules, but instead based on detecting that test is a string and startsWith() is in the String prototype, could be difficult to implement, buggy, and unpredictable in behavior....

That's my initial take. What do you think?

BTW another simple test case would be:

<input data-link="test" />
{^{:test^indexOf('a') }}
sany98215 commented 5 years ago

Thanks for your reply.I understand what is it.

BorisMoore commented 4 years ago

OK - I'll close this.