How change whole view without calling navigate()? - nativescript

I need dynamically change whole view to another (for example, root layout should be changed to DockLayout), but without using Frame.navigate() method.
How do this?
Other question is about View.load() method. What use cases of this method?
const view = parse(`<StackLayout><Button tap="{{ onButtonTap }}" text="Tap me!"/></StackLayout>`);
class ViewModel extends Observable {
public onButtonTap(args: EventData) {
// need to change whole view to another here (for example, root layout should be DockLayout now)
// but without using Frame.navigate() method
// How do this?
}
}
app.run({
create: () => {
view.bindingContext = new ViewModel();
return view;
}
});

You could use builder for that.
const builder = require('tns-core-modules/ui/builder');
const view = builder.parse(`<StackLayout><Button tap="{{ onButtonTap }}" text="Tap me!"/></StackLayout>`);
view.bindingContext = myBindingContext;
// If you want to replace everything in Page
page.content = view;
Or
// If you want to add it to a parent layout
parentStackLayout.addChild(view);
Edit: If you want to replace your root view itself, then you have to use the _resetRootView method on application module.
const create = () => {
const view = builder.parse(`<StackLayout><Button tap="{{ onButtonTap }}" text="Tap me!"/></StackLayout>`);
view.bindingContext = myBindingContext;
return view;
};
application._resetRootView({
create: create
});

Related

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.

Getting a page view element from a model class

I've got a model class and would like to get access to a ui element.
frameModule.topmost().getViewById("id") or
frameModule.topmost().page.getViewById("id")
don't work.
As I understand what you need, I hope this solution would help.
In XML, assuming that you have UI Element with an ID. You will get it in the controller. For example:
In page.xml:
<Label id="test"/>
In the binding object class in page-view-model.js, create a property that has type of label:
var labelModule = require("ui/label");
var Label = labelModule.Label;
class Someclass extends Observable {
public label: Label;
}
In page.js:
function pageLoaded(args) {
var page = args.object;
var myLabel = page.getViewById("test");
var context = new Someclass();
page.bindingContext = context;
context.label = myLabel;
}
There are many ways to get a reference to the Page object. The documentation is a good reference to learn about them: https://v7.docs.nativescript.org/ui/components/page#page-reference
To answer you question, the usual way to get a reference to the Page object inside a view model class is by using the EventData passed to each event like a tap event when a Button is tapped.
onButtonTap(args: EventData) {
const button = args.object as Button;
const page = button.page;
// ...
}

Require a view and add with a variable (Appcelerator Alloy)

I do not know if I am doing it right or using any good practices, but I am trying to require and add views on my view.
but here I have a view container in my index.xml
<View id="containerDays" layout="vertical" height="Titanium.UI.SIZE">
<Require id="requiredDay" src="NewDay"/>
</View>
<Label id="buttonAddDay" class="button" >Adicionar outro dia</Label>
also on the index.js I have:
$.buttonAddDay.addEventListener("click", addNewDay);
function addNewDay () {
var novoDia = $.getView("NewDay");
$.containerDays.add(novoDia);
}
also I have the view here in another folder on:
/app/views/NewDay.xml
and inside that view is a simple input
<Alloy>
<View class="containerNewDay" layout="vertical" height="Titanium.UI.SIZE">
<TextField id="Day" >write a new day</TextField>
</View>
</Alloy>
so summing everything up,
I am trying to add my $.containerNewDay inside my $.containerDay, but I am not having any success with the getView() or the .open()
I will have to use all the textfield items to send it to a server, how can I set the ids but unfortunately I have no Idea how to do that on appcelerator.
Re the answer above, are you including the .add method from your original block of code? so it should be:
$.buttonAddDay.addEventListener("click", addNewDay);
function addNewDay () {
var novoDia = Alloy.createController("NewDay").getView();
$.containerDays.add(novoDia);
}
or better, would be:
function addNewDay () {
$.containerDays.add(Alloy.createController("NewDay").getView());
}
as this doesn't leave a pointer open to the view.
if you wanted to make it even cleaner:
$.buttonAddDay.addEventListener("click", function addNewDay () {
$.containerDays.add(Alloy.createController("NewDay").getView());
});
or if you want to stick to a "pure" Alloy way, then leave the addNewDay function in place and just add an onClick="addNewDay" handler in the button XML.
Crucially, remember that you'll need your containing view ContainerDays to have a layout of horizontal or your added views will simply sit on-top of each other.
Change:
$.buttonAddDay.addEventListener("click", addNewDay);
function addNewDay () {
var novoDia = $.getView("NewDay");
}
To:
$.buttonAddDay.addEventListener("click", addNewDay);
function addNewDay () {
var novoDia = Alloy.createController("NewDay").getView();
}

Nativescript Observable.propertyChangeEvent

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!

Can the content in Alloy Controller.getView() can be updated?

I am using Alloy 1.3. Can the content in the Controller.getView() can be updated? For example:
In Alloy, if we have view view.xml
<Alloy>
<View>
<Label id="label1"/>
... other content ...
</View>
</Alloy>
In view.js
exports.updateLabel = function(value){
$.label1.text = value;
}
If I have another controller e.g. index.js
var v = Alloy.createController('view').getView();
// assume $.win is the <Window> in index.xml
$.win.add(v);
function updateContent(value){
// This is not work. I want to know how it can be updated
// after the controller turned into a view
v.updateLabel(value);
}
Updating content on object returned from controller.getView() method is fine. In your view.js example you can change label1 text in two different ways:
exports.updateLabel = function(value){
$.label1.text = value;
}
or
exports.updateLabel = function(value){
$.getView('label1').text = value;
}
If you are calling $.getView() without any parameters it will return top level view which has the same id as name of your controller and view.

Resources