4
votes

I am not able to compile my svelte component when using a get/set pair in custom methods. Is this not supported? Or am I doing something wrong?

Example:

Say I wanted to have a component that displays a name and I want to set the name using. com.name = 'The new name';

However I only want the component to use the name if it has no spaces in the name.

<h1>Hello {{name}}!</h1>

<script>
    export default {
        data () {
            return {
                name: 'The Name',
            }
        },
        methods: {
            get displayName() {
                return this.get('name'); 
            },
            set displayName(val) {
                if (val.indexOf(' ') < 0) {
                    this.set('name', val);
                }
            }
        }
    }
</script>

The Issue is that when I try to compile this, it says there is a duplicate key.

    Duplicate property 'displayName'

    49:             return this.get('name');
    50:         },
    51:         set displayName(val) {

Here is a REPL - https://svelte.technology/repl?version=1.13.2&gist=0eeab5717526694139ba73eae766bb30

I don't see anything in the documentation about this. I can just not use setters, but I would like to be able to.

2

2 Answers

6
votes

tl;dr this is possible with a wrapper object

The error message here is a bit confusing — it's not the duplicate property that's a problem, is that you can't have getters and setters in methods, which in any case is separate from the data object that populates a component's internal state (along with data supplied at instantiation and any computed values there might be). I've opened an issue for that here.

The data itself also can't have getters and setters — or rather it can, but they won't be used, because the object returned from your data function isn't the same as the internal state object (otherwise we'd all probably run into mutation-related bugs).

But it's actually fairly easy to create a wrapper object that allows you to get and set a component's data:

function wrap (component) {
  var wrapper = {};
  var data = component.get();

  Object.keys(data).forEach(key => {
    Object.defineProperty(wrapper, key, {
      get() {
        return component.get()[key];
      },
      set(value) {
        component.set({ obj[key]: value });
      }
    })
  });

  return wrapper;
}

var component = new Component({...});
var wrapper = wrap(component);

wrapper.name = 'Rich';

You could even do component.data = wrap(component) if you were so inclined — then you could manipulate component.data.name and so on.

I've put together a small repo demoing that approach — see it in action here.

2
votes

Edit: As Rich Harris pointed out in the comments below, getters and setters won't work within data because Svelte copies the properties to a plain JS object internally (thus ignoring getters and setters). I think the next best thing you can do is make a method name that can be called like name() as a getter, and name(value) as a setter.

Svelte Code:

<h1>Hello {{_name}}!</h1>

<script>
  export default {
    data() {
      return {
        _name: 'The Name'
      }
    },
    methods: {
      name(value) {
        if (value === void 0) return this.get('_name')
        this.set('_name', value)
      }
    }
  }
</script>

Original Post:

Your getter and setter should be in your data rather than your methods, because they create a property in the end. This property conflicts with the name you defined as equal to 'The Name' inside your original data method. I would suggest using a "private" property _name instead.

Svelte Code (REPL):

<h1>Hello {{name}}!</h1>

<script>
  export default {
    data() {
      return {
        _name: 'The Name',
        get name() {
          return this._name
        },
        set name(value) {
          / /.test(this._name) || (this._name = value)
        }
      }
    }
  }
</script>