Nativescript Observable.propertyChangeEvent - nativescript

I am trying to create some customer formatting on a field (to reproduce a masked text box functionality).
I have an observable and I am capturing the propertyChange Event. My question is: Can I modify the value of the observed property inside the event handler without entering in an infinite loop?
Here is my code:
model.customer.addEventListener(Observable.propertyChangeEvent, function(data) {
if (data.propertyName.toString() === 'homePhone') {
//Here is where I would like to change the value without triggering the event again
//The below code does not seem to be working
data.value = formatPhone(data.value);
}
});
I looked at https://github.com/bthurlow/nativescript-maskedinput, but unfortunately this module does not support databinding.
Thank you. Appreciate your help.

I have tested nativescript-maskedinput in a sample application and I was able to bind text property of this custom view. In regard to that if you add addEventListener and want to update for example TextField text property manually I think that property won't be updated properly. In addition I am attaching some sample code.
main-page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:mi="nativescript-maskedinput" navigatingTo="navigatingTo">
<StackLayout>
<Label text="Tap the button" class="title"/>
<mi:MaskedInput mask="1-999-999-9999? x999" hint="1-555-555-5555" text="{{ masktext }}" placeholder="#" />
<Button text="tapToView" tap="onTap" />
</StackLayout>
</Page>
main-page.js
var observable_1 = require("data/observable"); // Event handler for Page "navigatingTo" event attached in main-page.xml
var newObservable = new observable_1.Observable();
function navigatingTo(args) {
// Get the event sender
var page = args.object;
newObservable.set('masktext', '');
page.bindingContext = newObservable;
}
exports.navigatingTo = navigatingTo;
function onTap(args) {
var newvalue = newObservable.get('masktext');
console.log('newValueget + ' + newvalue);
}
exports.onTap = onTap;

I am not sure if it is possible to override the getter, but if it is not possible you can do this:
function isPhoneFormatted(phone) {
//your algorithm wich return true or false
}
model.customer.addEventListener(Observable.propertyChangeEvent, function(data) {
if (data.propertyName.toString() === 'homePhone') {
//Here is where I would like to change the value without triggering the event again
//The below code does not seem to be working
if (!isPhoneFormatted(data.value)) {
data.value = formatPhone(data.value);
}
}
});
Notice that it is not well tested!

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.

nativescript search bar on action bar not hiding

