Does RadListView still exist (Nativescript pro-ui) - nativescript

Edit: I have posted the higher level version of getting RadListView to work with the grouping function here. The question here addresses just getting RadListView to work in its basic form.
I have been away from Nativescript pro ui for a few months, and now am trying to put together a multilevel list (with categories, items in each category; and user able to hide and show categories with tap). From the discussion here I see that *ngFor is not the stable way to do a multilevel list (though it is the easiest!)
So now I am trying to use the pro ui listview, but the documentation is a few months old and uses the term "RadListView".
Does RadListView still exist? And what is the best documentation for doing a two or three level list in Nativescript Angular?
Details in case helpful:
So I am now trying to use RadListView to do this, but it is not clear to me that RadListView exists at all anymore. The market place listing for Nativescript pro-ui says the old pro-ui has been deprecated, and each item of the pro ui now must be downloaded individually (link).
It lists an npm listing for the pro ui "ListView", that uses the term "ListView". But, when you click on any of the documentation / sample code links in that npm listing, they all use the term "RadListView" (the old formulation).
I am not able to get RadListView to work. Even for the most simple example (which worked a few months ago), if I use RadListView in my component html, the screen is blank.
For example, I am trying to do a multilevel list. Looks like the "grouping" function in RadListView is the (only?) way to do this. I have cut and pasted in the code from here, but it does not work--blank screen with "RadListView" and no data with just "ListView".
example:
ts:
import { ListViewEventData, LoadOnDemandListViewEventData } from "nativescript-ui-listview";
import { RadListViewComponent } from "nativescript-ui-listview/angular";
export class SampleComponent implements OnInit {
public arrayItems = [
{category:'person', name: 'jim', description: 'a very
nice person'},
{category:'jungle animal', name: 'lion', description:
'king of the jungle'}
]
private _myGroupingFunc: (item: any) => any;
constructor (){
this.myGroupingFunc = (item: arrayItems) => {
return item.category;
};
}
ngOnInit(): void {
}
get myGroupingFunc(): (item: any) => any {
return this._myGroupingFunc;
}
set myGroupingFunc(value: (item: any) => any) {
this._myGroupingFunc = value;
}
}...
html:
<StackLayout>
<ListView [items]="arrayItems" enableCollapsibleGroups="true" [groupingFunction]="myGroupingFunc" >
<ng-template tkListItemTemplate let-arrayItem="item" >
<StackLayout>
<Label [text]="arrayItem.name"></Label>
<Label [text]="arrayItem.description"></Label>
</StackLayout>
</ng-template>
</ListView>
</StackLayout>
With this code, copied from here, there are not entries that appear (just the lines of a ListView with nothing inside). If I say "RadListView" instead of "ListView", the screen is entirely blank. I would definitely appreciate if someone has updated code for this action.

Thanks to Ian MacDonald for his help. RadListView does remain the Nativescript pro-ui version of list view. It works for me like this:
$ tns plugin add nativescript-ui-listview
$ npm i
$ tns update //don't know why/what was out of date, but features like the grouping function did not work for me until I ran this.
coolComponent.module.ts: (if using lazy loading, I was only able to get RadListView to work by importing the module directly into the component module)
import { NativeScriptUIListViewModule } from "nativescript-ui-listview/angular";
...
#ngModule({
imports: [
...
NativeScriptUIListViewModule,
]
...
coolComponent.html:
<GridLayout>
<RadListView [items]="cats" >
<ng-template tkListItemTemplate let-cat="item">
<StackLayout>
<Label [text]="cat"></Label>
</StackLayout>
</ng-template>
</RadListView>
</GridLayout>
coolComponent.ts:
import { Component } from "#angular/core";
import { ListViewEventData, RadListView } from "nativescript-ui-listview";
...
export class CoolComponent {
public cats = ['tiger', 'lion', 'puma']
constructor(){
}
}

Related

Cannot capture events from custom component in NS6

I am using NS 6.0 Core. Testing on a physical Android device (have not tried this on iOS yet).
In a nutshell, I have nested components and in the inside component, I want to capture a custom event and pass it to the host component.
Inside component (called TopBar):
<StackLayout padding="10" orintation="horizontal" loaded="onLoaded">
<Label text="" class="wa" fontSize="24" vertcalAlignment="middle" tap="back" />
</StackLayout>
import { StackLayout } from 'tns-core-modules/ui/layouts/stack-layout';
import { EventData } from 'tns-core-modules/ui/core/view/view';
var stack: StackLayout;
let eventData: EventData = {
eventName: "onBackEvent",
object: stack
}
export function onLoaded(args) {
stack = <StackLayout>args.object;
}
export function back() {
stack.notify(eventData);
}
The host component
import { Page } from "tns-core-modules/ui/page/page";
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout/stack-layout";
import { EventData, Observable } from "tns-core-modules/data/observable";
var model: Observable = new Observable();
var page: Page;
export function onLoaded(args: EventData) {
page = <Page>args.object;
var topBar: StackLayout = page.getViewById('topBar');
topBar.on('onBackEvent', () => {
console.log('go back');
});
page.bindingContext = model;
}
<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:tb="components/shared/top-bar/top-bar" loaded="onLoaded" actionBarHidden="true">
<GridLayout rows="*, 75" columns="*">
<StackLayout class="page-content">
<tb:TopBar id="topBar" height="50"></tb:TopBar>
</StackLayout>
</GridLayout>
</Page>
Any ideas on what I might be missing?
Thanks
{N} automatically trims event names starting with on, hence onBackEvent will be recorded as BackEvent only. So notifying onBackEvent will not have any effect.
In my opinion it makes sense, also when I checked last time Angular didn't use to support event names prefixed with on with event binding. That could also be a reason they had to force this as a standard measure.
So, after hours of trial and error, I manage to make it work.
The issue is that when notifying the client, we have to declare the EventData object as such:
let eventData: EventData = {
eventName: "BackEvent",
object: stack
}
Note that we capture the event by subscribing to onBackEvent but setting up the event name as BackEvent. I am not sure why this works or where in the documentation it is written, but this change did the job.
If anyone has more information about this, please post it here so we can all learn.
Thanks.

Inconsistent image move behavior in quilljs with react

i have encountered an issue, when making a text editor with support of image based tags. There is a need to move those tags around freely in the text, which is being made impractical by this issue.
Basically when I start dragging an image, and then drop it on desired location, one of two results can happen: A) it works as intended and B) the image is dropped to the end/beginning of the sentence. You can see the behaviour in attached gif. Resulting behavior
I'm using react and typescript combination for creating the page with quill being inserted in a component.
// TextEditor/index.tsx
import * as React from 'react';
import * as Quill from 'quill';
import { TextEditorState, TextEditorProps } from '../#types';
import { generateDelta } from '../#utils/generateDelta';
const formats = [
'image'
];
class TextEditor extends React.Component<TextEditorProps, TextEditorState> {
constructor(props: TextEditorProps) {
super(props);
this.state = {
Editor: undefined
}
}
componentDidMount() {
const self = this;
this.setState({Editor: new Quill('#editor-container', {formats: formats, debug: 'warn'})});
}
changeText(text: string) {
if(typeof(this.state.Editor) !== 'undefined') {
this.state.Editor.setContents(generateDelta(text), 'api');
}
}
render() {
return (
<div id="editor-container"></div>
);
}
}
export default TextEditor;
And the usage of this component in another component is just
// editor.tsx
import TextEditor from '../QuillEditor/TextEditor';
...
onUpdate(text: string) {
this.refs.targetEditor.changeText(text);
}
...
render() {
return (
...
<TextEditor
ref={'targetEditor'}
/>
...
)
}
I have tried to change the text editor to just contentEditable div and that worked flawlessly, so it shouldn't be because of some css glitch.
Has anyone some idea of what could be causing this?
EDIT Feb 6:
I have found out, that this issue is manifesting only in Chrome, as IE and MS Edge did not encountered this issue. I have tried to switch off all extensions, yet the issue is still there. Private mode also didn't help.
After few days of research I have figured out what is causing the issue.
The combination of Quill and React won't work, because of the way React 'steals' input events, while creating the shadow DOM. Basically, because it tries to process my input in contenteditable div created by Quill, it causes some actions to not fire, resulting in the weird behaviour. And because Quill tries to do it by itself, outside of React DOM.
This I have proved in my simple testing project, where adding a simple input tag anywhere on the page broke down the Quill editor.
Possible solution would be to use react-quill or some other component container, however I haven't managed to make it successfully work, or write some yourself, which would incorporate Quill to React in its DOM compatible way.

