Fuse.Reactive.ObjectMirror

I’m getting Fuse.Reactive.ObjectMirror displayed where I should have a value displayed after I reload.

I have a Task object that looks like this:

function Task(title){
	var self = this;
	this.title = title;
	this.unformated = Moment().add(getRandomInt(90,240),"minutes");
	this.nextTime = Observable(this.unformated.format("h:mm A"));
}

The Tasks are stored in an Observable list

var todoList = Observable();

And this is how they are being displayed

...
<Each Items="{todoList}">
...
      <Text ux:Name="itemTime" Value="{nextTime}"  />
...

When I add a new item to todoList, the attribute nextTime displays just fine. However, when I reload the screen, it shows Fuse.Reactive.ObjectMirror instead of the value.

I use this function to load the todoList from a stored JSON file(every time I add an item to the list, the JSON gets updated):

function loadItems(){
	var list = JSON.parse(json);
	list.forEach(function(r) {
		console.log(JSON.stringify(r));
    	todoList.add(r);
    });
}

I have monitored the JSON file, and it does not change from when it’s displaying correctly to when I reload and displays Fuse.Reactive.ObjectMirror.

Any ideas what is going on?

Please post a complete reproduction (something one could copy & paste and run). It’s impossible to check what’s going on without running the code that has the problem.

This code should reproduce the problem:

MainView.ux

<App Background="#666">
	<iOS.StatusBarConfig />
	<Text ux:Class="Complete" Font="Regular" TextAlignment="Center" Alignment="Center" Color="#fff" FontSize="20" /> 
	<JavaScript File="MainView.js" />

	<!-- Main View -->

	<StackPanel ux:Name="listStack">
		<Each Items="{todoList}">
			<Panel>

				<SwipeGesture ux:Name="swipeRight" LengthNode="itemPanel" Direction="Right" Type="Active" />

				<!-- Swipe animations -->
			    <SwipingAnimation Source="swipeRight">
			    	<Move X="1" RelativeTo="Size" RelativeNode="listStack" />
			    	<Change complete.Opacity="1" Easing="ExponentialOut" />
			    	<Move Target="complete" X="-1" RelativeTo="Size" RelativeNode="itemPanel" />
			    </SwipingAnimation>

			    <!-- Swipe triggers -->
			    <Swiped Source="swipeRight">
			    	<Callback Handler="{deleteItem}" />
			    </Swiped>

				<Panel ux:Name="itemPanel" Alignment="Top" Height="60" BoxSizing="Limit" LimitHeight="100%" Margin="16,10,16,10" Padding="16,0,16,0" Clicked="{change}" >
					<Rectangle ux:Name="background" Layer="Background" Fill="#fff" CornerRadius="5">
						<LinearGradient AngleDegrees="45">
							<GradientStop Offset="0" Color="#9D43DE" />
							<GradientStop Offset="1" Color="#3A5CC2" />
						</LinearGradient>
						<DropShadow Distance="0" Spread=".1" Color="#00000033" Angle="90" Size="5" />
					</Rectangle>
					<Panel Alignment="Left">
						<Text ux:Name="itemText" Value="{title}" Alignment="CenterLeft" TextAlignment="Left" Margin="0,0,0,0" FontSize="17" Color="#fff" TextWrapping="Wrap" />
					</Panel>
					<Panel Alignment="Right">
						<Text ux:Name="itemTime" Value="{nextTime}" Alignment="CenterRight" TextAlignment="Right" Margin="0,0,0,0" FontSize="17" Color="#fff" TextWrapping="Wrap" />
					</Panel>

				</Panel>

				<Text ux:Name="complete" Font="Regular" TextAlignment="Center" Alignment="CenterLeft" Color="#9D43DE" FontSize="18" Opacity="0"> 
						Complete
				</Text>

				<LayoutAnimation>
					<Move RelativeTo="LayoutChange" Y="1" Duration="0.6" Easing="BounceIn" />
				</LayoutAnimation>
				<AddingAnimation>
					<Move RelativeTo="Size" X="1" Duration="0.3" Easing="CircularInOut" />
				</AddingAnimation>
				<RemovingAnimation>
					<Move RelativeTo="Size" X="1" Duration="0.2" Easing="CircularInOut" />
					<Change complete.Opacity="0" Duration ="0" />
				</RemovingAnimation>

			</Panel>
		</Each>
	</StackPanel>

		<Panel Dock="Bottom" Color="#fff">
			<Panel>
				<TextInput Dock="Bottom" Value="{titleInput}" PlaceholderText="Enter task here" PlaceholderColor="#585959" TextColor="#585959" CaretColor="#585959" Padding="0, 0, 0, 0" Margin="24, 16, 24, 16" ActionStyle="Go">
					<TextInputActionTriggered>
			         <Callback Handler="{addItem}" />
			         <ReleaseFocus />
			     </TextInputActionTriggered>
				</TextInput>
				<Rectangle Height="1" Color="#DEDEDE" Alignment="Top" />
			</Panel>
		</Panel>
</App>

MainView.js

var Storage = require("FuseJS/Storage");
var FileSystem = require("FuseJS/FileSystem");
var Observable = require("FuseJS/Observable");
var tasks_json = FileSystem.dataDirectory + "/" + "tasks.json";
var json;
var Moment = require("Modules/moment.js");

FileSystem.exists(FileSystem.dataDirectory + "/tasks.json")
	.then(function(x) {
	    if(!x){
	        Storage.write(tasks_json, "");
	        console.log("Just made new file.");
	        json = FileSystem.readTextFromFileSync(tasks_json);
	    }
	    else{
	    	console.log("Has been read");
	    	json = FileSystem.readTextFromFileSync(tasks_json);
	    }
	    loadItems();
	});

function Task(title){
	var self = this;
	this.title = title;
	this.unformated = Moment().add(getRandomInt(90,240),"minutes");
	this.nextTime = Observable(this.unformated.format("h:mm A"));
}

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

var todoList = Observable();
var titleInput = Observable("");

function loadItems(){
	var list = JSON.parse(json);
	list.forEach(function(r) {
    	todoList.add(r);
    });
}

function addItem(arg) {
	var task = new Task(titleInput.value);
	todoList.add(task);
	var objString = JSON.stringify(todoList["_values"]);
    Storage.write(tasks_json,objString);
    titleInput.value = "";
}

function deleteItem(arg){
	todoList.remove(arg);
	var objString = JSON.stringify(todoList["_values"]);
    Storage.write(tasks_json,objString);
}

function change(arg){
	var item = arg.data;
	console.log(JSON.stringify(item));
	item.unformated =  Moment().add(getRandomInt(90,240),"minutes");
	item.nextTime.value = item.unformated.format("h:mm A");

}

module.exports = {
    todoList: todoList,
	titleInput: titleInput,
	addItem: addItem,
	deleteItem: deleteItem,
	change: change
};

Also note that I use Moment JS, which is referenced in MainView.js

As we concluded on Slack, the main issue with the code you have there is that you’re storing Observables in the file, when all you should be storing is plain data, simple types.

Observables are complex objects that can not be serialised (well, technically you can serialise them, but that’s beyond the point).

Your JSON file should only contain static data which you read and convert to objects during run-time. If necessary, you can make properties of those objects Observables. When saving, you need to convert them back to strings and other simple types you want to save in the file.

As a side note, you shouldn’t be accessing any of the private properties of Observables directly (the underscore before the variable name suggests that). So instead of todoList["_values"] you should be using todoList.toArray(). Full list of Observable operators is here.