Skip to content

RadListView sometimes the image doesn't render after binding update #528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
nuno-morais opened this issue Feb 13, 2018 · 14 comments
Closed

Comments

@nuno-morais
Copy link

nuno-morais commented Feb 13, 2018

Please, provide the details below:

Did you verify this is a real problem by searching Stack Overflow?

Yes

Tell us about the problem

When I update a image property (binding) after render the list, sometimes the image doesn't update and other times the image disappear.

Which platform(s) does your issue occur on?

iOS

Please provide the following version numbers that your issue occurs with:

  • Progress NativeScript UI version: 3.4.0
  • CLI: 3.4.1
  • Cross-platform modules: 3.4.0
  • Runtime(s): 3.4.1

Please tell us how to recreate the issue in as much detail as possible.

<RadListView [items]="Chats" selectionBehavior="None" 
        (itemTap)="OnChatSelected(Chats.getItem($event.index))">
        <ng-template tkListItemTemplate let-chat="item">
            <GridLayout class="ChatsListItem" [class.Highlighted]="HasNewChatMessages(chat)" columns="auto,*">
                <Image col="0" width="60" height="60" [src]="chat.Image" stretch="aspectFit"></Image>
                <StackLayout col="1" orientation="vertical">
                .... 
@tsonevn
Copy link

tsonevn commented Feb 15, 2018

Hi @xnramx,
First of all, thank you for your interest in UI for NativeScript.

Please provide sample project or at least the piece of code, which also demonstrates, how you are changing the image source in the code behind.

@nuno-morais
Copy link
Author

nuno-morais commented Feb 17, 2018

Items.component.ts

import { Component, OnInit } from "@angular/core";

import { Item } from "./item";
import { ItemService } from "./item.service";
var http = require("http");

@Component({
    selector: "ns-items",
    moduleId: module.id,
    templateUrl: "./items.component.html",
})
export class ItemsComponent implements OnInit {
    items: Item[];
    DefaultImage = "res://user_circle_o_384_0_e0e0e0"
    // This pattern makes use of Angular’s dependency injection implementation to inject an instance of the ItemService service into this class. 
    // Angular knows about this service because it is included in your app’s main NgModule, defined in app.module.ts.
    constructor(private itemService: ItemService) { }

    ngOnInit(): void {
        this.items = this.itemService.getItems();
        this.getImages();
    }

    async getImages()
    {
        for(const item of this.items)
        {
            item.image = await this.getImage();
            await this.delay(500);
        }
    }

    delay(ms: number) {
        return new Promise<void>(function(resolve) {
            setTimeout(resolve, ms);
        });
    }

    getImage() 
    {
        const address: string = "https://dummyimage.com/16:9x1080/";
        // const authorization: string; It's not necessary right now
        const request = {
            url: address,
            method: "GET",
            //headers: {
            //    Authorization: authorization
            //}
        };
        return http.getImage(request);
    }
}

items.component.html

<StackLayout class="page">
    <GridLayout tkExampleTitle tkToggleNavButton>
        <RadListView [items]="items" selectionBehavior="None">
            <ng-template tkListItemTemplate let-item="item">
                <GridLayout lass="ListItem" columns="auto,*">
                    <Image col="0" *ngIf="item.image != null" [src]="item.image" stretch="aspectFit"></Image>
                    <ActivityIndicator *ngIf="item.image == null"
                        [busy]="item.image == null"></ActivityIndicator>
                    <Label col="1" [text]="item.name"></Label>
                </GridLayout>
            </ng-template>
        </RadListView>
    </GridLayout>
</StackLayout>

@tsonevn I did a sample project and you can find it here

@tsonevn
Copy link

tsonevn commented Feb 19, 2018

Hi @xnramx
Thank you for the attached project.
I reviewed your case. However, the issue in the code seems to be related to the way the image source has been loaded.
While using the Image view you could directly set up the URL to the src property, The Image component will create the request and will load the needed Image. For example:

Full code:
HTML

<StackLayout class="page">
    <GridLayout tkExampleTitle tkToggleNavButton>
        <RadListView [items]="items" selectionBehavior="None">
            <ng-template tkListItemTemplate let-item="item">
                <GridLayout lass="ListItem" columns="auto,*">
                    <Image width="70" height="70" col="0" [src]="item.image" stretch="aspectFit"></Image>
                    
                    <Label col="1" [text]="item.name"></Label>
                </GridLayout>
            </ng-template>
        </RadListView>
    </GridLayout>
</StackLayout>

TypeScript

import { Component, OnInit } from "@angular/core";
import {getImage} from "http"
import { Item } from "./item";
import { ItemService } from "./item.service";

@Component({
    selector: "ns-items",
    moduleId: module.id,
    templateUrl: "./items.component.html",
})
export class ItemsComponent implements OnInit {
    items: Item[];
    constructor(private itemService: ItemService) { }

    ngOnInit(): void {
        this.items = this.itemService.getItems();
        this.getImages();
    }

