Accesing parent view's object inside from another view - appcelerator

When I click the label which is in datebar.xml showPicker() function fired but datepicker view is not visible which in datepicker.xml
Also alert is showing like "oops" Where is my fault? It should be a bug? I am trying to solve this problem since 3 days.
Also onDone function not working that is in datepicker.js on iOS
Thank you in advance. I am testing platfom is iOS and my SDK is 5.1.1 GA
<Alloy autoStyle="true">
<Window>
<View id="ViewDateBar">
<Require type="view" src="datebar" id="DateBar" />
</View>
<Require type="view" src="datepicker" id="DatePicker" />
</Window>
</Alloy>
datebar.xml
<Alloy>
<View id="datebar">
<Label onClick="ShowDatePicker"/>
</View>
</Alloy>
datebar.js
var ViewDatePicker = Alloy.createController("datepicker", {
onDone : function(SelectedDate) {
Alloy.Globals.JsonQsDateCurrent = SelectedDate;
}
});
function ShowDatePicker() {
ViewDatePicker.getView().showDatePicker();
}
datepicker.xml
<Alloy>
<View id="datepicker" visible="false">
<Picker id="picker" />
</View>
</Alloy>
datepicker.js
var moment = require("alloy/moment");
var args = arguments[0] || {};
var onDone = args.onDone;
init();
function init() {
if (OS_IOS) {
$.picker.setType(Titanium.UI.PICKER_TYPE_DATE);
}
}
function showDatePicker() {
if (OS_IOS) {
//> The iOS Picker will be slide up anddown the screen.
$.picker.setValue(Alloy.Globals.DateCurrent.toDate());
$.datepicker.visible = true;
} else if (OS_ANDROID) {
//> The Android picker will be a pop-up dialog.
$.picker.showDatePickerDialog({
value : Alloy.Globals.DateCurrent.toDate(),
callback : function(e) {
if (e.cancel) {
Ti.API.info('User canceled dialog');
} else {
done(e);
}
}
});
}
}
function done(e) {
if (OS_IOS) {
hideDatePicker();
onDone(selectedDate($.picker));
} else if (OS_ANDROID) {
if (!e.cancel) {
onDone(selectedDate(e));
}
}
}
function hideDatePicker() {
$.datepicker.visible = false;
}
function selectedDate(picker) {
return moment(picker.value);
}
$.datepicker.showDatePicker = showDatePicker;
$.datepicker.hideDatePicker = hideDatePicker;

If you define your requires in JavaScript, you can pass a reference to the parent.
window.js:
var dateBar = Alloy.createController('datebar', {parent: $.win});
I'm not sure why you're dividing up your controllers the way you are. Can you do it like this instead?
<Alloy autoStyle="true">
<Window>
<View id="ViewDateBar">
<Require type="view" src="datebar" id="DateBar" />
</View>
</Window>
</Alloy>
databar.xml:
<Alloy>
<View id="datebar">
<Label onClick="ShowDatePicker"/>
<Require type="view" src="datepicker" id="DatePicker" />
</View>
</Alloy>
And a final comment: there's not much benefit in defining a datepicker in an XML/TSS/JS controller set. There's not a ton of styling and such that the XML/TSS method would provide you. Why not simply define it in code in the label's click event handler?

Related

React-navigation focus on input blurs immediately when going back to previous screen

I want to focus on a textinput when I navigate to a new screen. This works when I add a screen to the stack, but not when I go back in the stack.
Instead, the input focuses for a second and blurs immediately.
Here is what I get:
Screen A is first in the stack and input blurs immediately
Screen B is added to the stack and works as intended
Any idea what is causing this?
FYI, I have the same issue if I use autoFocus.
Here is the whole (pretty straight forward) code:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import * as React from "react";
import { TextInput, View, Button, Text } from "react-native";
function ScreenA({ navigation }) {
const textInputRef = React.useRef();
const focusOnInput = e => {
textInputRef.current.focus();
};
navigation.addListener("focus", focusOnInput);
return (
<View>
<Text>SCREEN A</Text>
<TextInput ref={textInputRef} />
<Button title="Go to screen B" onPress={() => navigation.navigate("ScreenB")} />
</View>
);
}
function ScreenB({ navigation }) {
const textInputRef = React.useRef();
const focusOnInput = e => {
textInputRef.current.focus();
};
navigation.addListener("focus", focusOnInput);
return (
<View>
<Text>SCREEN B</Text>
<TextInput ref={textInputRef} />
<Button title="Go to screen A" onPress={() => navigation.navigate("ScreenA")} />
</View>
);
}
const Stack = createStackNavigator();
function TestComp() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen component={ScreenA} name="ScreenA" />
<Stack.Screen component={ScreenB} name="ScreenB" />
</Stack.Navigator>
</NavigationContainer>
);
}
export default TestComp;

