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
Related
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
i've been trying to execute a button that calls a function from a different component.
import ComponentB from './components/ComponentB '
import React, {Component} from 'react';
class ComponentA extends Component {
render() {
return
(
<button onClick={this.handleClick}>click me</button>
);
}
}
export default ComponentA;
this didn't work out. the button wasnt able to call the function. what am i doing wrong?
import React, {
Component
}
from 'react';
class ComponentB extends Component {
constructor() {
this.handleClick = this.handleClick.bind(this);
}
}
handleClick() {
console.log("hi hi hi");
}
}
export
default ComponentB;
You're importing ComponentB, but not using it, that's why is not working. In your case the best way is to implement the handleClick directly to your ComponentA, but this is not what you wanna do. If you're looking to share the same functions with different components, using redux together with react js will get the job done and will provided to the other components as well ;)
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>
I am getting started with NativeScript + Angular 2 and I'd like to implement a component that activates different routes depending on the device orientation. After much search, I was able to get this working but I haven't yet figured out how to get the initial device orientation, which sounds like it should be easier.
Here's my AppComponent code. Look at ngAfterViewInit:
import {Component, OnInit, OnDestroy, AfterViewInit} from "#angular/core";
import {Router} from "#angular/router";
import _application = require('application');
#Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
constructor(private router: Router) {}
ngOnInit() {
_application.on(_application.orientationChangedEvent, this.setOrientation.bind(this));
}
ngAfterViewInit() {
// How do I get the initial orientation here instead of hardcoding to 'portrait'?
this.setOrientation({newValue: 'portrait'})
}
ngOnDestroy() {
_application.off(_application.orientationChangedEvent, this.setOrientation);
}
setOrientation(args) {
if (args.newValue === 'landscape') {
this.router.navigate(['/landscape']);
} else {
this.router.navigate(['/portrait']);
}
}
}
private activity:any = application.android.foregroundActivity;
this.activity.getResources().getConfiguration().orientation;
You could use this plugin. In order to get access to the up-to-date version, you will need to pay a monthly subscription to the developers that maintain ProPlugins.
You can installed the last free version of it using:
npm i nativescript-orientation
This comes with no guaranties that it will work, especially in {N} 6+.
Once installed, you can get the current screen orientation like this:
const orientation = require("nativescript-orientation");
console.log(orientation.getOrientation());
While there is no helper to determine the initial orientation that I know of, you can still get the current screen dimensions. You may import the screen module from the core library.
import { screen } from 'tns-core-modules/platform';
Then on application initialization determine the orientation by determining if the screen height is larger than the width:
this.orientation = screen.mainScreen.heightPixels > screen.mainScreen.widthPixels ? 'portrait' : 'landscape';
Edit: This may be inconsistent and is not fully tested (meaning the screen height might not always be the height of the screen in portrait mode). If this is the case, on page load, you may use the same strategy to measure the main Page element and compare the height and width of the view to determine orientation.
I am writing a test for a React component that uses react-router. I am using Mocha with Chai and Chai-jQuery.
My tests work fine, until I import a component from react-router into a component (e.g. Link). At this point, I get the following error:
ReferenceError: navigator is not defined
at Object.supportsHistory (/Users/nico/google-drive/code/agathos/client/node_modules/history/lib/DOMUtils.js:61:12)
I used to get a similar error with react-bootstrap until I updated to react-bootstrap v0.29.3. I have the most recent version of react-router v2.4.0 (and history v2.1.1). But the problem persists.
The only solution I have found is to change node_modules/history/lib/DOMUtils: navigator into window.navigator. This is a hack though, and not a good solution.
I think the problem is with react-router, but I don't have a solution.
Just in case, here is my test-helper.js.
import jquery from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import jsdom from 'jsdom';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './reducers';
// set up a testing environment to run like a browser in the command line
// create a fake browser and html doc
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.defaultView;
// prevent jquery defaulting to the dom by giving it access to the global.window
const $ = jquery(window);
// build renderComponent function that should render a React class
function renderComponent(ComponentClass, props = {}, appState = {}) {
const componentInstance = TestUtils.renderIntoDocument(
<Provider store={createStore(reducers, appState)}>
<ComponentClass {...props} />
</Provider>
);
// produces html
return $(ReactDOM.findDOMNode(componentInstance));
}
//build a helper for simulating events
// $.fn allows you to add a custom function to your jquery library
$.fn.simulate = function(eventName, value) {
if (value) {
// `this` allows you to access the object appended to
// `val()` is a jquery function that sets the value of selected html element
this.val(value);
}
// the [] are object method selectors, which allow you to access e.g. Simulate.change
TestUtils.Simulate[eventName](this[0]);
};
// set up chai jquery
chaiJquery(chai, chai.util, $);
export {renderComponent, expect};
It seems that react-router assumes navigator is in the global scope.
To resolve this error, you should add navigator to the global scope in your test setup phase:
global.navigator = {
userAgent: 'node.js'
};