Can't store VueComponent in data?

I’m working on a UI based Vue.js editor, and one of the requirements is being able to input any JavaScript object, but when I try to do

data(){
  self: undefined
},
created(){
  this.$set(this, 'self', this)
}

I get a memory limit reached error from the _traverse system, it seems like VueComponent’s can’t have ob added?

I was thinking of changing the seen system to fallback to an array of objects if there’s no ob to get the depId from.

Thoughts?

1 Like

The code provided is currently not properly formatted for this forum. In its current state it’s illegible which makes it hard for people to help you.

Please read the following guide about how to properly format code and then edit your topic accordingly.

Thanks!

ok fixed, any chance you know the answer?

You created an infinite loop by assinging the component to itself, as Vue tries to make that object reactive it trraverses its properties, and as one of those properties points to itself, it never finishes.

Additionally, the component object refers to HTMLElements (this.$el) which has properties that contain all of its childnodes etc. Those would be traversed as well, trying to make them reactive.

In short: That won’t work.
If you assign it as a non-recative property, it would be ignored by reactivity and not an issue:

data(){
 // self: undefined
},
created(){
  this.self = this
}

But I don’t understand why you would need such a reference in the first place?

But there’s a seen set for catching circular relationships right? Doesn’t Vue add an __ob__ to all reactive properties? To check if the depId has been seen while traversing?

Well we’re working on a GUI based Vue editor and we really want it to work with any valid JavaScript, editing objects, arrays, primitive values etc… and not being limited at all so any property of an object is editable, which should include a Vue component.

If I make it non-reactive that sort of defeats the purpose of using Vue? because we use a graph-like value.vue and edge.vue to render JavaScript states, and the values are rendered in the template.

Value.vue looks like this

<template>
.value
  template (v-if=`isPrimitive(self)`)
    input(:value=`self`)
  template(v-for=`for (edgeValue, edge) in self`)
    edge( :edge=`edge` :parent=`self`
</template>

And Edge.vue like

<template>
.edge {{ edge }}
  template(v-if=`open`)
    value(:self=`parent[edge]`)
</template>

This can more or less render any JavaScript “thing”!

And we’d like it to be reactive ? :stuck_out_tongue:

Thanks for the reply, I really appreciate it

Yeah the issue is likely more in the part about traversing the whole DOM through $el.

Why? the component’s properties itself are still reactive. Can this.self be reassigned to some other value dynamically? If so, your code doesn’t show that. If not, then it doesn’t have ot be reactive.

This really feels like a very bad idea. A Vue component has lots of internal and private properties that you should not mess with. Instead of exposing the component, you should maybe expose the $data, which represents the properties that you can actually change.

Ah yeah true, I might have to look at making the traverse function limitable to top-level properties because not everything has to be reactive, just the top level properties. Thanks

Ahh ok, will the template be re-rendered if the values in this.self get changed and it isn’t reactive but the values are used in the template? I’ll play around with that :open_mouth:

We want source code to be editable :stuck_out_tongue: in fact we want any code to be editable haha!!

Thanks so much for your reply again, appreciate it greatly

Yes.

Good luck with that ^^

1 Like

Is it possible to pass non-reactive props?

In what way?

The traverse error happens again cause passing the VueComponent as a prop to another component makes it reactive, cause all props are reactive right?

So even doing this.something = this but the passing

<template>
.something
  subSomething(
    :subSomething=`something`
  )
<\template>

Gives the memory limit traverse error again :stuck_out_tongue: from subSomething component

No. Vue does not make the content of props reactive. If the object you pass is not reactive, it’s not being made reactive.

The reactivity must be added in another part of your code.

oh really!! hmm!?

I’ll see what’s going on then sorry, thanks <3

you were right, really sorry

I just got it running, but I can’t get the template to re-render?

This doesn’t work: https://jsfiddle.net/6u7p0mdc/2/

but this does: https://jsfiddle.net/6u7p0mdc/3/

Just by adding a reactive property to the template, it re-renders the non-reactive stuff, is there a way to do this without that kind of hack?

No. That’s kind of the point of Vues reactivity system: only re-render when a reactive dependency changes.

I still have no real idea what you are building, but I’ll repeat it seems like a bad idea.

1 Like

You can try this:

global_updater = new Vue({data: {toggler:false}})
some  = new Vue({
    created(){
           this.non_react_value = 'foo'
    }, 
    computed:{
            value(){
                // now  `value` will be recomputed if global_updater.toggle is changed  
                 global_updater.toggler 
                  return this.non_react_value
            }
    }
})

Does that mean I have to change toggler every time I make a change? I’m working on making Vue observe anything, just trying to figure out why VueComponent’s don’t get ob added when they’re set as reactive properties.

I fixed traversed with a

var seenObjects = new _Set();
var seenThings = []
/**
 * Recursively traverse an object to evoke all converted
 * getters, so that every nested property inside the object
 * is collected as a "deep" dependency.
 */
function traverse (val) {
  _traverse(val, seenObjects);
  seenObjects.clear();
	seenThings = []
}

function _traverse (val, seen) {
  var i, keys;
  var isA = Array.isArray(val);
  if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
    return
  }
  if (val.__ob__) {
    var depId = val.__ob__.dep.id;
    if (seen.has(depId)) {
      return
    }
    seen.add(depId);
  } else if(typeof val == "object"){
    if(seenThings.indexOf(val) >= 0){
      return
    } else {
      seenThings.push(val)
    }
  }

  if (isA) {
    i = val.length;
    while (i--) { _traverse(val[i], seen); }
  } else {
    keys = Object.keys(val);
    i = keys.length;
    while (i--) { _traverse(val[keys[i]], seen); }
  }
}