Nativescript Switch prevent change event firing on initial binding

Hi my template is something like the below
<ListView [items]="modules">
<template let-item="item" >
<StackLayout orientation="vertical">
<Switch (checkedChange)="onSwitchModule(item.id,$event)" [checked]="item.active"></Switch>
</StackLayout>
</template>
</ListView>
My controller is
ngOnInit() {
this._moduleService.getUserModules()
.subscribe(
response=>{
this.modules = response.data;
}
)
}
onSwitchModule(itemId) {
console.log(itemID); //Gets called on initial true binding on switch checked
}
The onSwitchModule get called everytime the page loads with item.active is true on any item, how to handle this ?
NOTE: Beginner in Nativescript
What I did to overcome this is I watch for tap events instead of checkedChange:
<Switch (tap)="switchClicked" [checked]="item.active"></Switch>
and in the callback, you can get the current item from bindingContext:
function switchClicked(args) {
const item = args.object.bindingContext.item;
}
I ran into a similar issue: loading up settings data from an API, and having the checked event fire for the value I'd set from the api -- not desirable in my case. I didn't see a great way to prevent events from firing on the initial binding, so I decided to simply ignore events until I knew they were legit events from the user actually using the switch.
I did that by using a property switchReady to keep track of when you want to start recognizing change events. This pattern also keeps the toggle disabled until you're ready to start accepting changes. This makes use of Switch's isEnabled property, see docs here.
Markup
<Switch [checked]="currentSettings.pushTurnedOn" [isEnabled]="switchReady" (checkedChange)="onPushSettingChange($event)" row="0" col="1"></Switch>
Component
export class SettingsComponent implements OnInit {
currentSettings: Settings = new Settings(false)
switchReady: boolean = false
ngOnInit() {
this.getCurrentSettings()
}
public onPushSettingChange(args) {
let settingSwitch = <Switch>args.object
if (settingSwitch.isEnabled) {
// do something with the event/change
} else {
// we aren't ready to accept changes, do nothing with this change
return
}
}
getCurrentSettings() {
this.settingsService.loadCurrentSettings().subscribe(
() => {
this.currentSettings = this.settingsService.currentSettings
// we've applied our api change via data binding, it's okay to accept switch events now
this.switchReady = true
},
err => alert('There was a problem retrieving your settings.')
)
}
}

