# Lazy image load

**URL:** <https://forums.fusetools.com/t/lazy-image-load/3257>\
**Category:** General\
**Created:** [September 26, 2017, 6:08pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257 "2017-09-26T18:08:09Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![R0b0tn1k](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/r0b0tn1k/32/49_2.png) [@R0b0tn1k](https://forums.fusetools.com/u/R0b0tn1k)\
**Post date:** [September 26, 2017, 6:08pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/1 "2017-09-26T18:08:09Z")

</div>

I’m trying to fetch images via fetch, save them locally, and then display them. Since they dont exist at first load, the image is empty.

I’d like to fix that like the example you have published, but i’m curious as to how.

Your example is:

```auto
		<Panel Dock="Fill" Margin="5,0,5,5" ClipToBounds="true">
			<WhileBusy>
				<Image ux:Name="lowQualityImage" Url="{lowQualityImage}" StretchMode="UniformToFill" MemoryPolicy="UnloadUnused">
					<Desaturate Amount="1" />
					<RemovingAnimation>
						<Change lowQualityImage.Opacity="0" Duration="0.35"/>
					</RemovingAnimation>
				</Image>
			</WhileBusy>
			<Image Url="{highQualityImage}" StretchMode="UniformToFill" MemoryPolicy="UnloadUnused" />
		</Panel>

```

But my code is:

```auto
fetch('https://www.bla.com/bla.json')
    .then(function(response) { return response.json(); })
    .then(function(responseObject) { 
    	responseObject.forEach(function(el){
    		
    		// Get JSON data and process image URL from there
    		var imgUrl = el.Photo;
    		var img = imgUrl.substring(imgUrl.lastIndexOf('/')+1,imgUrl.lastIndexOf('.')+4);

			console.log(FileSystem.dataDirectory + "/" + img);
			el.LocalPhoto = FileSystem.dataDirectory + "/" + img;

			// If the file does not exist, fetch it and save it

			FileSystem.exists(FileSystem.dataDirectory + "/" + img)
			    .then(function(x) {
			        
			        console.log(x ? "it's there! =)" : "it's missing :/");
			       
			        if (!x){
					var oReq = new XMLHttpRequest();
					oReq.open("GET", imgUrl, true);
					oReq.responseType = "arraybuffer";

					oReq.onload = function (oEvent) {
					  var arrayBuffer = oReq.response; 
					  if (arrayBuffer) {
					    var path = FileSystem.dataDirectory + "/" + img;
					    FileSystem.writeBufferToFile(path, arrayBuffer);
					  }
					};

					oReq.send(null);    					        
					}

			    }, function(error) {
			        console.log("Unable to check if file exists");
			    });
    	});
});

```

And the view code:

```auto
<Each Items="{tracks}">
    <DockPanel Height="350" Margin="10,20">
        <Rectangle Layer="Background" CornerRadius="10" Fill="#fff" />
        <Rectangle Height="200" Dock="Top" Margin="0,-10" CornerRadius="10">
        	<Image File="{LocalPhoto}" Height="230" Padding="30" StretchMode="Fill" /> 
    	</Rectangle>
    		<Clicked Handler="{locClick}" />
		<Panel Margin="0,40" >
            <Text Value="{Title}" TextAlignment="Left" Alignment="Left" Margin="10,0" Padding="10" Dock="Top" FontSize="22" />
            <Text Value="{Description}" Height="50" TextAlignment="Left" Alignment="Left" Margin="10,60" Dock="Top" TextWrapping="Wrap" />
        </Panel>
    </DockPanel>
	<Clicked>
		<Set nav.Active="page2" />
	</Clicked>
</Each>

```

A general approach to take would be great.

---

<div class="post-metadata">

**Author:** ![petterroea](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/petterroea/32/438_2.png) [@petterroea](https://forums.fusetools.com/u/petterroea)\
**Post date:** [September 26, 2017, 6:17pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/2 "2017-09-26T18:17:25Z")

</div>

Hey!

You could mark nodes as busy from JavaScript like [this](https://www.fusetools.com/docs/fuse/triggers/busy) (and use the UX class `Busy` to show a loading animation or low quality image as the first snippet you provided had)

---

<div class="post-metadata">

**Author:** ![R0b0tn1k](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/r0b0tn1k/32/49_2.png) [@R0b0tn1k](https://forums.fusetools.com/u/R0b0tn1k)\
**Post date:** [September 26, 2017, 6:41pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/3 "2017-09-26T18:41:40Z")

</div>

Hey Liam, thanks for your response.

The example targets a global busy event. How do I target a specific busy event for one specific image? I’d like to display them as they are loaded?

```auto
<Panel>
    <WhileBusy>
        <Text Value="Loading..."/>
    </WhileBusy>
    <Busy IsActive="false" ux:Name="busy"/>
    <JavaScript>
        exports.startLoad = function() {
            busy.activate()
            fetch( "http://example.com/some/data" ).then( function(response) {
                //use the response
                busy.deactivate()
            }).catch(function(err) {
                //make sure to disable the busy status here as well
                busy.deactivate()
            })
        }
    </JavaScript>
    <Activated Handler="{startLoad}"/>
</Panel>

```

---

<div class="post-metadata">

**Author:** ![R0b0tn1k](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/r0b0tn1k/32/49_2.png) [@R0b0tn1k](https://forums.fusetools.com/u/R0b0tn1k)\
**Post date:** [September 26, 2017, 7:10pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/4 "2017-09-26T19:10:20Z")

</div>

So for example, if image 1,2 are loaded, but image 3 needs to be loaded, how do I target the 3rd whilebusy event?

```auto
<Each Items="{tracks}">
    <DockPanel Height="350" Margin="10,20">
        <Rectangle Layer="Background" CornerRadius="10" Fill="#fff" />
        <Rectangle Height="200" Dock="Top" Margin="0,-10" CornerRadius="10">
        
        <WhileBusy>
            <Image ux:Name="lowQualityImage" Url="{lowQualityImage}" StretchMode="UniformToFill" MemoryPolicy="UnloadUnused">
                <Desaturate Amount="1" />
                <RemovingAnimation>
                    <Change lowQualityImage.Opacity="0" Duration="0.35"/>
                </RemovingAnimation>
            </Image>
        </WhileBusy>
        
        <Image File="{LocalPhoto}" StretchMode="UniformToFill" MemoryPolicy="UnloadUnused" />

        </Rectangle>
            <Clicked Handler="{locClick}" />
        <Panel Margin="0,40" >
            <Text Value="{Title}" TextAlignment="Left" Alignment="Left" Margin="10,0" Padding="10" Dock="Top" FontSize="22" />
            <Text Value="{Description}" Height="50" TextAlignment="Left" Alignment="Left" Margin="10,60" Dock="Top" TextWrapping="Wrap" />
        </Panel>
    </DockPanel>
    <Clicked>
        <Set nav.Active="page2" />
    </Clicked>
</Each>

```

---

<div class="post-metadata">

**Author:** ![Uldis](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/uldis/32/657_2.png) [@Uldis](https://forums.fusetools.com/u/Uldis)\
**Post date:** [September 27, 2017, 9:18am UTC](https://forums.fusetools.com/t/lazy-image-load/3257/5 "2017-09-27T09:18:51Z")

</div>

A node is automatically marked as busy (meaning, `WhileBusy` evaluates to `true`) when any one of its children is doing something that takes time. Downloading the data for an `Image` does that too, for example.

This means that you don’t need to explicitly set a node as busy from JavaScript to use `WhileBusy`. You simply put it on the parent container of your `Image` tags and it will _just work_.

If, as you say, the images don’t exist at first, you should use a `<WhileString Value="{pathToImage}" Equals="">` and show a placeholder image that you bundle with the app. If the path is an Observable, you could change it to the real path once the image arrives.

---

<div class="post-metadata">

**Author:** ![R0b0tn1k](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/r0b0tn1k/32/49_2.png) [@R0b0tn1k](https://forums.fusetools.com/u/R0b0tn1k)\
**Post date:** [October 14, 2017, 4:35pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/6 "2017-10-14T16:35:37Z")

</div>

Thanks, the WhileString is very helpful.

One thing that still grinds my hears is the path, and it being an observable.

Basically, i’m iterating a json file using the each field.

Now, the path of the image is a part of that json. So you have something like:

```auto
<each Items="tracks">
 <text value="{Title}">
 <Image File="{LocalPhoto}" StretchMode="UniformToFill" MemoryPolicy="UnloadUnused" />
</each>

```

Do I set LocalPhoto as an observable? Then just populate it from the json?

Example:

```auto
var LocalPhoto = Observable();

fetch('https://bla.firebaseio.com/bla.json')
    .then(function(response) { return response.json(); })
    .then(function(responseObject) { 
    	responseObject.forEach(function(el){
    		var imgUrl = el.Photo;

			// Check if file exists
			FileSystem.exists(FileSystem.dataDirectory + "/" + img)
			    .then(function(x) {
			        console.log(x ? "it's there! =)" : "it's missing :/");
			       
			        if (!x){
					var oReq = new XMLHttpRequest();
					oReq.open("GET", imgUrl, true);
					oReq.responseType = "arraybuffer";

					oReq.onload = function (oEvent) {
					  var arrayBuffer = oReq.response; 
					  if (arrayBuffer) {
					    var path = FileSystem.dataDirectory + "/" + img;
					    FileSystem.writeBufferToFile(path, arrayBuffer);
						LocalPhoto = path;
					  }
					};

					oReq.send(null);    					        
					}

			    }, function(error) {
			        console.log("Unable to check if file exists");
			    });
    	});
    	var tracks = responseObject;
});

module.exports = {
	tracks: tracks,
	LocalPhoto : LocalPhoto
}

```

The image fetch happens asynchronously. That’s what confuses me.

---

<div class="post-metadata">

**Author:** ![Uldis](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/uldis/32/657_2.png) [@Uldis](https://forums.fusetools.com/u/Uldis)\
**Post date:** [October 16, 2017, 10:20am UTC](https://forums.fusetools.com/t/lazy-image-load/3257/7 "2017-10-16T10:20:44Z")

</div>

You’re iterating over `tracks`:

```auto
<Each Items="{tracks}">

```

Since `tracks` can change during run-time (e.g., you load more tracks or remove some), it has to be an Observable list of objects.

Each object in that list should hold the properties that you want to show in the UI, in your example those are `title` and `localPhoto`:

```auto
{
    title: "This is a title for item",
    localPhoto: "/some/path/to/file.png"
}

```

However, since you also want to be able to _change the path to the image being shown_, the `localPhoto` property can not be a simple string, so we need to make it an Observable:

```auto
{
    title: "This is a title for item",
    localPhoto: Observable("/some/path/to/file.png")
}

```

And then we can update any particular item in the `tracks` list, by setting the `.value` of the `localPhoto` Observable whenever your async `fetch()` for a particular image completes:

```auto
tracks.getAt(2).localPhoto.value = "/some/other/path/to/file.png";

```

If you’re wondering about `.getAt(index)` and `.value`, you should read [Observable docs](https://www.fusetools.com/docs/fusejs/observable-api).

Hope this helps!

---

<div class="post-metadata">

**Author:** ![R0b0tn1k](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.fusetools.com/r0b0tn1k/32/49_2.png) [@R0b0tn1k](https://forums.fusetools.com/u/R0b0tn1k)\
**Post date:** [November 13, 2017, 12:55pm UTC](https://forums.fusetools.com/t/lazy-image-load/3257/8 "2017-11-13T12:55:45Z")

</div>

Thanks, was about to start using that, when I remembered I can force a redraw if I replace the observable. I just do that, does a redraw, and dont have to worry about that anymore.