It’s like a DevTools interface but the changes you make are persisted, like bubble.io or webflow but there’s no building, you just edit the app straight up through properties, there are modes you can jump in and out of like edit-mode/user-mode.

So the root Vue instance is basically window.vue = new Vue({...srcCode, ...userSrcCode})

where userSrcCode is either a localStorage.getItem("userSrcCode") or a axios.get() call

Even further than that, it will actually be more like

<!-- index.html -->
<html>
  <head>
    <script>
      var xmlHttp = new XMLHttpRequest();
      xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
      xmlHttp.send( null );
      let srcCode = JSON.stringify(xmlHttp.responseText) // { execute: Function }
      srcCode.execute()
    </script>
  </head>
</html

And then the JSON is editable like a DevTools/debugger situation where you can edit key/values and whatever else, but you can also style it, and do other stuff like toggle the application of styles and the vue template and stuff!!

let me know what you think :slight_smile:

What kind of properties are you going to edit? red / green, small / big? If you want to edit js-logic (templates / methods / computed) “on the fly”, how are you going to catch errors and debug them? What if need to edit 2 or more components at once (add props to child - need to add passing props from parent)? - I can’ t imagine the workflow. Maybe you need something like

<template v-for = 'obj in objects'>
    <component :is = 'obj.component' v-bind = 'obj.props' v-on = 'obj.events' ></component>
</template>

Does that make sense at all?

Not exactly sure what you mean by this? Any sub-components will show up under the $children property, but the idea is we’ll add an open-file like system but instead of files it’s JavaScript paths, like you can type in window.x.y.z and start editing it, and because you can edit property values or add properties, you can just add a new property to the object you’re currently editing called someValue: window.x.y.z and start editing it that way

We also want to conserve the graph like nature of Javascript, so if you were to serialise the object with someValue and then deserialise it, it would see that one of the properties was a pointer to window.x.y.z and will merge them using some strategy…

Ohhhhh I think I get what you mean by this!! Genius, yeah we will need this!! But the component that stores even that template should be editable too, that’s why I’m starting with the Javascript editing part of it.

Any property, the idea is to implement all of JavaScript within a GUI, just like a devtools inspector but pretty and social. Catching errors will be hard, might need a lot of wrapper try-catch statements, and a console output or notification link, we’re using quasar ontop of Vue too. Debugging will have advantages and disadvantages, because the app itself is based on the running JavaScript, you can inherently see what’s going on anywhere you want, but if something breaks, like on Linux if you delete all files, you’re fked, or on windows the system32 folder. And then the only way to fix is to reinstall.

With this, the only way to fix a serious issue would be hoping on a chrome devtools

one yet problem: methods and computed may use some functions/objects from the closure (if component imports something - it uses closure), so you can’t just replace it as properties without building, i.e. you have to rebuild whole component