Imagine I have an application with StackNavigator, that is when user clicks something on Page 1 he/she is redirected to Page 2 (using Stack Navigator), etc.
I want to have an app bar, which is global per the application, that is, each page (Page 1, Page2, etc.) should show this app bar.
This is what is meant by app bar.
[![enter image description here][1]][1]
I found a component on documentation which looks like this:
<ToolbarAndroid
logo={require('./app_logo.png')}
title="AwesomeApp"
actions={[{title: 'Settings', icon: require('./icon_settings.png'), show: 'always'}]}
onActionSelected={this.onActionSelected} />
Does it mean I need to put this component inside render of each page separately? (Page 1, Page2). Like I said I want only a single toolbar for whole app.
Can someone show me some code example how to achieve this?
You could create a separate component called MyHeader.js and import to a file you want to use it. Give props to customize it. Example that used NativeBase:
MyHeader.js
import React from 'react';
import { Text, View, Image } from 'react-native';
import { Container, Header, Left, Body, Right, Title, Button, Icon } from 'native-base';
import styles from '../assets/style';
export default class MyHeader extends React.Component {
render() {
const goBack = this.props.goBackProp;
return (
<Container>
<Header>
<Left>
<Button
transparent
onPress={() => goBack()}
>
<Icon name="ios-arrow-back" />
</Button>
</Left>
<Body>
<Title> {this.props.title} </Title>
</Body>
<Right />
</Header>
</Container>
);
}
}
Now I could import it to any component like this:
import MyHeader from '../components/MyHeader';
export default class BlogScreen extends React.Component {
render() {
const {goBack} = this.props.navigation;
return (
<MyHeader goBackProp={goBack} title={'Blog'}/>
);
}
}
Related
I have 2 screens A and B, in screen A I have a TextInput, it's value is taken from another screen to this screen, its code looks like this
const ScreenA = (props) => {
const { position, suffix } = props;
const navigation = useNavigation();
const [textValue, setTextValue] = useState(props.name || '');
const navigationScreen = () => {
navigation.navigate('screenB');
};
return (
<>
<View>
<View>
<TextInput
value={textValue}
onChangeText={(text) => {
setTextValue(text);
}}
/>
</View>
<TouchableOpacity onPress={navigationScreen}>
<View>
<View style={styles.rectangle} />
</View>
</TouchableOpacity>
</View>
</>
);
};
export default ScreenA;
And after I press the button in screen A it moves to screen B, but after I go back from screen B to screen A, how can I keep my changed value at screen A,
The example is as follows: initially I go to screen A, the value of textValue will be "Abcd", then I edit the value of TextInput to be "A913" then I press submit button, and when in screen B rotates about screen A, i want the value of TextInput to be "A913" not "Abcd"
Can someone help me to solve this problem?
You should store your textinput value in Global state. and you can do so by using React Context or Redux
I have a TextInput inside a Modal with autoFocus={true}. As soon as Modal is opened TextInput is focused automatically but keyboard is not opening automatically.And surprisingly this is happening randomly. sometimes keyboard opens and sometimes not. This is happening in both emulator and real device.
Any suggestions to overcome this behavior? Thank You.
you can pass focus to TextInput using reference whenever a Modal is visible
<Modal onShow={() => {this.textInput.focus()}} >
<TextInput ref={input => {this.textInput = input}} />
</Modal>
I'm having the same problem currently. I used the solution suggestes by saumil and others previously; but adjusted for functional components:
import React, { useRef } from 'react';
...
let textInput = useRef(null);
<Modal onShow={() => { textInput.focus(); }} ...>
<TextInput ref={(input) => { textInput = input; }} ... />
</Modal>
It works, but I don't quite know what I'm doing (explanations welcomed). It's important to delete the autoFocus={true} to get consistent results.
Need to check though,
// Keyboard IpM
InputMethodManager imm = (InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
// input is TextInputEditText in here.
// adding a listener
input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean isFocused) {
if (isFocused) {
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
});
You can try focus method. Like this;
focus() {
this.textInputRef.focus();
}
render() {
return (
<TextInput
ref={ref => {
this.textInputRef = ref;
}}
/>
);
}
Now you can call focus function whenever you want. It will focus on the textinput and open the keyboard immediately.
This code is working for me. Use android:focusedByDefault="true"
<com.google.android.material.textfield.TextInputLayout
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:focusedByDefault="true" />
</com.google.android.material.textfield.TextInputLayout>
Lets say there is a login page with username\password TextFields and login Button. When the button is pressed a request is set to a server and ActivityIndicator is shown.
Currently I put StackLayout on top of all other controls not to give the user a possibility to click on them while processing the request. But in some cases TextField stays focused and the user can type there.
I'm already using a component to wrap all TextFields to show validation errors:
#Component({
selector: "field",
template: "<grid-layout><ng-content></ng-content>...</grid-layout>"
})
export class FieldComponent {
#ContentChild(NgModel) private input: NgModel;
...
}
My question is can I set isEnabled property to false on TextField inside ng-content from FieldComponent having NgModel or in some another way?
If it is impossible what is the best practices in this case to disable inputs when an app is busy?
Here is my solution for NativeScript+Angular:
setControlInteractionState() is recursive.
the TextField cursor is hidden (using native android API).
XML:
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
</GridLayout>
or
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
</GridLayout>
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
TypeScript:
import { Component, ViewChild, ElementRef } from "#angular/core";
import { View } from "ui/core/view";
import { LayoutBase } from "ui/layouts/layout-base";
import { isAndroid, isIOS } from "platform";
#Component({
templateUrl: "./SignIn.html"
})
export class SignInComponent {
#ViewChild("mainGrid")
MainGrid: ElementRef;
isBusy: boolean = false;
submit() : void {
try {
this.isBusy = true;
setControlInteractionState(<View>this.MainGrid.nativeElement, false);
//sign-in here...
}
finally {
this.isBusy = false;
setControlInteractionState(<View>this.MainGrid.nativeElement, true);
}
}
setControlInteractionState(view: View, isEnabled: boolean) : void {
view.isUserInteractionEnabled = isEnabled;
if (isAndroid) {
if (view.android instanceof android.widget.EditText) {
let control = <android.widget.EditText>view.android;
control.setCursorVisible(isEnabled);
}
}
if (view instanceof LayoutBase) {
let layoutBase = <LayoutBase>view;
for (let i = 0, length = layoutBase.getChildrenCount(); i < length; i++) {
let child = layoutBase.getChildAt(i);
setControlInteractionState(child, isEnabled);
}
}
}
}
NS 2.5.0
There are a couple way you can do this;
You can use a ngIf or binding on isEnabled to disable it based on a data bound value.
You can create a simple routine that you call (my preferred method).
require("nativescript-dom");
function screenEnabled(isEnabled) {
runAgainstTagNames('TextEdit', function(e) { e.isEnabled = isEnabled; });
runAgainstTagNames('Button', function(e) { e.isEnabled = isEnabled; });
}
The nativescript-dom plugin has the runAgainst*, or getElementBy* wrappers to talk to the native layer like you were talking to a html dom.
Full disclosure, I'm the author of nativescript-dom, it is one of the plugins that I use in almost every app/demo I do.
I found an easiest solution in Angular, so i am posting here for any future reference. First in app.component.html file i added a Grid and ScrollView like following:
<GridLayout>
<page-router-outlet></page-router-outlet>
<!-- hack to block UI -->
<ScrollView isUserInteractionEnabled="false" *ngIf="isLoading">
<ActivityIndicator busy="true"></ActivityIndicator>
</ScrollView>
</GridLayout>
Notice the page-router-outlet which is inside the Grid. By default it will be place at row="0". The next thing is ScrollView which has isUserInteractionEnabled set to false.
Now in your app.component.ts file add a a variable called isLoading and toggle it using some kind of events e.g RxJs Observable events
Expanding #KTCO's answer to get the size of the overlay exactly the same as the main grid:
import { Size, View } from "tns-core-modules/ui/core/view";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout/grid-layout";
...
...
dialogSize: Size;
mainGrid: GridLayout;
...
submit() {
this.mainGrid = <GridLayout>this.MainGrid.nativeElement;
this.dialogSize = this.mainGrid.getActualSize();
.....
.....
<GridLayout *ngIf="isBusy" rows="auto" columns="auto">
<GridLayout rows="*" columns="*" [width]="dialogSize.width" [height]="dialogSize.height" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="50" height="50" busy="true">
</ActivityIndicator>
</GridLayout>
I know this is a little old, but sometimes I just got to do things my way. If you want to accomplish this programmatically:
const excludedView = someViewToBeExcluded;
const enabler = (parentView:View, enable:boolean) => {
parentView.eachChildView(childView => {
if (childView != excludedView) {
enabler(childView, enable);
childView.isEnabled = enable;
}
return true;
});
};
enabler(page, false);
Note: This will not disable/enable the initial parentView (ie. the page in this example)
view xml(List view template)
<Alloy>
<ItemTemplate id="test">
<View onTouchstart="touch" width="Ti.UI.SIZE" height="Ti.UI.SIZE">
<Label>test</Label>
</View>
</ItemTemplate>
</Alloy>
controller js
function touch(e){
Ti.API.debug('touch!!');
};
not fire only android..(iOS is good)
and under don't use ListView code, no problem android(and iOS).
<Alloy>
<Window class="container">
<View onTouchstart="touch" width="Ti.UI.SIZE" height="Ti.UI.SIZE">
<Label>test</Label>
</View>
</Window>
</Alloy>
I do not know anyone solution?
There are no 'touchstart' method for ListViews, you could use 'dragstart' and 'scrollstart' as alternative.
Check out the available events:
http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.ListView
I've tried once to add the 'change' event to a TextField inside the ItemTemplace, but I've to create an workaround:
Change event on TextField inside ListView Template
If it's not possible, you have to use the 'itemclick' and get the selected row:
$.list.addEventListener('itemclick',function(list) {
var row = $.list.sections[0].getItemAt(list.itemIndex);
if(row && row.title) {
row.title.text = 'hello world';
//when you click the View with 'cancel' bindId
if(list.bindId == 'cancel') yourCancelAction();
//when you click the View with 'save' bindId
//(if you need to pass the row index to the function,
//in order to update something on the clicked row with a external function)
if(list.bindId == 'save') yourSaveAction(list.itemIndex);
$.list.sections[0].updateItemAt(list.itemIndex,row);
}
list = row = null;
});
I am new to flex and i am building an application for a mobile device.
I have a tabbed view navigtator application which has tabs at the bottom.
On top i have an action bar with back and some other buttons not set up yet.
i created a home tab as i want to have a home page for the first view after the splash screen then with the guide which says select a specfic tab below, the problem is i don't want to have the tab for home at the bottom.
Is there a way to keep the homepage as a first view but not have it as part of the tabs at the bottom?
In simple words:
Add IndexChangeEvent listener to your TabbedViewNavigator instance [ http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/events/IndexChangeEvent.html ]
When index is changed or is changing check for tabbedViewNavigatorInstance.navigators[newIndex] firstView equal Home.
If is Home hide Home tab else show Home tab, for this task check this post [ Hiding a tab in a Spark TabBar ]
The implementation itself can be done in multiple ways depending on your current flow.
Here an quick example that works:
<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160" applicationComplete="onAppComplete(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import spark.components.DataGroup;
import spark.events.IndexChangeEvent;
import views.HomeView;
private function onAppComplete(e : Event) : void {
this.tabbedNavigator.addEventListener(IndexChangeEvent.CHANGE, onIndexChange);
showHomeTab(false);
}
private function onIndexChange(e : IndexChangeEvent) : void {
var vn : ViewNavigator = this.navigators[e.newIndex] as ViewNavigator;
if(vn.firstView === HomeView){
showHomeTab(false);
} else {
showHomeTab(true);
}
}
private function showHomeTab(state : Boolean) : void {
var dg : DataGroup = this.tabbedNavigator.tabBar.dataGroup;
///if HomeView is at index 0
dg.getElementAt(0).visible = state;
dg.getElementAt(0).includeInLayout = state;
}
]]>
</fx:Script>
<s:ViewNavigator label="Home" width="100%" height="100%" firstView="views.HomeView"/>
<s:ViewNavigator label="Other" width="100%" height="100%" firstView="views.OtherView"/>
</s:TabbedViewNavigatorApplication>