0

Is there any way of making this work?

Javascript:

const vm = new Vue({
  el: "#app",
  data: {
    invoice: {
      title: "test"
    }
  }
})

Pug:

#app
  // This works
  h2 {{ invoice.title }}
  input(v-model="invoice.title")

  // This is the thing I want to work
  div(v-model="invoice")
    span {{ title }}

The thing I want to achieve is to say that "this whole container is bound to this object in the data. Every child of it should try to find the wanted data in that object and not in the root.

Let's say I have a lot of properties in the homeAddress object, then I don't want to have to write

.container
  span {{ homeAddress.municipality }}    
  span {{ homeAddress.streetNumber }}

but only

.container(v-model="homeAddress")    
  span {{ municipality }}
  span {{ streetNumber }}

Edit: I know I can do this by declaring a component for the container, but it feels overkill.

Solander
  • 327
  • 1
  • 2
  • 7

1 Answers1

1

The short answer is that there's no completely straightforward feature of Vue that gives you the scope-resetting equivalent of JavaScript's with.

To get a new scope, you need a component. And to make all the keys you passed in top-level data items in the component, you will need to do a little trick: have the data function return the prop.

The magic of inline-template allows you to reuse your component and match your HTML with whatever data you decide to pass in.

const vm = new Vue({
  el: "#app",
  data: {
    invoice: {
      title: "test"
    },
    homeAddress: {
      municipality: 'here',
      streetNumber: 'num'
    },
    someOtherThing: {
      first: 'one',
      second: 'two',
      third: 'three'
    }
  },
  components: {
    newScope: {
      props: ['scope'],
      data() {
        return this.scope;
      }
    }
  }
});
<script src="//unpkg.com/vue"></script>
<div id="app">
  // This works
  <h2>{{invoice.title}}</h2>
  <input v-model="invoice.title">
  <new-scope :scope="homeAddress" inline-template>
    <div>
      <span>{{ municipality }}</span>
      <span>{{ streetNumber }}</span>
    </div>
  </new-scope>
  <new-scope :scope="someOtherThing" inline-template>
    <ul>
      <li>{{ first }}</li>
      <li>{{ second }}</li>
      <li>{{ third }}</li>
    </ul>
  </new-scope>
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Thanks! Is this a good or bad practice? Is it recommended to do the homeAddress.streetNumber instead? – Solander Nov 30 '17 at 19:52
  • I would say it's fine as long as you aren't modifying values in the component (e.g., by using an `input` with `v-model`). Modifying values would work, but it is not a good practice for components to modify parent data. – Roy J Nov 30 '17 at 20:03
  • If you wanted a shorter alias, like to be able to say `ha.streetNumber` instead of `homeAddress.streetNumber`, you could do `v-for="ha in [homeAddress]"` – Roy J Nov 30 '17 at 20:06