Nativescript Switch not to fire initial binding

My view is
<Switch checked="{{ active }}" propertyChange="onCheckChange"/>
exports.onCheckChange = function(args)
{
//Api Service call
}
Actually I am binding the active value by API call and the issue is that onCheckChange gets executed during the initial binding with false value, so whenever I initially set the active==true by api service call and load the page, the onCheckChange is executed with checked==false, can anyone give me an idea about this please.
Note: Beginner in Nativescript
I battled with the checked property a lot so I opted for two-way binding, which behaves as expected:
// test.xml
<Switch [(ngModel)]="isUnicorn"></Switch>
// test.ts
isUnicorn: boolean = true;
......
if (this.isUnicorn) {
console.log("It is a unicorn");
}
Note that to get two-way binding to work you need to import NativeScriptFormsModule in app.module.ts or applicable module for instance:
// app.module.ts
import { NativeScriptFormsModule } from "nativescript-angular/forms";
......
#NgModule({
imports: [
NativeScriptFormsModule,
......
],
exports: [
NativeScriptFormsModule,
......
],
......
The two-way data-binding (described by leoncc) might be specific to the Angular NativeScript.
Here's a workaround without the two-way data binding, hopefully it will be easier to port to the plain NativeScript if needs be.
In the controller we can get the state of the Switch with a ViewChild query:
checked = true;
#ViewChild ('switch') private switch: ElementRef;
switched() {
let switch: Switch = this.switch.nativeElement;
this.checked = switch.checked}
And in the template we should invoke the switched change handler:
<Switch #switch [checked]="checked" (checkedChange)="switched()" class="switch"></Switch>

React Native: Render Image from props [duplicate]

