Nativescript - onItemLoading callback called twice - nativescript

Have a simple NS/TS page with a ListView - the list is driven by an ObservableArray.
I add 1 entry to the array and the onItemLoading event gets called 2x.
Here's my XML
<ListView items="{{ dataItems }}"
itemLoading="{{ onItemLoading }}"
itemTap="{{ onNoteTap }}"
itemTemplateSelector="'note'">
<ListView.itemTemplates>
<template key="note">
<StackLayout>
<Label id="label"/>
</StackLayout>
</template>
</ListView.itemTemplates>
</ListView>
Here's my model class
export class NotesModel extends observable.Observable
{
public _listItemArray: ObservableArray<NoteItem>;
constructor()
{
super();
this._listItemArray = new ObservableArray<NoteItem>();
let item = new NoteItem();
item.label = "test";
this._listItemArray.push( item );
}
get dataItems(): ObservableArray<NoteItem>
{
return this._listItemArray;
}
onItemLoading( args: listviewModule.ItemEventData )
{
console.log( "onItemLoading =" + args.index + " " + args.view );
}
onNoteTap( args )
{
}
}
After I run the code, I get:
JS: constructor
JS: onItemLoading =0 StackLayout(217)#file:///app/my-notes-page.xml:39:15;
JS: onItemLoading =0 StackLayout(217)#file:///app/my-notes-page.xml:39:15;

This is your xml file
<ListView items="{{ dataItems }}"
itemLoading="onItemLoading"
itemTap="{{ onNoteTap }}"
itemTemplateSelector="'note'">
<ListView.itemTemplates>
<template key="note">
<StackLayout>
<Label id="label"/>
</StackLayout>
</template>
</ListView.itemTemplates>
</ListView>
This is your main.ts file code, its not the model ts
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
page.bindingContext = new HelloWorldModel();
}
export function onItemLoading(args) {
console.log('Calling here ');
}

Related

React Native using list of image in state with for loop

I'm beginner in react native. I want to use list of image and then push them to the child page with for loop
here is my code to explain what am I say.
state ={
a: ['./assest/image1.jpg','./assest/image2.jpg','./assest/image3.jpg'],
}
b = () => {
let d = [];
for (var i = 0; i<=this.state.a.lenght - 1; i++){
if (this.state.a[i] == ' ') { }
else {
d.push(<Child images={this.state.a[i]} />)
}
}
return d;
};
render() {
return <ScrollView style={styles.body}>
{this.b()}
</ScrollView>
}
}
And this is the code of child page which gives image url and set it to with props
constructor(props)
{
super(props);
}
render()
{
return <View style ={ styles.body}>
<Image style={styles.image}>{this.props.images}</Image>
</View>
}
}
What is the correct way to use list of image urls in state
And how to push them with for loop
state = {
a: ['./assest/image1.jpg','./assest/image2.jpg','./assest/image3.jpg'],
}
render() {
return (
<ScrollView style={styles.body}>
{
this.state.a.map((url,key) =>
<View style ={ styles.body}>
<Image
style={styles.image}
source={require(url)} // <--- changed
/>
</View>
)
}
</ScrollView>
)
}

How to access my aar library created by android studio from my nativescript angular project?

