Passing observables through properties (ux:Property : object)

Hi guys,

regarding Passing observables through properties, I would like to know how I get the value from (in this case) passedInObservable variable.

For example, in my case I want to pass an Object like this { year: 2017, month: 1, day: 3 }.

I have a property on my Component

<object ux:Property="DateToShow"/>

And my component is “called” like this:

<Tim.MyComponent DateToShow ="{dateToShow}"/>

where dateToShow is an observable with the value above.

In my component I have

var dateToShow = this.DateToShow;

and the output is:

{
	"_origin": -159,
	"_subscribers": [{
		"version": 2,
		"active": true
	}],
	"_isProxy": false,
	"_values": [{
		"_origin": -180,
		"_subscribers": [{
			"version": 2,
			"active": true
		}, {
			"version": 2,
			"active": true
		}],
		"_isProxy": false,
		"_values": [{
			"year": 2017,
			"month": 1,
			"day": 3
		}],
		"_beganSubscriptions": true
	}],
	"_beganSubscriptions": true
}

Note: if I use var dateToShow = this.DateToShow.inner(); it outputs

{
	"_origin": -161,
	"_subscribers": [],
	"_isProxy": true,
	"_values": [],
	"_subscriptionWatchers": [{
		"watcherId": 18
	}]
}

If I try dataToShow.value it returns Fuse.Scripting.JavaScriptCore.Object. In a desperate attempt I’ve tried dataToShow.value.value, still the same output.

On the other hand, if the Property type is a string it all works like a charm.

I’m guessing this happens because “If we pass an Observable in, this.Propertyname will contain an observable with the observable we passed through.”

So how do I get the value from that observable? There is a better way to do this?

I really liked some help on this. I got it work with<string ux:Property="DateToShow"/> but it’s not as classy as with <object ux:Property="DateToShow"/>.

I have the same issue, trying to make a nice wrapper of nekronos/FuseNativeControls. I’m want to bind the date value as Observable from the page where it is used, using the binding both to set initial value and to hold the selected value.

Hey everyone,

to me it seems you’re all missing one single point about the passed-in Observables: they are reactive. And as such, you can not access them directly in functional code. As an example, I have prepared this snippet for you to look at:

<App>

    <Panel ux:Class="MyComponent" Height="56">
        <object ux:Property="DateToShow" />
        <JavaScript>
            var localDateObject = this.DateToShow.inner();
            var dateString = localDateObject.map(function(obj) {
                return obj.day + "/" + obj.month + "/" + obj.year;
            });
            module.exports = {
                dateString: dateString
            };
        </JavaScript>
        <Text Value="{dateString}" Alignment="Center" />
    </Panel>

    <JavaScript>
        var Observable = require("FuseJS/Observable");
        var dateToShow = Observable({day: 4, month: 5, year: 2017}); // may the fourth be with you this year too
        module.exports = {
            dateToShow: dateToShow
        };
    </JavaScript>

    <MyComponent DateToShow="{dateToShow}" />

</App>

If we’re talking two-way mapping, then matters get complicated. For that, I suggest you take a look at the code in this pull request which should explain the concepts.

Hope this helps!

I am having the same problem.

Whem I pass an observable to a ux:Class through a “object ux:Property” and I try to access it in the javascript it returns always null. Even with the map function you suggested the value is inaccessible in the javascript

<Panel ux:Class="My.Class" >
	<object ux:Property="Data"/>
	<JavaScript>
		var d = this.Data.inner();
		var data = cd.map(function(val){
			return val;
		});
		console.log(JSON.stringify(data));
	</JavaScript>
</Panel>

<JavaScript>
	var Observable = require("FuseJS/Observable");
	var cData = Observable();
	var line = [5,4,9,7,3,0,10,8,9,7];
	cData.add(line);
	module.exports.cData = cData;
</JavaScript>
<My.Class Data="{cData}"/>
Output
[Viewport]: {"_origin":-50,"_subscribers":[],"_isProxy":true,"_values":[],"_subscriptionWatchers":[{"watcherId":15}]}

I am usin the MyClass as a template. I dont know if this has samething to do with the problem.

Wilton, you only emphasise my point about the reactive nature of the Observable there:

var data = d.map(function(val){
	return val;
});
// data CAN NOT be accessed in functional style like you do here:
console.log(JSON.stringify(data));

If you want to log the value, do it inside of the .map. Make sure you consume the resulting observable, too:

