Is it possible to remove some of the padding inside a tabview item?
The TabView has a lot of empty unused space. If I could remove this then my tabs wouldn't get cutoff. This would greatly improve my app since now you have to swipe a bit to the right to see the full title of the last menu item.
The tabview is created in NS-Vue but I don't think that will matter since this is a native issue.
I'm using negative margins to remove padding from TabView items. That was the only solution I found to do this. This approach is needed only for Android, for iOS it works fine.
import { Component, OnInit, HostListener } from '#angular/core';
import { Page } from 'tns-core-modules/ui/page/page';
import { RouterExtensions } from 'nativescript-angular/router';
import { ActivatedRoute } from '#angular/router';
import { TabView } from 'tns-core-modules/ui/tab-view';
import * as platform from 'tns-core-modules/platform';
#Component({
selector: 'app-tabs',
moduleId: module.id,
templateUrl: './tabs.component.html'
})
export class TabsComponent implements OnInit {
tabView: TabView;
constructor(
private page: Page,
private router: RouterExtensions,
private route: ActivatedRoute
) { }
ngOnInit(): void {
this.tabView = this.page.getViewById('tab');
this.router.navigate([
{
outlets: {
homeTab: ['home'],
organizadorTab: ['organizador'],
favoritosTab: ['favoritos'],
categoriasTab: ['categorias'],
perfilTab: ['perfil']
}
}
], { relativeTo: this.route });
}
#HostListener('loaded')
onLoaded() {
if (platform.isAndroid) {
this.tabView.android.tabLayout.setTabTextFontSize(10);
const viewGroup = this.tabView.android.tabLayout.getChildAt(0);
for (let i = 0; i < viewGroup.getChildCount(); i++) {
const view = viewGroup.getChildAt(i);
const layoutParams = view.getLayoutParams();
layoutParams.width = 1;
layoutParams.leftMargin = -20;
layoutParams.rightMargin = -20;
layoutParams.topMargin = -20;
layoutParams.bottomMargin = -20;
view.setLayoutParams(layoutParams);
}
this.tabView.requestLayout();
}
}
}
Related
I implement the project in Nativescript angular. In nativescript 5.0 I was get the focus change listener using the following code. After updating 6.0 I face the issue in android.
Reason: The Search bar extends the androidx widget anyone help how to fix the issue in 6.0.
<SearchBar id="searchBarMall"
[hint]="searchMall" (loaded)="searchBarloaded($event)"
(textChange)="onTextChanged($event)" (clear)="onClear($event)"
(submit)="onSubmit($event)"
textFieldHintColor="gray"></SearchBar>
Typescript
import { SearchBar } from "tns-core-modules/ui/search-bar";
import { Component, OnInit } from "#angular/core";
import { Page, isAndroid } from "tns-core-modules/ui/page";
#Component({
selector: "Home",
moduleId: module.id,
templateUrl: "./home.component.html",
styleUrls: ["./home.component.css"]
})
export class HomeComponent implements OnInit {
searchPhrase: string;
private searchbar: any;
onSearchSubmit(args): void {
let searchBar = <SearchBar>args.object;
console.log("You are searching for " + searchBar.text);
}
constructor(private _page: Page) {
}
ngOnInit(): void {
}
searchBarloaded(args) {
if (isAndroid) {
let self = this;
let searchBar = <SearchBar>args.object;
searchBar.android.setOnQueryTextFocusChangeListener(new android.view.View.OnFocusChangeListener({
onFocusChange: function (v: any, hasFocus: boolean) {
console.log("Focus" + hasFocus);
}
}));
this.searchbar.android.setFocusable(false);
this.searchbar.android.clearFocus();
}
}
ngAfterViewInit() {
this.searchbar = <SearchBar>this._page.getViewById("searchBarMall");
}
}
Use setOnQueryTextFocusChangeListener on nativeView.
this.searchbar.android.setOnQueryTextFocusChangeListener(new android.view.View.OnFocusChangeListener({
onFocusChange:function(v : any , hasFocus:boolean){
if(hasFocus){
...
}else{
...
}
}
}));
I using ui-routing for NG4 (each section is different ui-view).
In some section I use (waypoint.js - imakewebthings.com/waypoints/) with ngZone and I need wait for load all images and videos (in all ui-view in page) to get true page height. Is it posible and if is how can I do this?
Something like addEventListener('load', ...) not working because I have got some pages (each have multiple sections (ui-view)) and it's work only for first open page.
My code:
My page container (similar for evry page)
<ui-view name="header"></ui-view>
<ui-view name="moving-car"></ui-view>
<ui-view name="aaa"></ui-view>
<ui-view name="bbb"></ui-view>
for example moving-car component:
<section class="moving-car" id="moving-car" [ngClass]="{'is-active': isActive}">
<!-- content -->
</section>
TS:
import { Component, OnInit, AfterViewInit, OnDestroy, NgZone,
ChangeDetectorRef } from '#angular/core';
declare var Waypoint: any;
import 'waypoints/lib/noframework.waypoints.js';
#Component({
selector: 'app-avensis-moving-car',
templateUrl: './avensis-moving-car.component.html',
styleUrls: ['./avensis-moving-car.component.scss']
})
export class AvensisMovingCarComponent implements OnInit, OnDestroy, AterViewInit {
constructor(
private $zone: NgZone,
private ref: ChangeDetectorRef
) {}
private waypoint: any;
public isActive: boolean = false;
ngOnInit() {}
ngAfterViewInit(): void {
const activate = () => {
this.isActive = true;
this.ref.detectChanges();
};
this.$zone.runOutsideAngular(
() => {
/* this code below I want run after loaded all image in my
subpage (not only in this component) */
this.waypoint = new Waypoint({
element: document.getElementById('moving-car'),
handler: function (direction) {
activate();
this.destroy();
},
offset: '70%'
});
}
);
}
ngOnDestroy() {
this.waypoint.destroy();
}
}
I modify my ngAfterViewInit function and now it's look like working but I not sure if this is good way to resolve my problem
ngAfterViewInit(): void {
const activate = () => {
this.isActive = true;
this.ref.detectChanges();
};
const images = document.querySelectorAll('img');
let counter = 0;
for (let i = 0; i < images.length; i++) {
images[i].addEventListener('load', () => {
console.log('image loaded');
if (++counter === images.length) {
this.$zone.runOutsideAngular(
() => {
this.waypoint = new Waypoint({
element: document.getElementById('moving-car'),
handler: function (direction) {
activate();
this.destroy();
},
offset: '70%'
});
}
);
}
}, false);
}
}
I have angular2-nativescript application with several pages. structure is similar to groceries example. All pages has very similar action bar content so I don't want to add all action bar and SideDrawer event handlers for each page or add custom component to each page template
Is there any way to have single ActionBar and SideDrawer component for all application pages? Also it is important to have the ability to access this component from all pages and call its methods from page class (so I can tell this component that it should hide/show some content). I want to use some action bar animation in future so my ActionBar shouldn't be recreated each time page changes
I created components that contain side drawer.
import { Component, ViewChild, OnInit,ElementRef,Input } from '#angular/core';
import { TranslateService } from "ng2-translate";
import {Router, NavigationExtras} from "#angular/router";
import * as ApplicationSettings from "application-settings";
import { Page } from "ui/page";
import { RadSideDrawerComponent, SideDrawerType } from "nativescript-pro-ui/sidedrawer/angular";
import application = require("application");
import { Config } from "../../shared/config";
import * as elementRegistryModule from 'nativescript-angular/element-registry';
import { RouterExtensions } from "nativescript-angular/router";
import { AndroidApplication, AndroidActivityBackPressedEventData } from "application";
import { isAndroid } from "platform";
import { UserService } from "../../shared/user/user.service";
import { SideDrawerLocation } from "nativescript-pro-ui/sidedrawer";
#Component({
selector: 'sideDrawer',
templateUrl: 'shared/sideDrawer/sideDrawer.component.html',
styleUrls: ['shared/sideDrawer/sideDrawer.component.css']
})
export class SideDrawerComponent implements OnInit {
#Input () title =""
#Input () backStatus =true;
theme: string="shared/sideDrawer/sideDrawer.component.ar.css";
private drawer: SideDrawerType;
private isEnglish=true;
#ViewChild(RadSideDrawerComponent)
public drawerComponent: RadSideDrawerComponent;
constructor(private us: UserService,private translate: TranslateService,private router:Router, private routerExtensions: RouterExtensions,private _page: Page) { }
ngOnInit() {
this.drawer = this.drawerComponent.sideDrawer;
this.drawer.showOverNavigation=true;
if (ApplicationSettings.getString("language")=="ar") {
this.isEnglish=false;
this.drawer.drawerLocation = SideDrawerLocation.Right;
this.addArabicStyleUrl();
}
if (!isAndroid) {
return;
}
application.android.on(AndroidApplication.activityBackPressedEvent, (data: AndroidActivityBackPressedEventData) => {
data.cancel = true; // prevents default back button behavior
this.back() ;
});
}
back() {
this.routerExtensions.back();
}
public toggleShow(){
this.drawer.showDrawer();
}
add it in every page and customize it using input and output parameters
<sideDrawer [title]="'category' | translate"></sideDrawer>
I am trying to handle the hardware back button in a NativeScript app. I am using NativeScript version 2.3.0 with Angular.
Here is what I have in main.ts file
// this import should be first in order to load some required settings (like globals and reflect-metadata)
import { platformNativeScriptDynamic, NativeScriptModule } from "nativescript-angular/platform";
import { NgModule,Component,enableProdMode } from "#angular/core";
import { AppComponent } from "./app.component";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { routes, navigatableComponents } from "./app.routing";
import { secondComponent } from "./second.component";
import {AndroidApplication} from "application";
#Component({
selector: 'page-navigation-test',
template: `<page-router-outlet></page-router-outlet>`
})
export class PageNavigationApp {
}
#NgModule({
declarations: [AppComponent,PageNavigationApp,secondComponent
// ...navigatableComponents
],
bootstrap: [PageNavigationApp],
providers:[AndroidApplication],
imports: [NativeScriptModule,
NativeScriptRouterModule,
NativeScriptRouterModule.forRoot(routes)
],
})
class AppComponentModule {
constructor(private androidapplication:AndroidApplication){
this.androidapplication.on("activityBackPressed",()=>{
console.log("back pressed");
})
}
}
enableProdMode();
platformNativeScriptDynamic().bootstrapModule(AppComponentModule);
I am importing application with
import {AndroidApplication} from "application";
Then in the constrouctor of appComponentModule I am registering the event for activityBackPressed and just doing a console.log.
This does not work.
What am I missing here?
I'm using NativeScript with Angular as well and this seems to work quite nicely for me:
import { RouterExtensions } from "nativescript-angular";
import * as application from "tns-core-modules/application";
import { AndroidApplication, AndroidActivityBackPressedEventData } from "tns-core-modules/application";
export class HomeComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {
if (application.android) {
application.android.on(AndroidApplication.activityBackPressedEvent, (data: AndroidActivityBackPressedEventData) => {
if (this.router.isActive("/articles", false)) {
data.cancel = true; // prevents default back button behavior
this.logout();
}
});
}
}
}
Note that hooking into the backPressedEvent is a global thingy so you'll need to check the page you're on and act accordingly, per the example above.
import { Component, OnInit } from "#angular/core";
import * as Toast from 'nativescript-toast';
import { Router } from "#angular/router";
import * as application from 'application';
#Component({
moduleId: module.id,
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent {
tries: number = 0;
constructor(
private router: Router
) {
if (application.android) {
application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
if (this.router.url == '/main') {
args.cancel = (this.tries++ > 0) ? false : true;
if (args.cancel) Toast.makeText("Press again to exit", "long").show();
setTimeout(() => {
this.tries = 0;
}, 2000);
}
});
}
}
}
Normally you should have an android activity and declare the backpress function on that activity. Using AndroidApplication only is not enough. Try this code:
import {topmost} from "ui/frame";
import {AndroidApplication} from "application";
let activity = AndroidApplication.startActivity ||
AndroidApplication.foregroundActivity ||
topmost().android.currentActivity ||
topmost().android.activity;
activity.onBackPressed = function() {
// Your implementation
}
You can also take a look at this snippet for example
As far as I know, NativeScript has a built-in support for this but it's not documented at all.
Using onBackPressed callback, you can handle back button behaviour for View components (e.g. Frame, Page, BottomNavigation).
Example:
function pageLoaded(args) {
var page = args.object;
page.onBackPressed = function () {
console.log("Returning true will block back button default behaviour.");
return true;
};
page.bindingContext = homeViewModel;
}
exports.pageLoaded = pageLoaded;
What's tricky here is to find out which view handles back button press in your app. In my case, I used a TabView that contained pages but the TabView itself handled the event instead of current page.
I am using Angular v2 (2.0.0-beta-1) and displaying a simple chart using Google Charts.
import {Component, View} from "angular2/core";
import {Http, HTTP_PROVIDERS} from "angular2/http";
import {OnInit, OnDestroy} from 'angular2/core';
declare let io: any;
declare let google: any;
#Component({
selector:'default',
viewProviders: [HTTP_PROVIDERS]
})
#View({
templateUrl: 'app/default/default.html'
})
export class DefaultPage implements OnInit, OnDestroy {
charttitle: string;
data: any;
options: any;
timerToken: any;
chart: any;
socket: any;
constructor(http: Http) {
}
ngOnInit() {
console.log("onInit");
this.charttitle = "Sample Graph using live data";
this.options = {
title: "My Daily Activities",
is3D: true
};
this.socket = io();
this.socket.on("data_updated", (msg) => {
this.data = new google.visualization.DataTable();
this.data.addColumn('string', 'Task');
this.data.addColumn('number', 'Hours per Day');
this.data.addRows(5);
let data = JSON.parse(msg).activityData;
for (let i = 0; i < data.length; i++) {
let act = data[i];
this.data.setCell(i, 0, act.act);
this.data.setCell(i, 1, act.value);
}
this.chart.draw(this.data, this.options);
});
this.chart = new google.visualization.PieChart(document.getElementById('chart_div'));
google.visualization.events.addListener(this.chart, 'select', this.mySelectHandler);
}
mySelectHandler() {
console.trace();
console.log("Chart: " + this);
//let selectedItem = this.chart.getSelection()[0];
/*if (selectedItem) {
let value = this.data.getValue(selectedItem.row, 0);
console.log("The user selected: " + value);
}*/
}
ngOnDestroy() {
console.log("onDestroy");
this.socket.disconnect();
}
}
The problem I have is the following line.
google.visualization.events.addListener(this.chart, 'select', this.mySelectHandler);
The event is registered is when an element on the pie chart is selected the actual event handler is fired. But all the Angular JS 2 scope variables referenced by this aren't in scope. It's as if the Google Chart visualization library is running in its own scope.
I know that Angular JS has the Angular-Charts directive but we cannot use that as the company wants to use Angular v2 only.
Is there a way I can get the Google Charts API to 'bubble' an event to the event handler running on the scope of the Angular component.
If you want that your mySelectHandler takes part within the Angular2 context / change detection, you could leverage NgZone, as described below. This way, the changes you make in this function will update the view accordingly.
import {NgZone} from 'angular2/core';
export class DefaultPage implements OnInit, OnDestroy {
constructor(private ngZone:NgZone) {
}
ngOnInit()
this.chart = new google.visualization.PieChart(
document.getElementById('chart_div'));
google.visualization.events.addListener(
this.chart, 'select', () => {
this.ngZone.run(() => {
this.mySelectHandler();
});
}
);
}
}
Hope that I correctly understood your question.
Thierry