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

Computed property not updating when based on new state object property and used by template #798

Closed
Mardoxx opened this issue Jul 3, 2018 · 1 comment
Labels

Comments

@Mardoxx
Copy link

Mardoxx commented Jul 3, 2018

Version

1.0.0-beta.20

Reproduction link

#798

Steps to reproduce

Add this to test/specs/wrapper/setData.spec.js

it('should append a new property to an object when the new property is referenced by a template', () => {
  const TestComponent = {
    data: () => ({
      anObject: {
        propA: 'a',
        propB: 'b'
      }
    }),
    computed: {
      anObjectKeys() {
        return Object.keys(this.anObject).join(',')
      }
    },
    template: `<div>{{ anObjectKeys }}</div>`
  }
  const wrapper = mountingMethod(TestComponent)
  wrapper.setData({
    anObject: {
      propC: 'c'
    }
  })

  expect(wrapper.vm.anObject.propA).to.equal('a')
  expect(wrapper.vm.anObject.propB).to.equal('b')
  expect(wrapper.vm.anObject.propC).to.equal('c')
  expect(wrapper.vm.anObjectKeys).to.equal('propA,propB,propC')
  expect(wrapper.html()).to.equal('<div>propA,propB,propC</div>')
})

What is expected?

expect(wrapper.vm.anObjectKeys).to.equal('propA,propB,propC')

What is actually happening?

The computed property is not being updated, when setData() is being called.


If you remove the reference to anObjectKeys from the template, it works fine - besides the last assertion, obviously.

Also fails in dev @ 1ee03ac

See also, #565

@eddyerburgh eddyerburgh added the bug label Jul 4, 2018
@Mardoxx
Copy link
Author

Mardoxx commented Jul 4, 2018

I think it's because it's not setting nested object properties through Vue.set(); and hence not setting up new watchers for new properties on these objects.. which, as long as they're not root objects, vue supports.

Changing wrapper.setData(data) impl to the below works..

For reference: https://vuejs.org/v2/guide/reactivity.html

Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value)

  /**
   * Sets vm data
   */
  setData (data: Object) {
    if (this.isFunctionalComponent) {
      throwError(
        `wrapper.setData() cannot be called on a functional ` +
        `component`
      )
    }

    if (!this.vm) {
      throwError(
        `wrapper.setData() can only be called on a Vue ` +
        `instance`
      )
    }

    let setDataRecursive = function(target, key, val) {
      if (
        typeof val === 'object' &&
        val !== null &&
        !Array.isArray(val)
      ) {
        Object.keys(val).forEach(valKey => { setDataRecursive(target[key], valKey, val[valKey]) })
      } else {
        Vue.set(target, key, val)
      }
    }

    Object.keys(data).forEach(key => { setDataRecursive(this.vm, key, data[key]) })
  }

All tests pass...

Not a JS dev, so idk if this is sensible, also have little exposure to vue..

HTH. 😄

I guess for correctness you should:

foreach (key, val in data) {
    if (val is object)
    {
        if (key in this.vm.$data) {
            // recurse
        }
    } else {
        // set value/array
    }
}

i.e. only setting properties of the hierarchy of objects that are defined... that is, only appending new properties.. not appending new objects.. if that makes sense? Although this is probably making too many assumptions

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