Creating a Scalable Label in NativeScript

I found a post how explains it to create it for Ios.
http://nuvious.com/Blog/2015/5/9/creating-a-scalable-label-in-nativescript
But how to do this in Android.
That is what I tried:
import {Label} from "tns-core-modules/ui/label";
function ScalingLabel() {
this.myLabel = new Label();
let TextViewCompat;
if (androidx)
TextViewCompat = androidx.core.widget.TextViewCompat;
else
TextViewCompat = android.support.v4.widget.TextViewCompat;
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this.myLabel, 8, 100, 1, 2);
}
ScalingLabel.prototype = new Label();
exports.ScalingLabel = ScalingLabel;
Vue.registerElement('ScalingLabel', ScalingLabel);
I'm getting the following error:
[Vue warn]: Error in v-on handler: "TypeError: Could not load view
for: nativescalinglabel. Error: Cannot convert object to
Landroid/widget/TextView; at index 0"
You are not suppose to create a instance but extend the original class.
class ScalingLabel extends Label {
initNativeView() {
super.initNativeView();
if (this.android) {
androidx.core.widget.TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this.nativeViewProtected, 10, 100, 1, android.util.TypedValue.COMPLEX_UNIT_SP);
}
}
}
Vue.registerElement('ScalingLabel', () => ScalingLabel);
Then in your component try,
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar" />
<ScrollView>
<StackLayout class="form">
<TextField class="m-15 input input-border" v-model="message">
</TextField>
<Label :text="message" class="m-5 h3" />
<ScalingLabel :text="message" class="m-5 h3" height="30"
width="100%" textWrap="true" />
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
export default {
data() {
return {
message: ""
};
}
};
</script>

How can I change color / backgroundColor of list item in nativescript-vue?

I want to update selected item style when user taps on items. nextIndex/event.index is updated but style doesn't apply. Thanks for your help.
https://play.nativescript.org/?template=play-vue&id=ihH3iO
export default {
name: "CustomListView",
props: ["page", "title", "items", "selectedIndex"],
data() {
return {
nextIndex: this.selectedIndex ? this.selectedIndex : undefined
};
},
methods: {
onItemTap(event) {
this.nextIndex = event.index;
}
}
};
.selected {
color: white;
background-color: black;
}
<ListView for="(item, index) in items" #itemTap="onItemTap">
<v-template>
<Label :class="['list-item-label', { selected: index == nextIndex }]" :text="item" />
</v-template>
</ListView>
More info about this issue.
This is expected behavior because the ListView's item template is rendered and updated by the list view when scrolling (view recycling) if you need to make sure the list view is updated when you change your property, call refresh on it.
So the solution is
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar" />
<ListView v-for="(item, index) in items" #itemTap="onItemTap" ref="listView">
<v-template>
<Label :class="[{selected: index === nextIndex}, 'list-item-label']"
:text="item" />
</v-template>
</ListView>
</Page>
</template>
<script>
export default {
name: "CustomListView",
data() {
let selectedIndex = 2;
return {
items: ["Bulbasaur", "Parasect", "Venonat", "Venomoth"],
nextIndex: selectedIndex
};
},
methods: {
onItemTap(event) {
this.nextIndex = event.index;
this.$refs.listView.nativeView.refresh();
}
}
};
</script>
You need refresh your listView the code for that is this.$refs.listView.nativeView.refresh();
Don't forget to add the ref on the <ListView>

How i can parse webview content?

