Skip to content

Instantly share code, notes, and snippets.

@BinarySpike
Last active November 16, 2018 17:07
Show Gist options
  • Save BinarySpike/aa4f82fd024265b72fcde1327c7b0f09 to your computer and use it in GitHub Desktop.
Save BinarySpike/aa4f82fd024265b72fcde1327c7b0f09 to your computer and use it in GitHub Desktop.
Vue error when wrapping model in ObservableSlim
<!DOCTYPE html>
<html>
<head>
<title>ObservableSlim with Vue Error</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="js/ObservableSlim.js"></script>
</head>
<body>
<div id="app">
{{ model }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
model:
{
messageArray: [],
messageText: "unchanged",
}
}
})
function vueChange(changes) {
// applies changes to the underlying vue model
changes.forEach(function (item) {
//let path = item.currentPath.substring(0, item.currentPath.lastIndexOf("."));
let prop = item.currentPath.substring(item.currentPath.lastIndexOf(".") + 1, item.currentPath.length);
Vue.set(item.target, prop, item.newValue);
});
}
function noChange(changes) {
// do nothing
// specifically: do not call Vue.set
}
function copyChange(changes) {
// copies changes onto the base model, implies item.target does not point to app.model
changes.forEach(function (item) {
let path = item.currentPath.substring(0, item.currentPath.lastIndexOf("."));
let prop = item.currentPath.substring(item.currentPath.lastIndexOf(".") + 1, item.currentPath.length);
let obj = _.get(app.model, path) || app.model; // when path is empty, _.get returns undefined. Coalesce into the base value.
Vue.set(obj, prop, item.newValue);
});
}
function test1() {
// creates a 'default' obslim with domDelay and lets obslim commit the changes
// messageDelayedNewProp never appears in the model (non-reactive)
let dataModel = ObservableSlim.create(app.model, true, noChange);
dataModel.messageText = [];
dataModel.messageText.push("Test"); // works
setTimeout(function () { // delay to simulate dom event or devconsole changes.
dataModel.messageDelayedNewProp = { test: "Test" }; // not reactive!
}, 500);
}
function test2() {
// creates a 'default' obslim as above, but makes an immediate non-reactive change and a delayed non-reactive change.
// observable/proxy/obslim is intended to handle non-reactive changes and make them reactive.
// neither InitialNewProp nor messageDelayedNewProp appear on the model
let dataModel = ObservableSlim.create(app.model, true, noChange);
dataModel.InitialNewProp = {} // is not reactive, does not show up
setTimeout(function () { // delay to simulate dom event or devconsole changes.
dataModel.messageDelayedNewProp = { test: "Test" }; // not reactive!
}, 500);
}
function test3() {
// creates a 'default' obslim as above, but makes an immediate non-reactive, reactive, non-reactive, then delayed non-reactive change.
// immediate non-reactive are 'magically' reactive, while delayed remains non-reactive.
// That is, the non-reactive InitialNewProp and SubsequentNewProp appear magically but messageDelayedNewProp doesn't
let dataModel = ObservableSlim.create(app.model, true, noChange);
dataModel.InitialNewProp = {} // is reactive...??
dataModel.messageText = [];
dataModel.messageText.push("Test");
dataModel.SubsequentNewProp = {}; // reactive and shows up!?
setTimeout(function () { // delay to simulate dom event or devconsole changes.
dataModel.messageDelayedNewProp = { test: "Test" }; // not reactive!
}, 500);
}
function test4() {
// same as above, but pauses changes: Pause is needed because ObservableSlim does not respect Vue reactivity
// additionally, we use a new handler/notify callback that calls Vue.set to wire up reactivity correctly
// unfortunately, fails because array is not created before attempts are made to resolve members
let dataModel = ObservableSlim.create(app.model, true, vueChange);
ObservableSlim.pauseChanges(dataModel);
dataModel.InitialNewProp = {} // works with vueChange
dataModel.messageText = [];
dataModel.messageText.push("Test"); // Push fails to resolve because messageText hasn't been changed to an array yet.
}
function test5() {
// same as above, but uses `messageArray` to forgo creating an array.
// while it resolves the array problem this reveals another problem
let dataModel = ObservableSlim.create(app.model, true, vueChange);
ObservableSlim.pauseChanges(dataModel);
dataModel.messageArray.push("Test"); // Fails due to vue error. Documentation indicates it's not resolving `this`
}
function test6() {
// same as above, but uses noChange to determine if ObservableSlim is the issue.
// the object is paused and Vue.set is no longer called, meaning no changes should be made.
// this should be an immutable operation!
let dataModel = ObservableSlim.create(app.model, true, noChange);
ObservableSlim.pauseChanges(dataModel);
dataModel.messageArray.push("Test"); // Fails with same weird vue issue
}
function test7() {
// The only configuration that works for all my test cases
let dataModel = ObservableSlim.create(app.model, false, vueChange); // only interation that works
ObservableSlim.pauseChanges(dataModel);
dataModel.NewProp = {} // works!
dataModel.messageText = [];
dataModel.messageText.push("Test"); // works!
setTimeout(function () { // delay
dataModel.messageArray.push("Test"); // works!
dataModel.delayedNewProp = {} // works!
}, 500);
}
function test8() {
// creates an ObservableSlim, but then works directly against the vue model
// this works, but observers are not notified
let dataModel = ObservableSlim.create(app.model, true, vueChange);
ObservableSlim.pauseChanges(dataModel);
setTimeout(function () { // delay for 3 seconds
app.model.messageArray.push("Test"); // works!
dataModel.messageArray.__getTarget.push("Test2"); // also works
}, 500);
}
function test9() {
// creates obslim first, then sets it on vue. Basically backwards of what we've been doing
// this doesn't quite work in theory because we now have to respect vue reactivity, though we have observables
// however, it actually doesn't work because vue fails with a similar error as test5 and test6
let model = {messageArray:[]};
dataModel = ObservableSlim.create(model, true, noChange);
//ObservableSlim.pauseChanges() // we are letting obslim make the changes to the base model through a reactive call to Vue
Vue.set(app, "model", dataModel);
app.model.messageArray.push("Test") // works
setTimeout(function () { // delay
app.model.messageArray.push("Test2") // fails!
}, 500);
}
function test10() {
// creates a model like above and binds observable slim.
// instead of referencing it to vue, we use a "copyChange" handler to change the vueModel from the observable.
// Test cases work successfully while domDelay is true!
let model = {messageArray:[], messageText:"unchanged"} // ~matches app.model
let dataModel = ObservableSlim.create(model, true, copyChange);
dataModel.messageArray.push("Test"); // works
dataModel.newArray = [];
dataModel.newArray.push("Test2"); // works!! (doesn't fail like test4)
setTimeout(function () { // delay
dataModel.messageArray.push("Test3") // works
dataModel.newProp = "Test" // works
}, 500);
window.dataModel = dataModel;
}
//test1();
//test2();
//test3();
//test4();
test5();
//test6();
//test7();
//test8();
//test9();
//test10();
</script>
</body>
</html>
@BinarySpike
Copy link
Author

Updated comments for clarification. Expanded test7() to include non-reactive changes working properly along with Array.push

@BinarySpike
Copy link
Author

BinarySpike commented Nov 16, 2018

added tests test9 and test10. Added new handler named copyChange that copies changes from a base model to a vue model. Added lodash dependency

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment