美好的一天,
我正在开发一个Ionic应用程序,我正在从JSON提要中动态加载图像。我希望当从外部URL中提取图像时,占位符图像显示在其中,此后,占位符图像一旦被加载就应该被“真实”图像替换。
我目前的情况是:
<ion-card *ngFor="let item of items"...
<img [src]="item.picture" />
谢谢&amp;问候。
答案 0 :(得分:16)
大多数答案都是关于在同一视图中添加加载检查的逻辑,但似乎不合适。相反,您可以创建自己的指令来处理它。您可以查看 this amazing article ,看看如何完成。
首先在src/assets
文件夹中复制 this GIF ,并将其命名为preloader.gif
。
然后添加此自定义指令。代码几乎是不言自明的:
// An image directive based on http://blog.teamtreehouse.com/learn-asynchronous-image-loading-javascript
import {Directive, Input, OnInit} from '@angular/core';
// Define the Directive meta data
@Directive({
selector: '[img-preloader]', //E.g <img mg-img-preloader="http://some_remote_image_url"
host: {
'[attr.src]': 'finalImage' //the attribute of the host element we want to update. in this case, <img 'src' />
}
})
//Class must implement OnInit for @Input()
export class ImagePreloader implements OnInit {
@Input('img-preloader') targetSource: string;
downloadingImage : any; // In class holder of remote image
finalImage: any; //property bound to our host attribute.
// Set an input so the directive can set a default image.
@Input() defaultImage : string = 'assets/preloader.gif';
//ngOnInit is needed to access the @inputs() variables. these aren't available on constructor()
ngOnInit() {
//First set the final image to some default image while we prepare our preloader:
this.finalImage = this.defaultImage;
this.downloadingImage = new Image(); // create image object
this.downloadingImage.onload = () => { //Once image is completed, console.log confirmation and switch our host attribute
console.log('image downloaded');
this.finalImage = this.targetSource; //do the switch
}
// Assign the src to that of some_remote_image_url. Since its an Image Object the
// on assignment from this.targetSource download would start immediately in the background
// and trigger the onload()
this.downloadingImage.src = this.targetSource;
}
}
然后将新指令添加到您的app模块,如下所示:
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { ImagePreloader } from '../components/img-preload/img-preload';
@NgModule({
declarations: [
MyApp,
ImagePreloader, // <------- Here!
// ...
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
// ...
],
providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}
这是您使用新自定义指令的方法:
<img img-preloader="https://images.unsplash.com/photo-1413781892741-08a142b23dfe" alt="">
这就是它的样子:
然后您可以设置图像容器的高度/宽度,因此预加载器gif和最终图像都具有相同的尺寸。
答案 1 :(得分:0)
希望您可以尝试如下所示。您可以将占位符图像存储在assets/images
文件夹中。
html的
<ion-card *ngFor="let item of items">
<img [src]="item?.picture!= null ? item.picture : myImgUrl" />
</ion-card>
.TS
export class MyPage{
myImgUrl:stirng='./assets/images/myimage.png;
constructor(){}
}
答案 2 :(得分:0)
你能修改你的JSON以获得loaded
布尔属性吗?如果你能做到这一点很简单,那么就这样做:
<ion-card *ngFor="let item of items">
<img [src]="item.picture" (load)="item.loaded = true" [hidden]="!item.loaded" />
<img src="../path/to/your/placeholer/image.jpg" [hidden]="item.loaded">
</ion-card>
您的加载属性始终被初始化为false
,当图像加载时,将触发(load)
事件并将图像加载属性更改为true,隐藏占位符图像并显示已加载IMAGEM。
如果您无法更改此JSON(或者如果它太大而无法编辑),您可以使用上述代码,但请在页面.ts
public functionWhoseIsLoadingYourJson(){
// HTTP REQUEST FOR JSON
.subscribe(response => {
response.forEach(element => {
element.loaded = false;
});
this.items = response;
});
}
只需迭代您的响应,并在每个对象中将加载的属性设置为false。如果这会引发一些错误,您可以将<ion-card>
置于带有* ngIf
<div *ngIf="this.items != null">
<!-- ion card -->
</div>
由于它来自Firebase,因此有三个选项可用于更新您的JSON。第一个是一个函数,你只执行一次更新数据库,第二个仍然是我给出的代码(迭代结果并推送布尔值)但是因为你使用的是Firebase所以它会有所不同。
第一个解决方案:
在代码中的某个地方,它可以是任何页面,您只需要执行一次:
firebase.database().ref('myDataNode').on('value', snapshot => {
for(let key in snapshot.val()){
firebase.database().ref('myDataNode/' + key + '/loaded').set(false);
}
});
看到我正在循环浏览myDataNode
中的所有结果并使用其密钥将loaded
属性设置为false
。但是在使用set
时要小心,因为您可以覆盖所有节点,在执行此方法之前,请记住导出Firebase节点以进行备份。如果您要将代码从代码中的任何位置推送到Firebase,请记住将其更改为同时设置loaded
属性。
如上所述,第二个是我在这个答案中所做的,但现在在Firebase调用中使用它并使用随快照附带的forEach方法:
firebase.database().ref('myDataNode').on('value', snapshot => {
snapshot.forEach(element =>{
this.myDataNode.push({
picture: element.val(), // maybe it needs to be element.val().nameOfYourProperty
loaded: false
})
});
});
这将使用图片网址和已加载的属性将新元素推送到您的数组。如果它无法识别.push()
方法,只需声明您的变量类型为数组:
public myDataNode: any[];
第三个是本地更新,通过您的firebase快照进行更新,将结果保存在本地变量中,创建一个新属性并将其推送到您的阵列:
firebase.database().ref('myDataNode').on('value', snapshot => {
for (let key in snapshot.val()){
snapshot.forEach(item => { // iterate snapshot
let record = item.val(); // save the snapshot item value in a local variable
record.loaded = true; // add the property
this.newArray.push(record); // push it to your array that'll be used in ng-for
return true; // return true to foreach
});
}
});
希望这有帮助。
答案 3 :(得分:0)
据我所知,没有离子方法可以做到这一点。但你仍然可以通过javascript和css来实现。
创建一个div覆盖您的图像,并将占位符图像设置为该div的.HandleEventWithThisMethod
。
background-image
你可以在stackoverflow中轻松找到javascript的几种方法,所以我不会在这里重新回答。 {{3}}就是一个例子
答案 4 :(得分:-2)
试试这个:
.TS
export class MyPage {
placeHolder:string = './assets/img/placeholder.png';
constructor(){}
}
html的
<img [src]=item.image ? item.image : placeHolder>