I created an app and it does work great but images load every time I scroll up and down, I looked it in google and found this, the problem is I use angular, there is no such thing as viewModel in my angular version, how do I apply cache image into template?
You can try this code. It is working for me.
.ts code
import imageCacheModule = require("ui/image-cache");
import imageSource = require("image-source");
var cache = new imageCacheModule.Cache();
private _imageSrc: any;
private imgSource: any;
getImageCache(imageURL) {
cache.placeholder = imageSource.fromResource("res://no-image.png");
cache.maxRequests = 10;
cache.enableDownload()
var images = cache.get(imageURL)
if(images) {
return images
} else {
cache.push({
key: imageURL,
url: imageURL,
completed: (image: any, key: string) => {
if (imageURL === key) {
this.imgSource = imageSource.fromNativeSource(images);
}
}
})
cache.disableDownload();
}
HTMl code
<Image [src]="getImageCache(item.image?.url)" class="item-image"></Image>
Related
In Flame documentation, Image class just load from asset folder.
bgSprite = Sprite('avatar/avatar-sample.png');
How can I load network images in Flame.
You could do something like this in versions after 1.0.0:
import dart:ui; // This is the package you want the Image class from, there are several
Future<Image> getImage(String path) async {
Completer<ImageInfo> completer = Completer();
var img = new NetworkImage(path);
img.resolve(ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info,bool _){
completer.complete(info);
}));
ImageInfo imageInfo = await completer.future;
return imageInfo.image;
}
and then in your onLoad method, just initiate bgSprite:
#override
Future<void> onLoad() async {
final image = await getImage("your-url.com/sample.png");
bgSprite = Sprite(image);
}
In 0.28.0, which it looks like you are running, you'll just replace the last line with (but I really recommend upgrading to a version after 1.0.0):
bgSprite = Sprite.fromImage(image);
Getting below error while using the latest version of cloud_firestore: ^0.14.0+2 while uploading image on cloud firestore.
Uncaught (in promise) Error: [cloud_firestore/unknown] Invalid argument (dartObject): Could not convert: Instance of '_Uri'
The image gets successfully uploaded in the storage portion but the image link doesn't get updated in cloud firestore db.
Below is the class from where I pick the image and and hit he upload btn to upload the image
class AddNewItemView extends StatefulWidget {
#override
_AddNewItemViewState createState() => _AddNewItemViewState();
}
class _AddNewItemViewState extends State<AddNewItemView> {
MediaInfo _itemPic;
#override
Widget build(BuildContext context) {
return Scaffold(
.....
child: RawMaterialButton(
onPressed: () async {
MediaInfo pickedImg = await ImagePickerWeb.getImageInfo; <---- image_picker_web package
setState(() {
_itemPic = pickedImg;
});
.........
FlatButton(
onPressed: () async {
bool addDataSuccess = await model.addNewItem( _itemPic);
if (addDataSuccess) {
print("Inside addDataSuccess");
} else {
print("Outside addDataSuccess");
}
},
),
.....
);
}
This class is responsible for extracting image uri from uploadImageFile funtion and uploading it in cloud firestore.
Any other info passed alongside pic gets uploaded in cloud firestore, only 'pic' is not uploading
class ItemViewModel extends BaseViewModel {
Future<bool> addNewItem( MediaInfo pic ) async {
Uri _uploadedImgURL = await ConstantFtns()
.uploadImageFile(pic, "ImgPathString", "fileName");
await FirebaseFirestore.instance.collection('item').doc("tempID").set(
{
'pic': _uploadedImgURL,
'otherInfo': 'otherTempInfo'
},
);
}
in below class I get valid imageUri without any error and image gets uploaded in storage portion only problem is that it don't get uploaded in cloud firestore database
import 'package:firebase/firebase.dart' as fb;
import 'package:mime_type/mime_type.dart';
import 'package:path/path.dart' as p;
class ConstantFtns {
Future<Uri> uploadImageFile(
MediaInfo mediaInfo, String ref, String fileName) async {
try {
String mimeType = mime(p.basename(mediaInfo.fileName));
final String extension = extensionFromMime(mimeType);
var metadata = fb.UploadMetadata(
contentType: mimeType,
);
fb.StorageReference storageReference =
fb.storage().ref(ref).child(fileName + ".$extension");
fb.UploadTaskSnapshot uploadTaskSnapshot =
await storageReference.put(mediaInfo.data, metadata).future;
Uri imageUri = await uploadTaskSnapshot.ref.getDownloadURL();
print("download url $imageUri"); <---- IT GETS P[RINTED AND IT SHOWED VALED IMAGE URL STORED IN STORAGE
return imageUri;
} catch (e) {
print("File Upload Error $e");
return null;
}
}
}
Since what you want is simply to store the URL into firestore you don't really need the getDownloadURL() value to be a Uri object, you just need it's string value, being the error you are getting a conversion error, I suggest that you follow what is recommended in this community answer, that way your code will look like this:
String uploadImageFile(
MediaInfo mediaInfo, String ref, String fileName) async {
try {
...
var imageUri = await uploadTaskSnapshot.ref.getDownloadURL() as String;
print("download url $imageUri"); <---- IT GETS P[RINTED AND IT SHOWED VALED IMAGE URL STORED IN STORAGE
return imageUri
} catch (e) {
...
}
}
})
And you also have to change the call in the ItemViewModel class to:
String _uploadedImgURL = await ConstantFtns()
.uploadImageFile(pic, "ImgPathString", "fileName");
I have built an app with NativeScript Angular (now on v4.1). I am using the code below to set a status bar color on Android, which works fine for "regular" views by using angular routing:
if (isAndroid) {
if (app.android && device.sdkVersion >= '21') {
const nativeColor = new Color('purple').android;
const window = app.android.startActivity.getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
app.android.startActivity.getWindow().setStatusBarColor(nativeColor);
}
}
However, it does not work in a modal, where the colored status-bar turns to black. Anyone any ideas why this is? My best guess would be that a modal is not referenced by app.android.startActivity.getWindow() but I’m unclear as how to get it.
Not sure to what extent this is related, but also I am unable to set a different loading indicator on iOS in modals by using this code from the docs (again works fine in non-modal views):
if (isIOS) {
var indicator = this.page.getViewById("spinner");
indicator.ios.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge;
}
Thanks for any pointers!
P.S.
Here is a demo project which displays the problem both with the status bar on Android, as well as not being able to set the activity indicator on iOS.
For anyone reading this later on: I had some help on the NativeScript forum and did not yet accomplish setting the statusbar color on the modal, but I was able to span the background under the status bar in the modal component which is good enough for my purposes.
I was also able to change the ActivityIndicator in the modal simply by using an Angular ViewChild on the indicator element instead of finding it through Page (which turns out does not refer to the modal).
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from "#angular/core";
import { ModalDialogParams } from "nativescript-angular/modal-dialog";
import { isIOS, isAndroid, device } from 'platform';
import * as app from "application";
declare var UIActivityIndicatorViewStyle: any;
declare var android: any;
#Component({
template: `
<StackLayout #root>
<StackLayout orientation="horizontal">
<ActivityIndicator #spinner id="spinner" busy="true"></ActivityIndicator>
<ActivityIndicator busy="true"></ActivityIndicator>
</StackLayout>
</StackLayout>
`
})
export class ModalTest implements OnInit, AfterViewInit {
#ViewChild('root') root: ElementRef;
#ViewChild('spinner') spinner: ElementRef;
constructor(
public modalParams: ModalDialogParams
) {}
public ngOnInit() {
/* show large activity indicator on iOS */
if (isIOS) {
this.spinner.nativeElement.ios.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge;
}
}
public ngAfterViewInit() {
if (isAndroid) {
setTimeout(() => {
if (app.android && device.sdkVersion >= '21') {
const dialog = this.root.nativeElement._dialogFragment.getDialog();
const window = dialog.getWindow();
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
},1);
}
}
public closeModal() {
this.modalParams.closeCallback();
}
}
can anyone please help me how to download remote image to mobile using nativescript android app?
For example,
<StackLayout>
<Image [src]="remoteImgUrl"></Image>
<Label tap="downloadImg({{remoteImgUrl}})" text="Download">
</StackLayout>
when user clicks on 'Download' I need to save that image in android device.
here is an example based on this app here:
TypeScript
export function saveFile(res: ImageSource) {
fileName = "some-image-name" + ".jpeg";
var folderPath;
if (application.android) {
var androidDownloadsPath = android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_DOWNLOADS).toString();
folderPath = fileSystem.path.join(androidDownloadsPath, "MyImages");
}
var folder = fileSystem.Folder.fromPath(folderPath);
var path = fileSystem.path.join(folderPath, fileName);
var exists = fileSystem.File.exists(path);
if (!exists) {
var saved = res.saveToFile(path, enums.ImageFormat.jpeg);
}
}
The method accepts imageSource as a parameter so you can call it with fromUrl method for imageSource e.g.
import * as imageSource from "image-source";
imageSource.fromUrl(IMAGE_URL_HERE)
.then(res => {
saveFile(res); //
})
i'm trying to build angular2 component which draws chart (using jquery plot)
import {Component, ElementRef, Input, OnChanges} from 'angular2/core';
#Component({
selector: 'flot',
template: `<div>loading</div>`
})
export class FlotCmp implements OnChanges{
private width = '100%';
private height = 220;
static chosenInitialized = false;
#Input() private options: any;
#Input() private dataset:any;
#Input() private width:string;
#Input() private height:string;
constructor(public el: ElementRef) {}
ngOnChanges() {
if(!FlotCmp.chosenInitialized) {
let plotArea = $(this.el.nativeElement).find('div').empty();
plotArea.css({
width: this.width,
height: this.height
});
$.plot( plotArea, this.dataset, this.options);
FlotCmp.chosenInitialized = true;
}
}
}
Component getting chart "data" property as input parameter:
<flot [options]="splineOptions" [dataset]="dataset" height="250px" width="100%"></flot>
So far i managed to make it work as long as "dataset" is static variable.
this.dataset = [{label: "line1",color:"blue",data:[[1, 130], [3, 80], [4, 160], [5, 159], [12, 350]]}];
My problem is to make it work when data came as a promise:
export class App implements OnInit {
private dataset:any;
public entries;
getEntries() {
this._flotService.getFlotEntries().then(
entries => this.dataset[0].data = entries,
error => this.errorMessage = <any>error);
}
ngOnInit() {
this.getEntries()
}
constructor(private _flotService:FlotService) {
this.name = 'Angular2'
this.splineOptions = {
series: {
lines: { show: true },
points: {
radius: 3,
show: true
}
}
};
this.dataset = [{label: "line1",color:"blue",data:null]}];
}
}
For some reason data change cannot project to "flot" component
here is link to plunk
Please help
The problem is
entries => this.dataset[0].data = entries,
because only the inner state of the bound value is changed and Angular2 change detection doesn't observe the content only the value or reference itself.
A workaround would be to create a new array with the same content
this._flotService.getFlotEntries().then(
entries => {
this.dataset[0].data = entries;
this.dataset = this.dataset.slice();
},
In your case an additional event could work that notifies the child component that updates have happended.
Besides Günter's answer, another option is to implement your own change detection inside ngDoCheck() which will be called when your data comes back from the server:
ngDoCheck() {
if(this.dataset[0].data !== null && !this.dataPlotted) {
console.log('plotting data');
let plotArea = $(this.el.nativeElement).find('div').empty();
$.plot( plotArea, this.dataset, this.options);
this.dataPlotted = true;
}
}
I feel this is a cleaner approach, since we don't have to write code a specific way just to satisfy/trigger Angular change detection. But alas, it is less efficient. (I hate it when that happens!)
Also, the code you have in ngOnChanges() can be moved to ngOnInit().
Plunker
As Günter already mentioned, ngOnChanges() isn't called because the dataset array reference doesn't change when you fill in your data. So Angular doesn't think any input properties changed, so ngOnChanges() isn't called. ngDoCheck() is always called every change detection cycle, whether or not there are any input property changes.
Yet another option is to use #ViewChild(FlotCmp) in the parent component, which will get a reference to FlotCmp. The parent could then use that reference to call some method, say drawPlot(), on FlotCmp to draw/update the plot when the data arrives.
drawPlot(dataset) {
console.log('plotting data', dataset);
let plotArea = $(this.el.nativeElement).find('div').empty();
$.plot( plotArea, dataset, this.options);
this.dataset = dataset;
}
Plunker
This is more efficient than ngDoCheck(), and it doesn't have the issue I described above with the ngOnChanges() approach.
However, if I were to use this approach, I would rework the code somewhat, since I don't like how dataset is currently an input property, but then drawPlot() gets the data passed in via a function argument.