I'm currently building a test app using React Native. The Image module thus far has been working fine.
For example, if I had an image named avatar, the below code snippet works fine.
<Image source={require('image!avatar')} />
But if I change it to a dynamic string, I get
<Image source={require('image!' + 'avatar')} />
I get the error:
Requiring unknown module "image!avatar". If you are sure the module is there, try restarting the packager.
Obviously, this is a contrived example, but dynamic image names are important. Does React Native not support dynamic image names?
This is covered in the documentation under the section "Static Resources":
The only allowed way to refer to an image in the bundle is to literally write require('image!name-of-the-asset') in the source.
// GOOD
<Image source={require('image!my-icon')} />
// BAD
var icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('image!' + icon)} />
// GOOD
var icon = this.props.active ? require('image!my-icon-active') : require('image!my-icon-inactive');
<Image source={icon} />
However you also need to remember to add your images to an xcassets bundle in your app in Xcode, though it seems from your comment you've done that already.
http://facebook.github.io/react-native/docs/image.html#adding-static-resources-to-your-app-using-images-xcassets
This worked for me :
I made a custom image component which takes in a boolean to check if the image is from web or is being passed from a local folder.
// In index.ios.js after importing the component
<CustomImage fromWeb={false} imageName={require('./images/logo.png')}/>
// In CustomImage.js which is my image component
<Image style={styles.image} source={this.props.imageName} />
If you see the code, instead of using one of these:
// NOTE: Neither of these will work
source={require('../images/'+imageName)}
var imageName = require('../images/'+imageName)
I'm just sending the entire require('./images/logo.png') as a prop. It works!
RELEVANT IF YOU HAVE KNOWN IMAGES (URLS):
The way I hacked my way through this problem:
I created a file with an object that stored the image and the name of the image:
export const ANIMAL_IMAGES = {
dog: {
imgName: 'Dog',
uri: require('path/to/local/image')
},
cat: {
imgName: 'Cat on a Boat',
uri: require('path/to/local/image')
}
}
Then I imported the object into the component where I want to use it and just do my conditional rendering like so:
import { ANIMAL_IMAGES } from 'path/to/images/object';
let imgSource = null;
if (condition === 'cat') {
imgSource = ANIMAL_IMAGES.cat.uri;
}
<Image source={imgSource} />
I know it is not the most efficient way but it is definitely a workaround.
Hope it helps!
If you're looking for a way to create a list by looping through a JSON array of your images and descriptions for example, this will work for you.
Create a file (to hold our JSON database) e.g ProfilesDB.js:
const Profiles = [
{
id: '1',
name: 'Peter Parker',
src: require('../images/user1.png'),
age: '70',
},
{
id: '2',
name: 'Barack Obama',
src: require('../images/user2.png'),
age: '19',
},
{
id: '3',
name: 'Hilary Clinton',
src: require('../images/user3.png'),
age: '50',
},
];
export default Profiles;
Then import the data in our component and loop through the list using a FlatList:
import Profiles from './ProfilesDB.js';
<FlatList
data={Profiles}
keyExtractor={(item, index) => item.id}
renderItem={({item}) => (
<View>
<Image source={item.src} />
<Text>{item.name}</Text>
</View>
)}
/>
Good luck!
As the React Native Documentation says, all your images sources needs to be loaded before compiling your bundle
So another way you can use dynamic images it's using a switch statement. Let's say you want to display a different avatar for a different character, you can do something like this:
class App extends Component {
state = { avatar: "" }
get avatarImage() {
switch (this.state.avatar) {
case "spiderman":
return require('./spiderman.png');
case "batman":
return require('./batman.png');
case "hulk":
return require('./hulk.png');
default:
return require('./no-image.png');
}
}
render() {
return <Image source={this.avatarImage} />
}
}
Check the snack: https://snack.expo.io/#abranhe/dynamic-images
Also, remember if your image it's online you don't have any problems, you can do:
let superhero = "spiderman";
<Image source={{ uri: `https://some-website.online/${superhero}.png` }} />
First, create a file with image required - React native images must be loaded this way.
assets/index.js
export const friendsandfoe = require('./friends-and-foe.png');
export const lifeanddeath = require('./life-and-death.png');
export const homeandgarden = require('./home-and-garden.png');
Now import all your assets
App.js
import * as All from '../../assets';
You can now use your image as an interpolated value where imageValue (coming in from backend) is the same as named local file ie: 'homeandgarden':
<Image style={styles.image} source={All[`${imageValue}`]}></Image>
Important Part here:
We cannot concat the image name inside the require like [require('item'+vairable+'.png')]
Step 1: We create a ImageCollection.js file with the following collection of image properties
ImageCollection.js
================================
export default images={
"1": require("./item1.png"),
"2": require("./item2.png"),
"3": require("./item3.png"),
"4": require("./item4.png"),
"5": require("./item5.png")
}
Step 2: Import image in your app and manipulate as necessary
class ListRepoApp extends Component {
renderItem = ({item }) => (
<View style={styles.item}>
<Text>Item number :{item}</Text>
<Image source={Images[item]}/>
</View>
);
render () {
const data = ["1","2","3","4","5"]
return (
<FlatList data={data} renderItem={this.renderItem}/>
)
}
}
export default ListRepoApp;
If you want a detailed explanation you could follow the link below
Visit https://www.thelearninguy.com/react-native-require-image-using-dynamic-names
Courtesy : https://www.thelearninguy.com
you can use
<Image source={{uri: 'imagename'}} style={{width: 40, height: 40}} />
to show image.
from:
https://facebook.github.io/react-native/docs/images.html#images-from-hybrid-app-s-resources
import React, { Component } from 'react';
import { Image } from 'react-native';
class Images extends Component {
constructor(props) {
super(props);
this.state = {
images: {
'./assets/RetailerLogo/1.jpg': require('../../../assets/RetailerLogo/1.jpg'),
'./assets/RetailerLogo/2.jpg': require('../../../assets/RetailerLogo/2.jpg'),
'./assets/RetailerLogo/3.jpg': require('../../../assets/RetailerLogo/3.jpg')
}
}
}
render() {
const { images } = this.state
return (
<View>
<Image
resizeMode="contain"
source={ images['assets/RetailerLogo/1.jpg'] }
style={styles.itemImg}
/>
</View>
)}
}
To dynamic image using require
this.state={
//defualt image
newimage: require('../../../src/assets/group/kids_room3.png'),
randomImages=[
{
image:require('../../../src/assets/group/kids_room1.png')
},
{
image:require('../../../src/assets/group/kids_room2.png')
}
,
{
image:require('../../../src/assets/group/kids_room3.png')
}
]
}
when press the button-(i select image random number betwenn 0-2))
let setImage=>(){
//set new dynamic image
this.setState({newimage:this.state.randomImages[Math.floor(Math.random() * 3)];
})
}
view
<Image
style={{ width: 30, height: 30 ,zIndex: 500 }}
source={this.state.newimage}
/>
I know this is old but I'm going to add this here as I've found this question, whilst searching for a solution. The docs allow for a uri: 'Network Image'
https://facebook.github.io/react-native/docs/images#network-images
For me I got images working dynamically with this
<Image source={{uri: image}} />
<StyledInput text="NAME" imgUri={require('../assets/userIcon.png')} ></StyledInput>
<Image
source={this.props.imgUri}
style={{
height: 30,
width: 30,
resizeMode: 'contain',
}}
/>
in my case i tried so much but finally it work StyledInput component name
image inside the StyledInput if you still not understand let me know
Say if you have an application that has similar functionality as that of mine. Where your app is mostly offline and you want to render the Images one after the other. Then below is the approach that worked for me in React Native version 0.60.
First create a folder named Resources/Images and place all your images there.
Now create a file named Index.js (at Resources/Images) which is responsible for Indexing all the images in the Resources/Images folder.
const Images = {
'image1': require('./1.png'),
'image2': require('./2.png'),
'image3': require('./3.png')
}
Now create a Component named ImageView in your choice of folder. One can create functional, class or constant component. I have used the Const component. This file is responsible for returning the Image depending on the Index.
import React from 'react';
import { Image, Dimensions } from 'react-native';
import Images from './Index';
const ImageView = ({ index }) => {
return (
<Image
source={Images['image' + index]}
/>
)
}
export default ImageView;
Now from the component wherever you want to render the Static Images dynamically, just use the ImageView component and pass the index.
< ImageView index={this.qno + 1} />
Create a constant where you save the image path including require, then in source put the name of that constant.
const image = condition ? require("../img/image1.png") : require('../img/image2.png');
<Image source={image} />
Here is a simple and truly dynamic solution to the problem if you have a bigger no of files.
[Won't work for Expo Managed]
Although the question is old I think this is the simpler solution and might be helpful. But I beg a pardon for any terminological mistakes, correct me please if I do any.
INSTEAD OF USING REQUIRE WE CAN USE THE URI WITH NATIVE APP ASSETS FOR ANDROID (AND/OR iOS). HERE WE WILL DISCUSS ABOUT ANDROID ONLY
URI can easily be manipulated as per the requirement but normally it's used for network/remote assets only but works for local and native assets too. Whereas require can not be used for dynamic file names and dirs
STEPS
Open android/app/src/main/assets folder from your App.js or index.js containing directory, if the assets folder doesn't exist create one.
Make a folder named images or any NAME of your choice inside assets, and paste all the images there.
Create a file named react-native.config.js in the main app folder containing App.js or index.js.
Add these lines to the new js file:
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets/YOUR_FOLDER_NAME/'],
};
at the place of YOUR_FOLDER_NAME use the newly created folder's name images or any given NAME
Now run npx react-native link in your terminal from main app folder, this will link/add the assets folder in the android bundle. Then rebuild the debug app.
From now on you can access all the files from inside android/app/src/main/assets in your react-native app.
For example:
<Image
style={styles.ImageStyle}
source={{ uri: 'asset:/YOUR_FOLDER_NAME/img' + Math.floor(Math.random() * 100) + '.png' }}
/>
You should use an object for that.
For example, let's say that I've made an AJAX request to an API and it returns an image link that I'll save to state as imageLink:
source={{uri: this.state.imageLink}}

Resources