I need parse content from webview in my application,
is it possible? Now i can get only url page.
I tried search content in LoadFinished object, but i not found
WebView Component:
<template>
<Page class="webview-page">
<ScrollView>
<WebView height="100%" src="https://www.fl.ru/login/"
#loadFinished="parsePage" />
</ScrollView>
</Page>
</template>
<script>
export default {
methods: {
parsePage(webargs) {
//I need gets content here
console.log(webargs.url);
}
},
data() {
return {};
}
};
</script>
I expect get all html content from pageview
Try this,
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar" />
<GridLayout>
<WebView src="https://www.nativescript.org/" #loadFinished="onLoadFinished"
:visibility="visibility"></WebView>
<ScrollView v-if="html">
<Label class="h3" :text="html" textWrap="true"></Label>
</ScrollView>
</GridLayout>
</Page>
</template>
<script>
import {
device
} from "platform";
export default {
data() {
return {
html: ""
};
},
computed: {
visibility: function() {
return this.html ? "hidden" : "visible";
}
},
methods: {
onLoadFinished: function(args) {
const that = this,
webView = args.object,
jsStr = "document.body.innerHTML";
if (webView.ios) {
webView.ios.evaluateJavaScriptCompletionHandler(jsStr,
function(
result,
error
) {
if (error) {
console.log("error...");
} else if (result) {
that.html = result;
}
});
} else if (webView.android) {
// Works only on Android 19 and above
webView.android.evaluateJavascript(
jsStr,
new android.webkit.ValueCallback({
onReceiveValue: function(html) {
that.html = html;
}
})
);
}
}
}
};
</script>
If you like support for older Android versions (API level 17 & 18), the implementation gets bit difficult. You will have to implement a #JavascriptInterface interface which can be written only in Java. There is already an issue reported on enabling the ability to access Java Annotation form JavaScript. You may have to write an Android library project where you implement the #JavascriptInterface and utilise it to track content as explained here.
Playground Sample

React-Native refs undefined on text input

we are currently running React-Native 0.33
We are trying to use the refs to go from 1 text input to another when they hit the next button. Classic username to password.
Anyone have any idea? Below is the code we are using. From other posts I've found on stack this is what they've done; however, it's telling us that this.refs is undefined.
UPDATE
So I've narrowed the problem down to
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={{backgroundColor: 'transparent'}}
routeMapper={NavigationBarRouteMapper} />
} />
);
}
If I just render the code below in the renderScene function inside of the original render it works, however with the navigator it won't work. Does anyone know why? Or how to have the navigator show as well as render the code in renderScene to appear in the original render?
class LoginIOS extends Component{
constructor(props) {
super(props);
this.state={
username: '',
password: '',
myKey: '',
};
}
render() {
return (
<Navigator
renderScene={this.renderScene.bind(this)}
navigator={this.props.navigator}
navigationBar={
<Navigator.NavigationBar style={{backgroundColor: 'transparent'}}
routeMapper={NavigationBarRouteMapper} />
} />
);
}
renderScene() {
return (
<View style={styles.credentialContainer}>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="person" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
ref = "FirstInput"
style={styles.input}
placeholder="Username"
autoCorrect={false}
autoCapitalize="none"
returnKeyType="next"
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({username: text})}
value={this.state.username}
onSubmitEditing={(event) => {
this.refs.SecondInput.focus();
}}
>
</TextInput>
</View>
</View>
<View style={styles.inputContainer}>
<Icon style={styles.inputPassword} name="lock" size={28} color="#FFCD00" />
<View style={{flexDirection: 'row', flex: 1, marginLeft: 2, marginRight: 2, borderBottomColor: '#e0e0e0', borderBottomWidth: 2}}>
<TextInput
ref = "SecondInput"
style={styles.input}
placeholder="Password"
autoCorrect={false}
secureTextEntry={true}
placeholderTextColor="#e0e0e0"
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
returnKeyType="done"
onSubmitEditing={(event)=> {
this.login();
}}
focus={this.state.focusPassword}
>
</TextInput>
</View>
</View>
</View>
);
}
Try setting the reference using a function. Like this:
<TextInput ref={(ref) => { this.FirstInput = ref; }} />
Then you can access to the reference with this.FirstInput instead of this.refs.FirstInput
For a functional component using the useRef hook. You can use achieve this easily, with...
import React, { useRef } from 'react';
import { TextInput, } from 'react-native';
function MyTextInput(){
const textInputRef = useRef<TextInput>(null);;
return (
<TextInput ref={textInputRef} />
)
}
Try changing the Navigator's renderScene callback to the following (based on Navigator documentation) cause you will need the navigator object later.
renderScene={(route, navigator) => this.renderScene(route, navigator)}
Then, use 'navigator' instead of 'this' to get the refs.
navigator.refs.SecondInput.focus()

Resources