var d = this.Data.inner();
var data = d.map(function(val){
    console.log(JSON.stringify(val));
    return val;
});
data.subscribe(module);

Alternatively, for debugging purposes, if the resulting Observable is a single-value Observable, you can add a logger-subscriber to it:

var d = this.Data.inner();
var data = d.map(function(val){
    return val;
});
data.onValueChanged(module, function(val) {
    console.log(JSON.stringify(val));
});

This worked now.

var d = this.Data.inner();
var data = d.map(function(val){
    console.log("Map:"+JSON.stringify(val));
    return val;
});
data.onValueChanged(module, function(val) {
    console.log(JSON.stringify(val));
});


[Viewport]: Map:[5,4,9,7,3,0,10,8,9,7]
[Viewport]: [5,4,9,7,3,0,10,8,9,7]

Thank’s Uldis

Thanks for pointing me in the right direction, Uldis!

After a bit of trial and failure, I ended up with a pretty nice solution, supporting two-way binding:

<Panel ux:Class="DatePicker">
  <object ux:Property="Date" />
  <object ux:Property="MinDate" />
  <object ux:Property="MaxDate" />
  <JavaScript>
      var date = this.Date.innerTwoWay();
      var minDate = this.MinDate.inner();
      var maxDate = this.MaxDate.inner();

      date.onValueChanged(function(newValue){
        // Assumes datePicker checks for same value to prevent endless updates
        if (newValue) datePicker.setDate(newValue);
      });

      minDate.onValueChanged(function(newValue){
        if (newValue) datePicker.setMinDate(newValue);
      });

      maxDate.onValueChanged(function(newValue){
        if (newValue) datePicker.setMaxDate(newValue);
      });

      datePicker.CurrentDate.addSubscriber(function () {
        date.value = datePicker.CurrentDate.value;
      });

  </JavaScript>
  <StackPanel>
    <NativeViewHost>
      <Native.DatePicker ux:Name="datePicker" />
    </NativeViewHost>
  </StackPanel>
</Panel>

Hi Bjørn,

nice it helped. Your code obviously has the apparent infinite loop problem lurking out every corner, and .addSubscriber() is a very bad practice - because you should also remove the subscriber yourself, otherwise you’re leaking memory. Fix those and you’re golden.

A better solution could be implemented by using either .mapTwoWay or .innerTwoWay on those Observables, just like in the PR link I posted.

Hi Uldis,

Thanks for your feedback!

I’m already using .innerTwoWay on the Date-object. I tried just binding to Native.DatePicker´s CurrentDate like:

<Native.DatePicker ux:Name="datePicker" CurrentDate="{Property Date}" />

However, that didn’t work.

I replaced the .addSubscriber()-part with:

        datePicker.CurrentDate.onValueChanged(function (newValue) {
          if (date.value && newValue
            && date.value.year === newValue.year
            && date.value.month === newValue.month
            && date.value.day === newValue.day) {
            return;
          }

          date.value = newValue;
        });

I guess that will fix the memory leak issue.

not unless you replace the first line there with this :slight_smile:

datePicker.CurrentDate.onValueChanged(module, function (newValue) {

Oops, thanks again :wink:

Philosophically it is important to remember that Fuse is built with a model/view/view-model mindset, where the model is kept strictly separate from the view-models, view-models transform data for consumption in the view, and commute interactions with the view back to the model: Because of this it’s absolutely necessary to reason about the use/consumption of any data passed through a view model, and to have an understanding of the view-model’s purpose.

Think of it this way: A view-model is a transform that takes data from a source and offers an alternative representation to be consumed by a view. If the data does not need to be transformed, you do not need a view-model, you can databind directly to the untransformed data. In other words, with good architecture you should never have synchronous need for the data contained in this.Parameter, only async/reactive representations of it. When interactions happen in the view, you communicate those interactions back to the model through its static representation, ie require('MyApp').onUserDataChanged(newData), the model is mutated, your reactive bindings update, your view-model updates, your UI updates. This is the reactive data flow in a well designed Fuse app.

Another tip for avoiding conceptual headaches: Never forget about combineLatest. Using a single combineLatest you can aggregate a context where you have access to every single observable value in a synchronous scope. It is IMO one of the most useful reactive operators.

MVVM and reactive is fine, but I wonder how you can make general, reusable components, like a DatePicker, if they need to know about the global scope where they are used?