    getImages()
    {
        for(const item of this.items)
        {
            setTimeout(() => {
                item.image= "https://dummyimage.com/16:9x1080/";
            }, 2000);
            
        }
    }

   
}

Something more, while creating the HTTP request in NativeScript Angular project, the recommended way is to do this with HTTPClient module, instead of the HTTP module, provided by the core modules. A sample code could be found here.

For your convenience, I am attaching sample project with the suggested changes.

Archive.zip

@nuno-morais
Copy link
Author

It doesn't solve my issue, because:

  • I need authorization context to get the image;
  • I would like to show a "busy indicator / activity indicator" while a photo is beeing loaded;

@nuno-morais
Copy link
Author

nuno-morais commented Feb 19, 2018

Just to clarify, your sample works because you removed *ngIf="item.image != null" and the ActivityIndicator

PS: if you scroll down and up all images will be rendered in my 1st project

@tsonevn
Copy link

tsonevn commented Feb 20, 2018

Hi @xnramx,
I checked again the project and the problem seems to be related to the ngIf directive. Regarding that, I logged a new issue and we will investigate the problem further.

Meanwhile, you could just remove the ngIf directive from the Image, which should not change the behaviour of the app. For example:

<StackLayout class="page">
    <GridLayout tkExampleTitle tkToggleNavButton>
        <RadListView [items]="items" selectionBehavior="None">
            <ng-template tkListItemTemplate let-item="item">
                <GridLayout lass="ListItem" columns="auto,*">
                    <Image col="0" [src]="item.image" stretch="aspectFit"></Image>
                    <ActivityIndicator *ngIf="item.image == null"
                        [busy]="item.image == null"></ActivityIndicator>
                    <Label col="1" [text]="item.name"></Label>
                </GridLayout>
            </ng-template>
        </RadListView>
    </GridLayout>
</StackLayout>

@tsonevn tsonevn closed this as completed Feb 20, 2018
@nuno-morais
Copy link
Author

Finally I found the "bug".
If I include a StackLayout the image only will be rendered if I scroll down and up. Is there any way to improve it?

               <GridLayout class="ListItem" columns="*">
                    <StackLayout col="0">
                        <Image [src]="item.image" stretch="aspectFill"></Image>
                        <ActivityIndicator *ngIf="item.image == null"
                            [busy]="item.image == null"></ActivityIndicator>
                        <Label [text]="item.name"></Label>
                    </StackLayout>
                </GridLayout>

@tsonevn
Copy link

tsonevn commented Feb 21, 2018

Hi @xnramx,
Thank you for the provided code snippet.
If I understand you correctly, you are facing an issue with showing the ActivityIndicator.
Regarding that, if you remove the ngIf directive from the component the indicator will be displayed correctly.

@nuno-morais
Copy link
Author

Nop... I have the same issue (render the image that is beeing loaded in background - lazy load).

I've attatched the project. And if you really want, you can remove the ActivityIndicator, the image doesn't render.

Download the project here

screen shot 2018-02-21 at 08 36 58

@meysam-mahmoodi
Copy link

I had this problem. If Images are in StackLayout it will not load after scrolling or it will not render at all.
I removed(commented in this example) the StackLayout element and it will work well.

                  <ng-template tkListItemTemplate let-item="item" let-i="index">
                    <!-- comment StackLayout -->
                    <!-- <StackLayout> -->
                      <Image src="{{item.pictures[0]}}"></Image>
                    <!-- </StackLayout> -->
                    
                  </ng-template>

@tsonevn
Copy link

tsonevn commented Mar 6, 2019

Hi @meysam-mahmoodi,
In the scenario, where the Image view is set up inside a StackLayout, you need to specify the height of the image. Example:

<StackLayout> 
                      <Image height="150" src="{{item.pictures[0]}}"></Image>
</StackLayout> 

@meysam-mahmoodi
Copy link

meysam-mahmoodi commented Mar 6, 2019

@tsonevn Yes, I did. I have specified already the height in CSS file. There was just a problem with the StackLayout element. Now it works well without the StackLayout.
my code:

                  <GridLayout class="collection-scroll" tkExampleTitle tkToggleNavButton>
                    <RadListView [items]="items">
                      <ListViewLinearLayout tkListViewLayout scrollDirection="Horizontal" itemWidth="90" itemHeight="140">
                      </ListViewLinearLayout>
                      <ng-template tkListItemTemplate let-item="item" let-i="index">
                        <Image class="collection-item" src="{{ item.picture }}"></Image>
                      </ng-template>
                    </RadListView>
                  </GridLayout>

@tsonevn
Copy link

tsonevn commented Mar 7, 2019

Hi @meysam-mahmoodi,
Can you send us a project, that demonstrates the issue?

@meysam-mahmoodi
Copy link

@tsonevn I have no problem now. sorry if I couldn't explain my mind enough good. I removed the StackLayout and now everything is ok.
I just wanted to say how I could solve the problem here.

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

No branches or pull requests

3 participants