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

Binding to properties of bindings fails #639

Closed
Rich-Harris opened this issue Jun 14, 2017 · 3 comments
Closed

Binding to properties of bindings fails #639

Rich-Harris opened this issue Jun 14, 2017 · 3 comments
Labels

Comments

@Rich-Harris
Copy link
Member

A couple of errors here (no REPL link, because I can't access the gist API — sveltejs/v2.svelte.dev#97):

<select bind:value='selected'>
  {{#each tasks as task}}
    <option value='{{task}}'>{{task.description}}</option>
  {{/each}}
</select>

<!-- there should always be something selected -->
{{#if selected}}
  <label>
    <input type='checkbox' bind:checked='selected.done'> {{selected.description}}
  </label>
{{/if}}

<h2>Pending tasks</h2>
<!-- this updates correctly -->
<strong>{{tasks.filter(t => !t.done).map(t => t.description)}}</strong>

<!-- this does not -->
{{#each tasks.filter(t => !t.done) as task}}
<p>{{task.description}}</p>
{{/each}}

<script>
  export default {
    data () {
      return {
        tasks: [
          { description: "put your left leg in", done: false },
          { description: "your left leg out", done: false },
          { description: "in, out, in, out", done: false },
          { description: "shake it all about", done: false }
        ]
      };
    }
  };
</script>

Firstly, selected should (I think) default to the first task if not otherwise specified, because otherwise there's a discrepancy between state and UI.

Secondly, toggling the done state of a given task would ideally cause the each tasks... block to update. Right now, it doesn't, because the generated code looks like this:

// we don't update the `each` block, because it looks like
// 'tasks' hasn't changed
var each_block_value = state.tasks;
if ( 'tasks' in changed ) {
  for ( var i = 0; i < each_block_value.length; i += 1 ) {
  // ...
  }
}

// we're setting `selected` — we should also set `tasks`
function input_change_handler () {
  input_updating = true;
  var state = component.get();
  state.selected.done = input.checked;
  component._set({ selected: state.selected });
  input_updating = false;
}

Presumably, then, whenever we have a <select bind:value='foo.bar.baz'> binding, we need to add all the dependencies of the select's child <option> element values (just tasks in this case) as dependencies of selected, and any time we get the dependencies of selected, we also get its dependencies. In other words, we generate this code:

function input_change_handler () {
  input_updating = true;
  var state = component.get();
  state.selected.done = input.checked;
-  component._set({ selected: state.selected });
+  component._set({ selected: state.selected, tasks: state.tasks });
  input_updating = false;
}

It may be less straightforward in practice. Let's find out!

Rich-Harris added a commit that referenced this issue Jun 15, 2017
Sync state with view if <select> binding does not have initial value
Rich-Harris added a commit that referenced this issue Jun 15, 2017
mark indirect dependencies of <select> bindings
@hexagon6
Copy link

I stumbled upon this problem a long time ago (< svelte v1.0), but was not able pin down what exactly was going wrong.
Thank you for fixing this finally!

I would agree that the first option should be selected and it's bound values be updated.

I had to do workarounds like this: https://github.com/hexagon6/wave/blob/svelte/src/components/Algorithm.html#L21 to set the initial value.

@hexagon6
Copy link

Also this issue might be related to #502 but I am not sure.

@Rich-Harris
Copy link
Member Author

Fixed in recent versions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants