6

Let's say I want to create a multi-color picker with svelte, maybe to let the user choose a foreground color and a background color. My data model that looks like this:

{
  foreground: {
    r: 100,g:100,b:100
  },
  background: {
    r: 200,g:200,b:200
  }
};

So my app.js is

    import AppUI from './App.html';
import { Store } from 'svelte/store.js';

const defaultData = {
  foreground: {
    r: 100,g:100,b:100
  },
  background: {
    r: 200,g:200,b:200
  }
};

const store = new Store(defaultData);

window.store = store; // useful for debugging!

store.onchange(() => console.log('something changed'));

var app = new AppUI({
  target: document.querySelector( '#main' ),
  store
});

export default app;

Then I can build an RGBSelector component to reuse:

  <input type="range" min=0 max=255 step=1 bind:value=data.r/>{{data.r}}
  <input type="range" min=0 max=255 step=1 bind:value=data.g/>{{data.g}}
  <input type="range" min=0 max=255 step=1 bind:value=data.b/>{{data.b}}

And my App.html is pretty simple:

foreground:
<RGBSelector bind:data=$foreground/>

background:
<RGBSelector bind:data=$background/>

<script>
  import RGBSelector from './RGBSelector.html';

  export default {
    components: {
      RGBSelector
    }
  };
</script>

This seems to work, mostly. The two-way binding in the range inputs is working (the labels update), and the store is even being updated (verified by inspecting store._state in the console). So I believe the bind keywords in the RGBSelector are passing the change up to where they're declared in the App, which in turn is binding them to the store.

Trouble is, the store.onchange handler is not firing. Can anyone see what I'm doing wrong?

Full example: https://glitch.com/edit/#!/nonstop-hourglass

1 Answer 1

3

This is a bug in Svelte, not your app! It turns out component bindings don't play nicely with store — the bind:data=$foreground is just updating $foreground in your <App> component rather than updating foreground in your store.

Tracking the issue here: https://github.com/sveltejs/svelte/issues/1100

There isn't a great workaround sadly — until we get this fixed you would need to do something like this:

foreground: <RGBSelector bind:data=foreground/>
background: <RGBSelector bind:data=background/>
text: <Textinput bind:value=text/>

<script>
  import RGBSelector from './RGBSelector.html';
  import Textinput from './Textinput.html';

  export default {
    components: {
      RGBSelector, Textinput
    },

    oncreate() {
      this.observe('foreground', foreground => {
        this.store.set({ foreground });
      });

      this.observe('background', background => {
        this.store.set({ background });
      });

      this.observe('text', text => {
        this.store.set({ text });
      });
    }
  };
</script>

And in your JS file, this:

var app = new App({
  target: document.body,
  data: defaultData,
  store
});

If changes can go both ways, you'd also need to observe the store properties, taking care to prevent infinite update loops:

// inside `oncreate` — would also need to do this
// for `background` and `text`
let foregroundUpdating = false;

this.observe('foreground', foreground => {
  if (foregroundUpdating) return;
  foregroundUpdating = true;
  this.store.set({ foreground });
  foregroundUpdating = false;
});

this.store.observe('foreground', foreground => {
  if (foregroundUpdating) return;
  foregroundUpdating = true;
  this.set({ foreground });
  foregroundUpdating = false;
});

Recreating the functionality of bindings like this is obviously a bit cumbersome, so we'll try and get this bug fixed soon.

Sign up to request clarification or add additional context in comments.

1 Comment

Glad to know it's not just me :). The confusing thing about that theory, though, is that the store is updated when you slide the range elements, so I suspect what's going on is a bit subtler. Anyway thanks for digging into it, I'll keep an eye on the github issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.