Nativescript Switch not to fire initial binding - nativescript

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>

Related

CKEditor 5 custom plugin not disabled in read mode

Right now I'm integrating custom plugins into the ckeditor 5. I created and added plugins using the ckeditor 5 documentation.
I also have a custom "super" build (similar to this example) that I use in my web application.
Now my problem is that my plugins will not be disabled in the ckeditor read mode (as showcased in the image at the button). The ckeditor documentation mentions that this should be the "default" behaviour for plugins / buttons.
If someone has an idea where I'm going wrong that'd be greatly appreciated!
Here is a skeleton example of my custom plugin class.
import { Plugin } from 'ckeditor5/src/core';
import { ButtonView } from 'ckeditor5/src/ui';
import ckeditor5Icon from './icons/insertvariable.svg';
export default class HWInsertVariable extends Plugin {
static get pluginName() {
return 'HWInsertVariable';
}
init() {
const that = this;
const editor = this.editor;
const model = editor.model;
let labelTxt = 'Variable einfügen';
editor.ui.componentFactory.add( 'hwInsertVariableButton', locale => {
const view = new ButtonView( locale );
view.set( {
label: labelTxt,
icon: ckeditor5Icon,
tooltip: true,
affectsData: true
} );
this.listenTo( view, 'execute', () => {
model.change( writer => {
that.buttonClicked();
} );
editor.editing.view.focus();
} );
return view;
} );
}
buttonClicked() {
//code
}
}
Im not sure what the correct method to resolve this is, as I also am facing the same. But what I have found so far, is that there might be a hacky way to get around this.
Checkout the docs regarding "disabling commands", "read-only" mode, and notice the CSS class "ck-disabled"
if/when I find a working way, I will try to come back here and post a better solution
Update:
I fixed this for me when I found that I was missing the section of the code in my my_plugin_ui.js where
const command = editor.commands.get('nameOfCommand');
// Execute the command when the button is clicked (executed).
buttonView.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');

react-navigation tabBarComponent cannot read property "key" of undefined

The documentation for react-navigation is pretty unclear to me about how to customise the tabBarComponent beyond simply changing colours. I am able to create my custom component for tabs and point to it like so;
import { TabNavigator } from 'myComponentsSomewhere'
...
const Navigator = createBottomTabNavigator(
{
Route1: Route1Component,
Route2: Route2Component,
...
},
{
tabBarComponent: props => <TabNavigator {...props} />,
tabBarOptions: {
activeTintColor: colors.first,
showLabel: false,
},
},
)
On the TabNavigator side, i get a load of props as i would expect;
activeTintColor: "#d85089",
getAccessibilityLabel,
getButtonComponent,
...
navigation,
...
Within the navigation prop, i can get to the state and then the routes etc... but i am unable to fire off any of the functions for getting buttons or rendering icons. (renderIcon, getButtonComponent)
The docs on these functions are weak, but looking at the code, it seems they both require a "route" object that contains a key, routeName etc.
That shape can be found in the navigation.state.routes array - but passing one of those objects simply throws the error;
Cannot read property 'key' of undefined
Here is an example of that code that errors;
import React from 'react'
import { View, Text } from 'react-native'
const TabNavigator = props => {
return props.navigation.state.routes.map(route =>
props.getButtonComponent(route),
)
}
export default TabNavigator
Ultimately, i want to be able to write my own code to contain the tabs, rather than be restricted to passing code to the react-navigation markup. I don't understand why none of the render functions received in the props will work when passed a complete route object, straight out of the navigation prop
It turns out i was not reading the error properly, which actually told me everything i needed to know...
Cannot read property 'key' of undefined
getButtonComponent (and all the other getting functions in navigation prop) required an object with route included, not just the route.
getButtonComponent({ route }) rather than getButtonComponent(route)
Simplicity wins...

Complex navigation in React Native using react-navigation and Redux

I have the following navigation structure in my React Native app:
StackNavigator configured with 3 routes:
Splash screen (React Component)
StackNavigator for my login flow
DrawerNavigator for my core app screens.
The DrawerNavigator has some dynamic multiple routes, but also one static route which is another StackNavigator.
Everything seems to be working as expected:
The store is being updated accordingly.
Navigation between screen works.
Go back between screen works when configured within each component, with the following command:
this.props.navigation.goBack();
My question is - is there a way for me to handle back button on Android globally? Currently when I click on the back button, nothing happens (due to the fact I'm using Redux). Should I handle the back button in each component or is there a way of doing it using Redux?
A bit late, but there is a way to handle this with redux. In your index.js file where you create your store you can make export a class and add a componentWillMount call to handle dispatching a call to your redux actions. Just remember to import the actions you need above.
const store = configureStore();
export default class Index extends Component {
componentWillMount = () => {
BackHandler.addEventListener('hardwareBackPress', () => {
const { nav: { routes } } = store.getState();
const currentRouteName = routes[routes.length-1].routeName;
if (currentRouteName === 'EditCoupleProfile') {
store.dispatch(editCoupleActions.navigateBack())
} else if ( currentRouteName === 'EditInterests' ) {
store.dispatch(interestsActions.navigateBack())
} else {
store.dispatch(popFromStack());
}
return true;
})
};
componentWillUnmount = () => {
BackHandler.removeEventListener('hardwareBackPress');
};
render() {
return (
<Provider store={store}>
<AppWithNavigation />
</Provider>
);
}
}

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.')
)
}
}

How do you expose methods from NativeScript custom components?

I want to expose a method from a custom component in a NativeScript project. Here is the conceptual setup:
MyComponent.xml
<StackLayout loaded="loaded"> ...UI markup... </StackLayout>
MyComponent.ts
export function loaded(args) { //do stuff };
export function myCustomMethod() { //do more stuff };
MyPage.xml
<Page xmlns:widget="..." loaded="loaded">
<widget:MyComponent id="myComponent" />
</Page>
MyPage.ts
export function loaded(args) {
let page = <Page>args.object;
let widget = page.getViewById("myComponent");
widget.myCustomMethod(); //<--THIS DOES NOT WORK
}
Question: How do I properly expose the myCustomMethod on the custom component so that the hosting page can access and call it via JavaScript?
By default, when trying to get a reference to the custom component on the page, a reference to the custom component's layout container is returned (in this example, a StackLayout).
So, as a workaround, I am doing this:
MyComponent.ts
export function loaded(args) {
let stackLayout = <StackLayout>args.object; //Layout container
stackLayout.myCustomMethod = myCustomMethod; //Attach custom method to StackLayout
}
It's a bit of a hack, but it's working. Is there any better way to expose custom component methods short of creating a super class for the layout container?
UPDATE
To be more accurate, the custom component has instance properties, so the eventual solution would need to support a scenario like...
MyComponent.ts
let isStarted = false;
export function loaded(args){ // do stuff };
export function myCustomMethod() { isStarted = true; };
As the method you want to reuse is not coupled to your customCompoenent, you can create a common file and require it where needed and reuse the exposed methods accordingly. Or you can directly import your MyComponent.ts and reuse its exported methods (not so good idea as you will probably have access to all exposed methods of your MyCompoennt.ts like onLoaded, onNavigationgTo, etc.)
For example:
in your navigator.ts (or if you prefer to your MyComponent.ts)
import frame = require("ui/frame");
export function navigateBack() {
frame.goBack();
}
and then reuse it where needed like this:
MyPage.ts
import navigatorModule = require("navigator");
export function goBack(args: observable.EventData) {
navigatorModule.goBack(); // we are reusing goBack() from navigator.ts
}
Another applicable option is to use MVVM pattern and expose your methods via the binding context.

Resources