Nativescript #Input string not passing but number does - nativescript

I've this weird behavior where when i use #Input with a string I get "undefined" but if it's a number the data seems to be passed.
custom-progress-bar-component.ts
import { Component, OnInit, Input, ViewChild, ElementRef} from '#angular/core';
#Component({
moduleId: module.id,
selector: 'custom-progress-bar',
templateUrl: './custom-progress-bar.component.html',
styleUrls: ['./custom-progress-bar.component.css']
})
export class CustomProgressBarComponent implements OnInit {
#Input() step: string;
#Input() stepstring : string;
constructor() { }
ngOnInit() {
console.log("step" + this.step); // 1
console.log("stepstring" + this.stepstring); // undefined
}
}
home.html
<StackLayout>
<custom-progress-bar [step]="1" [stepstring]="toto"></custom-progress-bar>
</StackLayout>
what am I misunderstanding ?

By using brackets you're telling Angular the value you're passing in needs to be interpolated. Not entirely sure why numbers work, maybe some sort of implicit processing under the hood since variables can't start with numbers.
You can either remove the brackets or pass in an explicit string to the interpolated attribute:
<StackLayout>
<custom-progress-bar [step]="1" stepstring="toto"></custom-progress-bar>
</StackLayout>
Or with brackets
<StackLayout>
<custom-progress-bar [step]="1" [stepstring]="'toto'"></custom-progress-bar>
</StackLayout>
I prefer bracket-less attributes if it's a primitive value, but up to you.

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.

Diagram created with Nativescript shows no values (=empty diagram)

When creating a diagram in Nativescript using the RadCartesianChart framework, an empty diagram is generated only --> the diagram does not contain anything anything representing the input data.
In a Nativescript mobile app I want to display near-real-time data obtained from a wearable. Data acquisition and data handling are working fine.
However, I did not achieve to create a viewgraph representing the input data. I can modify its axes, but the graph always stays empty.
Do you have any ideas?
My data.component.html (relevant parts are extracted):
<StackLayout class="page page-content">
<RadCartesianChart tkExampleTitle tkToggleNavButton>
<LinearAxis tkCartesianHorizontalAxis></LinearAxis>
<LinearAxis tkCartesianVerticalAxis></LinearAxis>
<LineSeries tkCartesianSeries [items]="collectedData" categoryProperty="time" valueProperty="value">
</LineSeries>
</RadCartesianChart>
</StackLayout>
My data.component.ts (relevant parts extracted):
import { Injectable, Component, OnInit, OnDestroy } from "#angular/core";
import { PlotDatapoint } from "./PlotDatapoint";
#Component({
selector: "Data",
moduleId: module.id,
templateUrl: "./data.component.html"
})
#Injectable()
export class DataComponent implements OnInit, OnDestroy {
constructor() {}
ngOnInit(): void {
this.collectedData = new ObservableArray<PlotDatapoint (this.dataSource);
console.log (collectedData);
}
get dataSource() {
return [
{ time: 1, value: 10},
{ time: 2, value: 10},
{ time: 3, value: 1},
{ time: 4, value: 3}
];
}
}
Actual result --> I get an empty viewgraph.
Expected result -->
The viewgraph should contain lines or points representing the input data.
My assumption: The communication between the data.component.ts and the data.component.html is not working.
UPDATE
I prepared a playgroud link here:
https://play.nativescript.org/?template=play-ng&id=8zpRGL&v=4
In the meantime, I was quite successful in generating quite meaningful viewgraphs. Main problem was the excellent but quite complex Telerik-framework (especially the question "which axis type goes along with which '...Series'").
However, the goal is to display data received from our wearable according to their timestamps. Dropouts should be recognizable:
-- a "CatigoricalAxis" would not be useful.
Instead, the x-axis should be a "LinearAxis" or better "DateTimeContinuousAxis".
My current problem: I did not achieve getting a reasonable x-axis-labelling such as "HH:MM". Instead, the Unix-timestamp ALWAYS persists.

Get the hours and minutes in TimePicker widget in Nativescript + Angular when click in a button

The Nativescript Timepicker documentation says I can use both loaded and timeChange events in order to inteact with the dates.
However I need to get the time just when the user press a submit button. Can you give me some guidelines for achieve this? I've using #ViewChild for getting the Element by reference although seems not to be the right way to do it.
You can use #ViewChild to get a reference to your TimePicker and read the current time values. Still, if you prefer, you could also directly use the native loaded event to achieve the same thing.
Example with using a reference from the loaded event
time-picker.component.html
<StackLayout class="home-panel">
<TimePicker #tp (loaded)="onTimePickerLoaded($event)"
[hour]="currentHour" [minute]="currentMinute"
verticalAlignment="center"></TimePicker>
<Button text="Submit" (tap)="onSubmit()"></Button>
</StackLayout>
time-picker.component.ts
import { Component } from "#angular/core";
import { TimePicker } from "tns-core-modules/ui/time-picker";
#Component({
selector: "time-picker",
moduleId: module.id,
templateUrl: "./time-picker.component.html"
})
export class HomeComponent {
currentHour: number = new Date().getHours();
currentMinute: number = new Date().getMinutes();
timePicker: TimePicker;
onTimePickerLoaded(args) {
this.timePicker = args.object as TimePicker;
}
onSubmit(): void {
console.log("Submit was pressed");
console.log(this.timePicker.time);
console.log(this.timePicker.hour);
console.log(this.timePicker.minute);
}
}
Playground demo for the above scenario.
Another possibility is to get the reference via getViewById and with Page DI.
Example for using Page dependency injection.
time-picker.component.html
<StackLayout class="home-panel">
<TimePicker #tp id="my-time-picker" (loaded)="onTimePickerLoaded($event)"
[hour]="currentHour" [minute]="currentMinute"
verticalAlignment="center"></TimePicker>
<Button text="Submit" (tap)="onSubmit()"></Button>
</StackLayout>
time-picker.component.ts
import { Component } from "#angular/core";
import { TimePicker } from "tns-core-modules/ui/time-picker";
import { Page } from "tns-core-modules/ui/page";
#Component({
selector: "Home",
moduleId: module.id,
templateUrl: "./time-picker.component.html",
})
export class HomeComponent {
currentHour: number = new Date().getHours();
currentMinute: number = new Date().getMinutes();
timePicker: TimePicker;
constructor(private _page: Page) {
this._page.on("loaded", () => {
this.timePicker = this._page.getViewById("my-time-picker");
})
}
onSubmit(): void {
console.log("Submit was pressed");
console.log(this.timePicker.time);
console.log(this.timePicker.hour);
console.log(this.timePicker.minute);
}
}
Playground demo for the above example

