Each item javascript overrides data already in object

I have the following Each loop

<Each Items="{posts}">
    <ag.NewsListItem 
        Clicked="{gotoPost}" 
        Title="{title.rendered}" 
        ImageUrl="{acf.mobile_image.sizes.medium}"
        Date="{date}"
        />
</Each>

Each post in posts is an object containing different information (it is a Wordpress article object downloaded view the WP Rest API).

In the gotoPost function I send this data to the next page which displays the article:

function gotoPost(arg) {
	console.log("gotoPost: arg.data = " + JSON.stringify(arg.data)); // having a look at the data
	router.push("newspost", arg.data);
}

This works perfectly. However, then I wanted to add some formatting to the date in the ag.NewsListItem component and added this to the file.

  <JavaScript>
    var Moment = require("../Modules/moment");

    var moment = Moment(this.Date.value);
    var formattedDate = moment.format("d. MMM YYYY");

    module.exports = {
      formattedDate: formattedDate
    }
  </JavaScript>

However, now the arg.data in the gotoPost function only contains the new formattedDate variable.

Is There a way of keeping the post object while adding the formattedDate?

Hi Wodger,

a complete copy-pasteable, ready-to-run reproduction that shows all of the code in question is the shortest path to getting an answer.

The snippets you are showing tell very little about the root cause of your problem.

However, there is a solution, and in this case it comes as the News Feed example which does pretty much what you need to achieve.

Hope this helps!

Turns out it is when adding the JavaScript tag to the list item the problem appears.

Example (new project with the two files below) :

MainView.ux

<App>
  <Page Color="#fff">
	<JavaScript>
    var posts = [
      {"title": "Title 1"},
      {"title": "Title 2"},
      {"title": "Title 3"},
      {"title": "Title 4"},
    ];

    function gotoPost(arg) {
      console.log("Data = " + JSON.stringify(arg.data));
    }
    module.exports = {
      posts: posts,
      gotoPost: gotoPost
    }
  </JavaScript>
	<DockPanel>
		<ScrollView>
			<StackPanel>
				<Each Items="{posts}">
					<ListItem
						Clicked="{gotoPost}"
						Title="{title}"
						/>
				</Each>
			</StackPanel>
		</ScrollView>
	</DockPanel>
</Page>
</App>

ListView.ux

<Panel ux:Class="ListItem" Height="50" Background="#999">
  <string ux:Property="Title" />
  <!-- <JavaScript>
  </JavaScript> -->
  <Text Value="{ReadProperty Title}" />
</Panel>

If you run this example, the console.log in gotoPost will show the data of the post, for example Data = {"title":"Title 4"}

If you remove the comments around the JavaScript tag in ListView.ux the output is Data = {}.

Is there a way to add javascript to the ListItem without destroying the post data?

Yes, there is a way to add JavaScript to your ListItem without destroying the post data.

You can read more about creating reusable components here.

My brain is tired, so I guess I´ll have to wait until tomorrow to go through that document. (If you have any hints on where in that long document I can find what I am after, it will be greatly appreciated, though) :slight_smile:

Sorry, no shortcuts on this one. That’s because you have to understand the data contexts a ux:Class operates in and with.

Basically your component-local JS data context overrides whatever data context you had before, so you need to pass in stuff using ux:Property instead. And then you can use reactive mapping on implicit Observables available in JS.

And since that ^^^ probably made no sense, back to square one - reading :slight_smile:

Have read through it and as far as I can see it doesn´t mention a way to pass the entire object as a ux:Property. Is this possible?

I tried using this and {this} which seemed most obvious, but that didn´t work

<Each Items="{posts}">
    <ag.NewsListItem
        Clicked="{gotoPost}"
        Title="{title.rendered}"
        ImageUrl="{acf.mobile_image.sizes.medium}"
        Date="{date}"
        Post="{this}"
        />
</Each>

A side question: Is there a way to get the index each item produced by Each? Something like

<Each Items="{posts}">
  <Text Value="{index} />
</Each>

Hi Wodger,

let’s answer those three questions separately.

Is there a way to pass the entire object as a ux:Property?

The answer is yes, but not in the context you are asking. So: no. Instead, the entire object [of iterated item] is implicitly available to the ux:Class. Consider the following code:

<App>

	<JavaScript>
	var Observable = require("FuseJS/Observable");

	var list = Observable(
		new Item("item one"),
		new Item("item two"),
		new Item("item three")
	);

	function Item(label) {
		this.label = label;
	}

	module.exports = {
		items: list
	};
	</JavaScript>

	<Panel ux:Class="ListItem" Height="56">
		<Text Value="{label}" Alignment="Center" Color="#000a" />
		<Rectangle Color="#0003" CornerRadius="2" />
	</Panel>

	<ClientPanel Color="#f003">
		<ScrollView>
			<StackPanel ItemSpacing="2" Margin="2">
				<Each Items="{items}">
					<ListItem />
				</Each>
			</StackPanel>
		</ScrollView>
	</ClientPanel>

</App>

What if I need to do something to a ux:Property before it’s displayed?

One way would be to do that in the original data source, in the object itself - so that you don’t have to do anything after. The other way involves defining separate ux:Property for each item property you need and manipulating them in component-local scope. Consider the following code:

<App>

	<JavaScript>
	var Observable = require("FuseJS/Observable");

	var list = Observable(
		new Item("item one"),
		new Item("item two"),
		new Item("item three")
	);

	function Item(label) {
		this.label = label;
	}

	module.exports = {
		items: list
	};
	</JavaScript>

	<Panel ux:Class="ListItem" Height="56">
		<JavaScript>
			var localLabel = this.Label.map(function(x) {
				return "this is " + x;
			});
			module.exports = {
				localLabel: localLabel
			};
		</JavaScript>
		<string ux:Property="Label" />
		<Text Value="{localLabel}" Alignment="Center" Color="#000a" />
		<Rectangle Color="#0003" CornerRadius="2" />
	</Panel>

	<ClientPanel Color="#f003">
		<ScrollView>
			<StackPanel ItemSpacing="2" Margin="2">
				<Each Items="{items}">
					<ListItem Label="{label}" />
				</Each>
			</StackPanel>
		</ScrollView>
	</ClientPanel>

</App>

And finally,

Is there a way to get the index each item produced by Each?

Not directly, no. There are no implicit magic indexes. However, you can easily add some yourself! Consider the following code:

<App>

	<JavaScript>
	var Observable = require("FuseJS/Observable");

	var list = Observable(
		new Item("item one"),
		new Item("item two"),
		new Item("item three")
	);

	var indexedList = list.map(function(itm, idx) {
		return {index: idx, item: itm};
	});

	function Item(label) {
		this.label = label;
	}

	module.exports = {
		items: indexedList
	};
	</JavaScript>

	<Panel ux:Class="ListItem" Height="56">
		<JavaScript>
			var localLabel = this.Label.map(function(x) {
				return "this is " + x;
			});
			module.exports = {
				localLabel: localLabel
			};
		</JavaScript>
		<string ux:Property="Label" />
		<Text Value="{index}: {localLabel}" Alignment="Center" Color="#000a" />
		<Rectangle Color="#0003" CornerRadius="2" />
	</Panel>

	<ClientPanel Color="#f003">
		<ScrollView>
			<StackPanel ItemSpacing="2" Margin="2">
				<Each Items="{items}">
					<ListItem Label="{item.label}" />
				</Each>
			</StackPanel>
		</ScrollView>
	</ClientPanel>

</App>

Note how inside of the ux:Class we can use both {index} from the iterated object directly, and {localLabel} from component-local scope.