I am developing android and ios application using nativescript angular project.I want to access my android library(aar) created using android studio,I was created a plugin project in nativescript and put the aar file inside the plugin project.I was also installed the plugin project to my project and run it but it was not working.How can i solve the issue
My java code is:
package com.deemsysinc;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ScrollView;
public class ResponsiveScrollView extends ScrollView {
public interface OnEndScrollListener {
public void onEndScroll(int x,int y);
}
private boolean mIsFling;
private OnEndScrollListener mOnEndScrollListener;
public ResponsiveScrollView(Context context) {
this(context, null, 0);
}
public ResponsiveScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ResponsiveScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void fling(int velocityY) {
super.fling(velocityY);
mIsFling = true;
Log.d("FlingCalled","true");
}
#Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (mIsFling) {
if (Math.abs(y - oldY) < 2 || y >= getMeasuredHeight() || y == 0) {
if (mOnEndScrollListener != null) {
mOnEndScrollListener.onEndScroll(x,y);
}
mIsFling = false;
}
}
}
public OnEndScrollListener getOnEndScrollListener() {
return mOnEndScrollListener;
}
public void setOnEndScrollListener(OnEndScrollListener mOnEndScrollListener) {
this.mOnEndScrollListener = mOnEndScrollListener;
}
}
I was calling my android native class like:
declare var com: any
export class FoodCourtScroll extends ScrollView
{
protected attachNative() {
(<any>ScrollView.prototype).attachNative.call(this);
}
public createNativeView() {
return this.orientation === "horizontal" ? new org.nativescript.widgets.HorizontalScrollView(this._context) : new com.example.admin.scrollwork.ResponsiveScrollView(this._context);
}
}
Package.json in my plugin project is:
{
"name": "nativescript-fscroll",
"version": "0.0.1",
"main": "index.js",
"nativescript": {
}
}
Please help to solve the issue.
My app.module.ts file:
import { NgModule, NO_ERRORS_SCHEMA } from "#angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import {ActiveComponents,AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { NativeScriptUIListViewModule } from "nativescript-ui-listview/angular";
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import {NativeScriptFormsModule } from "nativescript-angular/forms";
import { NativeScriptHttpModule } from "nativescript-angular/http";
import { NativeScriptHttpClientModule } from "nativescript-angular/http-client";
import { DataService } from "~/app/data_services/data_services"
import { RestaurantService } from "~/app/services/restaurant.service";
import { NetWorkMonitor } from "~/app/Utils/NetworkMonitor";
import { AccordionModule } from "nativescript-accordion/angular";
import { TNSCheckBoxModule } from "nativescript-checkbox/angular";
import { NgRippleModule } from 'nativescript-ng-ripple';
import { CartService } from "~/app/services/cart.service";
import { ScrollView } from "ui/scroll-view";
import { registerElement } from "nativescript-angular";
import { OrderService, OrderData } from "~/app/services/order.service";
//import { FoodCourtScroll } from "~/app/Utils/deemsys_scroll";
import { DropDownModule } from "nativescript-drop-down/angular";
registerElement("PreviousNextView", () => require("nativescript-iqkeyboardmanager").PreviousNextView);
declare var org, android;
let ResponsiveScrollView;
function init() {
class ResponsiveScrollViewImpl extends org.nativescript.widgets.VerticalScrollView {
public mIsFling: boolean = false;
public fling(velocityY: any) {
super.fling(velocityY);
this.mIsFling = true;
}
}
ResponsiveScrollView = ResponsiveScrollViewImpl;
}
class FoodCourtScroll extends ScrollView {
createNativeView() {
init();
return new ResponsiveScrollView(this._context);
}
protected attachNative() {
const that = new WeakRef(this);
(<any>this).handler = new android.view.ViewTreeObserver.OnScrollChangedListener({
onScrollChanged: function (x: number, y: number, oldX: number, oldY: number) {
const owner: ScrollView = that.get();
if (owner) {
(<any>owner)._onScrollChanged();
console.log("mIsFling: " + owner.nativeViewProtected.mIsFling);
if (owner.nativeViewProtected.mIsFling) {
if (Math.abs(y - oldY) < 2 || y >= owner.nativeViewProtected.getMeasuredHeight() || y == 0) {
owner.nativeViewProtected.mIsFling = false;
owner.notify({
object: owner,
eventName: "dragEnd",
scrollX: x,
scrollY: y
});
}
}
}
}
});
this.nativeViewProtected.getViewTreeObserver().addOnScrollChangedListener((<any>this).handler);
}
}
registerElement("FoodCourtScroll", () => FoodCourtScroll);
#NgModule({
bootstrap: [
AppComponent
],
imports: [
NativeScriptModule,
AppRoutingModule,
NativeScriptUIListViewModule,
NativeScriptFormsModule,
FormsModule,
ReactiveFormsModule,
NativeScriptHttpModule,
NativeScriptHttpClientModule,
AccordionModule,
TNSCheckBoxModule,
NgRippleModule,
DropDownModule
],
declarations: [
AppComponent,
...ActiveComponents,
],
providers: [
NetWorkMonitor,
DataService,
RestaurantService,
CartService,
OrderService,
OrderData
],
schemas: [
NO_ERRORS_SCHEMA
]
})
export class AppModule { }
My Html file:
<ActionBar title="Menu" class="action-bar">
<ActionItem icon="res://search_red" ios.position="right" android.position="right"></ActionItem>
<!-- <StackLayout orientation="horizontal" ios.position="right">
<Image src="res://search_red" class="action-image"></Image>
</StackLayout> -->
<NavigationButton text="" icon="res://ic_arrow_red" (tap)="onNavBtnTap()"></NavigationButton>
</ActionBar>
<GridLayout rows="55,*,50">
<ScrollView #categoryList (scroll)="onHorizontalScroll($event)" orientation="horizontal" row="0" (loaded)="onLoaded($event)">
<StackLayout class="m-t-10 m-l-5 m-r-5" orientation="horizontal" >
<StackLayout orientation="horizontal" *ngFor="let category of AllMenuList; let a=index;" >
<Label height="55" width="170" [id]="category.categoryname" [class.category-label-Selected]="a===selectedIndex" class="category-label" [text]="category.categoryname" (tap)="onCategoryClick(a)"></Label>
<Label width="10"></Label>
</StackLayout>
</StackLayout>
</ScrollView>
<FoodCourtScroll id="subMenuScroll" (dragEnd)="onDragEnd($event)" (scrollEnds)="onScrollEnds($event)" (onScrollEnd)="onScrollEndd($event)" (scroll)="onVerticalScroll($event)" #ScrollList class="m-t-10" orientation="vertical" row="1">
<StackLayout id="vscroll">
<StackLayout width="100%" class="m-t-20 m-l-10" orientation="vertical" *ngFor="let category of AllMenuList; let i=index;" (tap)="onCellClick($event)">
<StackLayout [id]="category.categoryname" width="100%" >
<Label class="category-name" [text]="category.categoryname"></Label>
<StackLayout class="m-t-30" orientation="vertical" *ngFor="let food of category.menus; let k=index;" >
<GridLayout rows="auto,auto,auto,auto">
<StackLayout row="0" orientation="horizontal">
<Image [src]="food.cuisineType==1? '~/app/images/veg.png':'~/app/images/nonveg.png'" height="15" width="15"></Image>
<Label class="food-name" [text]="food.menuname"></Label>
</StackLayout>
<Label [text]="'$'+food.price" row="1" class="food-price"></Label>
<Button [visibility]="!food.isCountEnabled? 'visible': 'collapse'" class="add-button" row="1" horizontalAlignment="right" text="ADD" (tap)="Add(i,k)"></Button>
<GridLayout [visibility]="food.isCountEnabled? 'visible':'collapse'" class="plusandminuscontainer" row="1" horizontalAlignment="right">
<Label horizontalAlignment="left" verticalAlignment="center" class="minus" text="" (tap)="decrement(i,k)"></Label>
<Label horizontalAlignment="center" verticalAlignment="center" class="count-label" [text]="food.itemCount"></Label>
<Label horizontalAlignment="right" verticalAlignment="center" class="plus" text="" (tap)="increment(i,k)"></Label>
</GridLayout>
<StackLayout class="prep-container" row="2" orientation="horizontal">
<Label class="time-icon" text=""></Label>
<Label class="prep-time" [text]="food.preptime + ' minutes' "></Label>
</StackLayout>
<StackLayout row="3" class="hr-light m-l-20 m-t-10" ></StackLayout>
</GridLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</FoodCourtScroll>
<GridLayout [visibility]="cartItem==0? 'collapse':'visible'" class="anim-itemInfo cart-container" row="2">
<StackLayout orientation="horizontal" horizontalAlignment="left">
<Label verticalAlignment="center" class="m-l-10 cart-label" [text]="cartItem + ' Items'"></Label>
<Label verticalAlignment="center" class="m-l-10 cart-label" text="|"></Label>
<Label verticalAlignment="center" class="m-l-10 cart-label" [text]="'$'+cartTotal"></Label>
</StackLayout>
<Label verticalAlignment="center" class="m-r-10 viewcart-label" text="VIEW CART " horizontalAlignment="right"></Label>
</GridLayout>
</GridLayout>
My Html related ts file:
import { Component, OnInit, AfterViewInit,ViewChild, ElementRef} from "#angular/core";
import {Page, borderTopRightRadiusProperty} from "ui/page";
import { isIOS, isAndroid } from 'tns-core-modules/platform';
import { RadListView, ListViewEventData } from "nativescript-ui-listview";
//import { RestaurentMenuModel,RestaurentSubMenuModel } from "~/app/models/restaurant";
import { ObservableArray } from "tns-core-modules/data/observable-array";
import { run } from "tns-core-modules/application/application";
import { ScrollEventData, ScrollView } from "tns-core-modules/ui/scroll-view/scroll-view";
import { RouterExtensions } from "nativescript-angular/router";
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout/stack-layout";
import { Label } from "tns-core-modules/ui/label/label";
import { analyzeAndValidateNgModules } from "#angular/compiler";
import * as utils from "tns-core-modules/utils/utils";
import { setTimeout, setInterval} from "tns-core-modules/timer";
import { EventData } from "data/observable";
import { FoodScrollDelegate } from "~/app/Utils/scroll_delegate";
declare var UITableViewCellSelectionStyle;
declare var UIView, NSMutableArray, NSIndexPath;
//declare var setShowsHorizontalScrollIndicator;
#Component({
selector:"restaurant_menu",
moduleId:module.id,
templateUrl:"./restaurant_menu.component.html",
styleUrls:["./restaurant_menu.component.css"],
})
export class RestaurantMenu implements OnInit
{
//public cartCount:Number=1;
#ViewChild("ScrollList") scrollList:ElementRef;
#ViewChild("categoryList") categoryList:ElementRef;
public AllMenuList:any;
public cartTotal:any=0;
public cartItem:any=0;
public isCartVisible=false;
incrementItem:any=0;
decrementItem:any=0;
categoryContainer:Label;
subMenuContainers:StackLayout;
offsetValue:any=0;
dummyStatus:any=0;
iterate:any=0;
horizontalOffsetvalue:any=0;
calculationValue:any=0;
lengthHorizontaloffsetLength:any=0;
public selectedIndex:Number=0;
public scrollEnabled:Boolean=false;
lastItemY:Number=0;
subMenuScroll:ScrollView;
foodScrollDelegate:FoodScrollDelegate;
public id;
_delegate:any;
constructor(private page:Page,public routerExtension:RouterExtensions)
{
}
ngOnInit()
{
this.subMenuScroll=this.page.getViewById("subMenuScroll");
//this.subMenuScroll.ios.delegate=FoodScrollDelegate.initWithOriginalDelegate(this._delegate);
//console.log("PrintDelegate"+this.foodScrollDelegate.scrollViewDidEndDraggingWillDecelerate);
// this.foodScrollDelegate=new FoodScrollDelegate();
// this.foodScrollDelegate.yvalue.subscribe(yvalue=>{
// console.log("TheYYValue"+yvalue);
// });
}
onLoaded(event)
{
const scrollview = event.object;
if (scrollview.ios) {
scrollview.ios.showsHorizontalScrollIndicator = false;
}
else
{
scrollview.android.setHorizontalScrollBarEnabled(false);
}
}
onNavBtnTap()
{
this.routerExtension.backToPreviousPage();
}
public onHorizontalScroll(args:ScrollEventData)
{
// console.log("scrollX: " + args.scrollX);
// console.log("scrollY: " + args.scrollY);
}
public onVerticalScroll(args: ScrollEventData) {
console.log("Scrolling")
}
onScrollEndd(event)
{
console.log("TheAndroidEvent"+event.y);
}
}
You don't need a AAR library for simple implementations like this one. You could handle everything with your project here
declare var org, android;
let ResponsiveScrollView;
function init() {
class ResponsiveScrollViewImpl extends org.nativescript.widgets.VerticalScrollView {
public mIsFling: boolean = false;
constructor(context) {
super(context);
return global.__native(this);
}
public fling(velocityY: any) {
super.fling(velocityY);
this.mIsFling = true;
}
}
ResponsiveScrollView = ResponsiveScrollViewImpl;
}
class FoodCourtScroll extends ScrollView {
createNativeView() {
init();
return new ResponsiveScrollView(this._context);
}
protected attachNative() {
const that = new WeakRef(this);
(<any>this).handler = new android.view.ViewTreeObserver.OnScrollChangedListener({
onScrollChanged: function () {
const owner: ScrollView = that.get();
if (owner) {
const nativeView = owner.nativeViewProtected;
if (nativeView) {
const x = nativeView.getScrollX();
const y = nativeView.getScrollY();
const oldX = (<any>owner)._lastScrollX;
const oldY = (<any>owner)._lastScrollY;
console.log("mIsFling: " + nativeView.mIsFling);
console.log("newX: " + x);
console.log("oldX: " + oldX);
console.log("newY: " + y);
console.log("oldY: " + oldY);
if (nativeView.mIsFling) {
if (Math.abs(y - oldY) < 2 || y >= owner.nativeViewProtected.getMeasuredHeight() || y == 0) {
owner.nativeViewProtected.mIsFling = false;
owner.notify({
object: owner,
eventName: "dragEnd",
scrollX: x,
scrollY: y
});
}
}
}
(<any>owner)._onScrollChanged();
}
}
});
this.nativeViewProtected.getViewTreeObserver().addOnScrollChangedListener((<any>this).handler);
}
}
registerElement("FoodCourtScroll", () => FoodCourtScroll);
Here is the Playground Sample, make sure you execute Android specific code conditionally to not break the iOS version. You may consider writing platform specific TS files that implements FoodCourtScroll Or simply use a if condition based on platform.
Note: fling method is never called, I haven't used it before. So you might want to check your implementation there.