I'm trying to implement an action bar with a search-bar in it along with one buttton to show/hide the search-bar.
At start the search-bar must be hidden. Only must be showed title of action-bar and a action item to show search-bar. In the view is working as expected, but the problem arises when I go to another view and then go back to this view. The search-bar is not hidden, but neither is the button. I'm using an observable with a boolean to control the items.
When is tapped onSearch search-bar shows up, and when I catch the clear event I set search-bar to be hidden.
Finally, I am also facing that when I go back to this view, the clearEvent event is called two or three times. I don't understand why this behaviour. I have tried in Android so far.
When I launch the app, the action bar looks like the first image.
If I tap on the search icon the action bar is like the second one
And when I go to a different view and go back is like the third one:
Edit, I have changed the code needed but it does not work yet. Here it's a complete view and js file to reproduce the problem:
xml:
<dpg:DrawerPage navigatedTo="onNavigatedTo" navigatingTo="navigatingTo"
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:dpg="nativescript-telerik-ui/sidedrawer/drawerpage"
xmlns:drawer="nativescript-telerik-ui/sidedrawer"
xmlns:component="customcomponents/menu"
xmlns:lv="nativescript-telerik-ui/listview"
loaded="loaded"
>
<ActionBar class="actionB" title="stores" >
<android>
<NavigationButton icon="res://ic_menu_black_24dp" tap="showSlideout" />
</android>
<ios>
<ActionItem icon="res://ic_menu" ios.position="left" tap="showSlideout" />
</ios>
<ActionItem>
<ActionItem.actionView>
<SearchBar id="search" class="blank" backgroundColor="#3C5AFD" hint="Search..." visibility="{{ myShowSearchBar ? 'visible' : 'collapsed' }}" />
</ActionItem.actionView>
</ActionItem>
<ActionItem tap="onSearch"
ios.systemIcon="12" ios.position="right"
android.systemIcon="ic_menu_search" android.position="actionBar" visibility="{{ myShowSearchBar ? 'collapsed' : 'visible' }}"/>
</ActionBar>
<dpg:DrawerPage.sideDrawer>
<drawer:RadSideDrawer id="drawer" drawerSize="270">
<drawer:RadSideDrawer.drawerContent>
<component:menu />
</drawer:RadSideDrawer.drawerContent>
</drawer:RadSideDrawer>
</dpg:DrawerPage.sideDrawer>
</dpg:DrawerPage>
the js file:
var frameModule = require("ui/frame");
var observable = require("data/observable");
var searchBarModule = require("ui/search-bar");
var topmost;
var drawer;
var page;
var observableView = new observable.Observable({myShowSearchBar: false});
exports.loaded = function(args) {
page = args.object;
topmost = frameModule.topmost();
observableView.set("myShowSearchBar", false);
page.bindingContext = observableView;
drawer = page.getViewById("drawer");
var searchBarView = page.getViewById('search');
if (searchBarView.android) {
searchBarView.android.clearFocus();
}
searchBarView.on(searchBarModule.SearchBar.submitEvent, function (args) {
console.log("Search for " + (args.object).text);
observableView.set("myShowSearchBar", false);
});
searchBarView.on(searchBarModule.SearchBar.clearEvent, function (args) {
observableView.set("myShowSearchBar", false);
});
};
exports.showSlideout = function(){
drawer.toggleDrawerState();
}
exports.onSearch = function(args){
console.log("onSearch");
observableView.set("myShowSearchBar", true);
}
args.object is not the page when accessing it in the clearEvent or via onSearch method. And just before that you are creating the observable property myShowSearchBar for the page.bindingContext. So basically, you are handling different binding contexts.
Better (for readability and reuse) to create the separated view-model and access it via a known variable.
e.g.
var myViewModel = new Observable();
exports.loaded = function(args) {
myViewModel.set("myShowSearchBar", false);
page.bindingContext = myViewModel;
}
exports.onSearch = function(args){
myViewModel.set("myShowSearchBar", true);
}
Even better if use separation of concerns and extract the whole view-model in an own file and then import it.

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

Angular ng-click on inputed later on dom

i'm changing a innter html of a dom element with a button. And when button is clicked i want to fire another controller function. Something like that. ... But is not working :).
$scope.addBtn = function() {
$('domtarget').html('<button ng-click="removeButton();"></button>');
}
$scope.removeBtn = function() {
$('domtarget').html('');
}
Please suggest fix :)
Do not modify DOM inside your controller, ever.
<div ng-show="showMe"></div>
<button ng-click="showMe = !showMe;anotherAction()">Switch</button>
<button ng-click="someOtherAction()">Switch2</button>
.
function SomeCtrl($scope) {
$scope.showMe=true;
$scope.anotherAction = function () {
alert("gotcha");
};
$scope.someOtherAction = function () {
$scope.showMe = !$scope.showMe;
$scope.anotherAction();
};
}
For hiding/showing an element conditionally, use ng-show or ng-hide.
For firing an event on click, use ng-click

How to add event handlers to HTML buttons from within javascript code

I have a list of buttons (squares) in HTML like this -
<td><button id="18" ></button></td>
<td><button id="28" ></button></td>
<td><button id="38" ></button></td>
...
Earlier, for each button, I had to put code within the button tag itself to add an event handler, like this -
<button id="18" onclick="squareWasClicked(event)">
function squareWasClicked(event)
{
var element = event.target;
// more code
}
But now I have found out that it is not good practice. So instead, I am trying to add event handlers from within my javascript code. But I don't know how to do that. So far, I have tried this -
function assignEventsToSquares()
{
var i,j;
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
var squareID = "" + i + "" + j;
var square = document.getElementById(squareID);
square.onclick = squareWasClicked();
}
}
}
But this simply calls the squareWasClicked() function. So how do I add it as an event handler so that the function will be invoked when the square is clicked? Also, how do I say something like
square.onclick = squareWasClicked(event);
event is not detected in the JavaScript code. Please help.
Use element.addEventListener() (originating from the DOM 2 Events spec). In your case it will be
document.getElementById("18").addEventListener("click", squareWasClicked, false);
square.onclick = squareWasClicked();
This calls your click function and assigns the result to your element's event listener, which isn't what you want. Remove the ()s so that the function itself gets assigned.
Hence, it should look like this
square.onclick = squareWasClicked;

Resources