Error passing required() Image as a prop

I have a file called data.js where I export an object, which one of its elements is an Image. This is how it looks:
var data = [
{
"id": 1,
"Category": "Category Title",
"Image": require("../images/comingsoon.png"),
"Summary": ""
},
And then in another component I import the data variable and I pass the data.Image to another component as a prop using FlatList.
<FlatList
style={{margin:5}}
numColumns={2}
data={this.state.data}
renderItem={({item}) => item}
/>
item looks like this:
<CategoryItem key={item.id} Image={item.Image} Category={item.Category}/>
And then I use the Image prop inside CategoryItem like so:
<Image source={this.props.item.Image} style={styles.CategoryImage}>
And this works perfectly!...
But I wanted to simply pass Item as a prop to CategoryItem like so:
<CategoryItem key={item.id} item={item}/>
And once inside CategoryItem, I would do:
render(){
const {Category, Image} = this.props.item;
And call the Image simply by doing
<Image source={Image} style={styles.CategoryImage}>
However, when I do that, the app crashes and it says that Image is a number.
After logging what item looks like, I found out that require()ing turns the image into a number, and it should work by simply passing it differently, it crashes. Any thoughts?
The issue is that the local variable Image is shadowing the component named Image.
Your code, simplified, might look something like this:
import { Image } from 'react-native';
class CategoryItem extends React.Component {
render() {
const { Category, Image } = this.props;
return <Image source={Image} />
}
}
Instead of the <Image /> declaration referring to the component as you'd expect, it refers to the Image prop.
This is easy to fix by renaming the variable:
class CategoryItem extends React.Component {
render() {
const { Category, Image: imageSource } = this.props;
return <Image source={imageSource} />
}
}
The reason the error tells you the component is a number is a implementation detail of how the React Native asset imports work. When you import an image with require('path.jpg'), React Native assigns that image a unique numeric ID and returns that instead of a image path descriptor. This is beneficial for performance reasons, so instead of passing a descriptor over the native-javascript bridge for each render, it can pass over a number, which are cheaper to serialize and to marshall.
As a side note, it's a common React practice to declare your component props in lowerCamelCase, so instead of <CategoryItem Image={} /> you would have <CategoryItem image={} />. This is just a convention, but in this case it would have avoided this error.

Angular2 access another components methods without loading its template with #ViewChild

I am trying to access a child components method with #ViewChild, and it works, but I am forced to load its template as well, which I don't want to do, because that also forces that child components OnInit, AfterViewInit and other system functions to run.
I want them to run only when I call this child component in a different scenario, but I want to access this childs custom methods on demand in AppComponent.
So how do I do it?
This is a plunker which depicts the problem: http://plnkr.co/edit/FT6GTJ8mmUnyFxJAPbGV
You can see that dashboards test() function is called, thats what I want, however, its ngOnInit function is also initialized, which I don't want.
template: <h1>AppComponent</h1><my-dashboard></my-dashboard>
I though it was pretty obvious to remove <my-dashboard></my-dashboard> from AppComponent template, to not load dashboards template, but then I get error that dashboard itself is not defined (you can see the error if u remove <my-dashboard></my-dashboard> and run the plunker again) even though I have included it through import statement.
What am I missing?
EDIT-----------------------------------------------------------------------------------------------------------------
So, in the end, you have to use a service to store reusable data/functions to work without a hick up.
Not entirely sure why you would want this, but you can try and use the Component as a provider. Although i fail to see how this can fall under the, "if it looks stupid, but it works, it ain't stupid", rule.
import { Component, OnInit, AfterViewInit, ViewChild } from '#angular/core';
import { DashboardComponent } from './dashboard.component';
#Component({
selector: 'my-app',
template: `
<h1>AppComponent</h1>
`,
providers: [DashboardComponent]
})
export class AppComponent implements OnInit {
constructor(public dashboardComponent: DashboardComponent){}
ngOnInit() {
this.dashboardComponent.test();
}
}
plnkr
Another way I assume, could be to simply initiate a new class instance, this would result in something like this:
import { Component, OnInit, AfterViewInit, ViewChild } from '#angular/core';
import { DashboardComponent } from './dashboard.component';
#Component({
selector: 'my-app',
template: `
<h1>AppComponent</h1>
`
})
export class AppComponent implements OnInit {
public dashboardComponent: DashboardComponent = new DashboardComponent();
constructor(){}
ngOnInit() {
this.dashboardComponent.test();
}
}
plnkr
But again I fail to see why you would want to do something like this. Apparently there is logic in your component which shouldn't be there

Resources