Nativescript list-view with multiple item templates toggling visibility

I have an issue with the radlistview with multiple item templates and toggling visibility.
I'm trying to re-create an according type display without using the nativescript-accordion plugin. By toggling the visibility attribute for a given item. Here's my xml:
<lv:RadListView row="3" items="{{ locationList }}" id="locationList" iosEstimatedRowHeight="0" itemTap="listViewItemTap" itemTemplateSelector="templateSelector" class="list-group">
<lv:RadListView.itemTemplates>
<template key="header">
<GridLayout columns="auto, *" visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}">
...content...</GridLayout>
</template>
<template key="list-item">
<GridLayout columns="*, auto" rows="auto, 15" visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}">...content...
</GridLayout>
</template>
</lv:RadListView.itemTemplates>
</lv:RadListView>
Here's the itemTap method:
if (tappedItem.type == "list-item" || tappedItem.type == "list-item-no-location") {
// Navigate to the details page with context set to the data item for specified index
topmost().navigate({....}});
} else if (tappedItem.type == "header") {
viewModel.collapseExpandItems(tappedItem);
setTimeout(() => {
if (platformModule.isIOS) {
// Uncomment the lines below to avoid default animation
// UIView.animateWithDurationAnimations(0, () => {
var indexPaths = NSMutableArray.new();
indexPaths.addObject(NSIndexPath.indexPathForRowInSection(rowIndex, args.groupIndex));
//console.log("indexPaths:", indexPaths);
listView.ios.reloadItemsAtIndexPaths(indexPaths);
// });
}
if (platformModule.isAndroid) {
listView.androidListView.getAdapter().notifyItemChanged(rowIndex);
}
}, 550);
And for the loading of the items, here is some code:
var newHeader = new Item(location.type, location.id, location.name, ..., true);
viewModel.locationList.push(newHeader);
var newItem = new Item(listItem.type, listItem.id, listItem.name, ... , true);
viewModel.locationList.push(newItem);
locationList being the ObservableArray in the viewModel.
And here is the Item class in the viewModel:
var Item = (function (_super) {
__extends(Item, _super);
function Item(type, id, name, ..., isItemVisible) {
var _this = _super.call(this) || this;
_this.type = type;
...
_this.isItemVisible = isItemVisible;
return _this;
}
Item.prototype.toggleVisibility = function (args) {
// console.dir(this);
console.log("toggleVisibility value: " + this.isItemVisible);
this.set("isItemVisible", !this.isItemVisible);
};
return Item;
}(Observable.Observable));
And finally the viewModel.collapseExpandItems method in the viewModel:
collapseExpandItems: function(tappedItem) {
this.locationList.forEach(function(item) {
//console.log("isItemVisible:", item.isItemVisible);
if ((item.type === 'list-item') && item.id === tappedItem.id) {
item.toggleVisibility();
}
});
},
It's hiding the items below the header item, but all the items below, even the ones that were not set to visibilty="collapsed".
Please see .gif for the behavior. Any ideas?enter image description here
It seems like momentarily its doing the right thing, but then it hides everything under, which is not what I want. I want it to just hide the items under the tapped header.

How to add items to nativescript actionbar programmatically?

How do I programmatically add items to the ActionBar? I've been trying to play around with this code below but the action items never update.
public setActionBarItems(actionBar: ActionBar) {
let tab = TabFactory.getTab(this.currentTabIndex);
let actionItem = new ActionItem();
actionItem.set("ios.systemIcon", "12");
actionItem.set("ios.position", "right");
actionItem.set("android.systemIcon", "ic_menu_search");
actionItem.set("android.position", "right");
actionBar.actionItems.addItem(actionItem);
// for (let actionItem of tab.actionItems) {
// actionBar.actionItems.addItem(actionItem);
// }
}
Do I perhaps need to tell the view somehow to update itself? I also tried setting actionItems="{{actionBarItems}}" on the ActionBar itself, but that throws a warning that the property is read-only.
Not that proud of it right now but here is a possible solution. Will probably refactor it in the future.
view model
export class MainViewModel extends Observable {
public isArticlesListTabVisible : boolean;
public isArchiveTabVisible : boolean;
public isAccountTabVisible : boolean;
public items: Array<BottomBarItem> = [
new BottomBarItem(0, "Archive", "ic_archive_black", "#FF303030"),
new BottomBarItem(1, "My List", "ic_list_black", "#FF303030"),
new BottomBarItem(2, "Account", "ic_account_circle_black", "#FF303030")
];
constructor() {
super();
}
get title() : string {
return "My List";
}
public setActionBarTitle(tab: ITab) {
this.notifyPropertyChange("title", tab.title);
}
public setActionBarItems(currentTab: ITab) {
if (currentTab instanceof ArticlesListTab) {
this.isArticlesListTabVisible = true;
this.isAccountTabVisible = false;
this.isArchiveTabVisible = false;
}
else if (currentTab instanceof AccountTab) {
this.isAccountTabVisible = true;
this.isArticlesListTabVisible = false;
this.isArchiveTabVisible = false;
}
else {
this.isArchiveTabVisible = true;
this.isArticlesListTabVisible = false;
this.isAccountTabVisible = false;
}
this.notifyPropertyChange("isArticlesTabVisible", this.isArticlesListTabVisible);
this.notifyPropertyChange("isAccountTabVisible", this.isAccountTabVisible);
this.notifyPropertyChange("isArchiveTabVisible", this.isArchiveTabVisible);
}
}
xml file
<ActionBar title="{{ title }}" class="action-bar" id="mainActionBar">
<ActionItem tap="{{onSearch}}"
ios.systemIcon="12" ios.position="right"
android.systemIcon="ic_menu_search" android.position="right" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Newest" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Oldest" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Most Progress" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
<ActionItem tap="{{onArticlesFilter}}"
ios.systemIcon="10" ios.position="right"
android.systemIcon="ic_menu_sort_by_size" android.position="popup" text="Least Progress" visibility="{{isArticlesListTabVisible ? 'visible' : 'collapsed'}}"/>
</ActionBar>
code behind
export function onBottomBarLoaded(args: EventData) {
this._bottomBar = args.object as BottomBar;
this._bottomBar.selectItem(1);
this._bottomBar.on('tabSelected', (args) => {
switchBottomBarTab(args.newIndex, args.oldIndex);
let currentTab = TabFactory.getTab(args.newIndex);
_model.setActionBarTitle(currentTab);
_model.setActionBarItems(currentTab);
});
}

Using image-cache for a LIstView in Typescript/Angular2/Nativescript

I've implemented a solution similar to this Answer however I'm using Angular2 conventions to define my page and not traditional Nativescript as in the referenced discussion.
The issue is that my ListView does not react to the notifyPropertyChange event to update the images when the upload into the image-cache is complete. The displayed images do not change from the placeholder image until I force it to update such as scrolling to hide the row and it updates when it reappears or leave the page and return.
I'm thinking the problem is that I'm mixing data/observable and the Angular2 Observable and/or the changeDetection: ChangeDetectionStrategy.OnPush. Suggestions?
Definition of ImageItem
import observable = require("data/observable");
import imageCache = require("ui/image-cache");
import imageSource = require("image-source");
import {Config, everliveConfig } from './config';
var cache = new imageCache.Cache();
cache.maxRequests = 10;
cache.placeholder = imageSource.fromFile("~/resource-seed/butterfly.jpg");
export class ImageItem extends observable.Observable
{
private _imageSrc: string
get imageSrc(): imageSource.ImageSource
{
var image = cache.get(this._imageSrc);
if (image)
{
return image; /* 3 */
}
cache.push(
{
key: this._imageSrc,
url: this._imageSrc,
completed: (image) =>
{
this.notifyPropertyChange('imageSrc', image);
console.log("this.notifyPropertyChange('imageSrc', image)");
}
});
return cache.placeholder;
}
constructor(imageId : string)
{
super();
everliveConfig.files.getDownloadUrlById(imageId)
.then( url => {
this._imageSrc = url;
});
}
}
Layout of my component
<GridLayout rows="32, 32, *" columns="*">
<Label row="0" text='Points of Interest' class='page-title' horizontalAlignment="center" > </Label>
<Label row="1" text='Tap to learn more' class='page-subtitle' horizontalAlignment="center" > </Label>
<ListView row="2" [items]="areas" class="area-group" (itemTap)="onItemTap($event)">
<template let-area="item">
<GridLayout rows="100" columns="38, 100, *" orientation="horizontal">
<Button col="0" [backgroundImage]='checkButtonImagePath(area)'
class='area-checkmark-button'
(tap)='onToggleCheck(area)'>
</Button>
<Image col="1" [src]='area.imageItem.imageSrc' class='area-thumbnail' stretch="aspectFill"></Image>
<Label col="2" [text]='area.Name' class='area-title'> </Label>
</GridLayout>
</template>
</ListView>
</GridLayout>
Component Definition
import { Component, ChangeDetectionStrategy, Inject } from "#angular/core";
import { Router } from '#angular/router';
import { Area, AreaList } from '../../shared/model/area.list';
#Component({
moduleId: module.id,
selector: "area",
templateUrl: "area.html",
styleUrls:["area.common.css"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AreaComponent {
public areas ;
constructor(
private _router: Router,
#Inject('AreaList') public areaList: AreaList) {
this.areas = areaList.list;
}
public onItemTap(args) {
console.log("Item Tapped at cell index: " + args.index);
var area: Area = this.areaList.area(args.index);
this._router.navigate(["/detail", area.Key]);
}
public onToggleCheck(area:Area) {
area.isChecked = !area.isChecked;
}
public checkButtonImagePath(area):string {
return area.isChecked ? 'res://dawes_11' : 'res://dawes_21';
}
}
Definition of AreaList
import {Config, everliveConfig } from './config';
import { Injectable } from "#angular/core";
import {ImageItem} from '../../shared/model/image.item';
import { ObservableArray } from "data/observable-array";
export class Area {
public isChecked = false;
public imageItem : ImageItem;
constructor(
public Id: string,
public Name: string,
public Description: string,
public Image: string,
public Key: string
) {
this.imageItem = new ImageItem(this.Image);
}
}
#Injectable()
export class AreaList {
public list : Array<Area>;
constructor () {
var areaData = everliveConfig.data('Area');
areaData.get(null, data => {
var l = new Array<Area>();
data.result.forEach(area => {
l.push(new Area(area.Id, area.Name, area.Description,
area.Image, area.Key))
})
this.list = l;
},
err => {
console.log(err.message);
});
}
public area(index: number) : Area {
return this.list[index];
}
public areaByKey(key: string) : Area {
for (let area of this.list) {
if (key == area.Key) {
return area;
}
}
return null;
}
}

Resources