Angular 2 How to add click event with empty template? - dom

I have the following Component:
import {Component, ElementRef} from 'angular2/core';
#Component({
selector: 'test',
template: ''
})
export class TestComponent {
el;
constructor(public elementRef: ElementRef) {
this.el = elementRef.nativeElement;
this.renderer = new THREE.WebGLRenderer();
this.el.appendChild(this.renderer.domElement);
}
onClick() {
console.log(this);
}
}
Since the template is empty, how can I add a click event to the component? Note that
this.el.addEventListener('click', this.onClick, false);
won't work because the click event is added to this.el, instead of the component itself (console log returns < test >< /test > rather than TestComponent itself).

You can use host-listener:
#Component({
selector: 'test',
template: ''
host: {'(click)':'clickHandler($event)'}
})
export class TestComponent {
// #HostListener('click') // alternative way to the decorator argument
clickHandler() {
doSomething();
}
}

Related

setting global variable in ipcRenderer.on eventhandler running ionic on electron does not work

This is my code in home.ts to receive values from main-process
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
declare var electron : any;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
arguments: any="ping";
constructor(public navCtrl: NavController) {
}
ionViewDidLoad() {
electron.ipcRenderer.send("test_channel","ping");
electron.ipcRenderer.on("test_channel", function (err,arg) {
this.arguments=arg; // receive new value "pong" from main.js
console.log("Message received from electron: "+arg); // works fine
});
console.log("Message received from electron: "+this.arguments); //does not work, still default value
};
}
This is added in my code in main.js, and it works to receive the event from render-process
var ipcMain = require('electron').ipcMain;
mainWindow.webContents.openDevTools();
ipcMain.on("test_channel",function(err,arg){
console.log(err);
console.log("Received message: "+arg);
global.sharedObj = {prop1: arg};
console.log("Sending message back!");
// Send message back!
mainWindow.webContents.send("test_channel",arg+'yeah');
})
This is added in my index.html, to make it run for ionic
<script>
const electron = require('electron');
</script>
First of all don't duplicate the message name. and in the main.js , send the event request to the renderer process using event.sender.send as shown below:-
ipcMain.on('message_name', (event,args) => {
event.sender.send('message_name_2', args)
});

Angular 2 | How to handle input type file in FormControl?

Good day,
How can i handle input type file in formControl? im using reactive form but when i get the value of my form it returns null value on my <input type="file">??
You need to write your own FileInputValueAccessor. Here is the plunker and the code:
#Directive({
selector: 'input[type=file]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: FileValueAccessorDirective,
multi: true
}
]
})
export class FileValueAccessorDirective implements ControlValueAccessor {
onChange;
#HostListener('change', ['$event.target.value']) _handleInput(event) {
this.onChange(event);
}
constructor(private element: ElementRef, private render: Renderer2) { }
writeValue(value: any) {
const normalizedValue = value == null ? '' : value;
this.render.setProperty(this.element.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn) { this.onChange = fn; }
registerOnTouched(fn: any) { }
nOnDestroy() { }
}
And then you will be able to get updates like this:
#Component({
moduleId: module.id,
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>
<h3>File path is: {{path}}</h3>
<input type="file" [formControl]="ctrl">
`
})
export class AppComponent {
name = 'Angular';
path = '';
ctrl = new FormControl('');
ngOnInit() {
this.ctrl.valueChanges.subscribe((v) => {
this.path = v;
});
}
}
there is no way to handle this in angular form-control.
we can provide some hack to make this work if you want to upload the image.
just add the <input type=file> as form control on which user can add the file and acter grabbing the file we can change it to the base64code and then assign that value to the hidden field of our main form.
then we can store the image file in that way.
else you can go for the ng-file-upload moduleng file upload

Angular 2: Custom Input Component with Validation (reactive/model driven approach)

I have to create a component with custom input element (and more elements inside the component, but its not the problem and not part of the example here) with reactive / model driven approach and validation inside and outside the component.
I already created the component, it works fine, my problem is that both formControl's (inside child and parent) are not in sync when it comes to validation or states like touched. For example if you type in a string with more then 10 characters, the form control inside the form is stil valid.
Plunkr
//our root app component
import {Component, Input} from '#angular/core'
import {
FormControl,
FormGroup,
ControlValueAccessor,
NG_VALUE_ACCESSOR,
Validators
} from '#angular/forms';
#Component({
selector: 'my-child',
template: `
<h1>Child</h1>
<input [formControl]="childControl">
`,
providers: [
{provide: NG_VALUE_ACCESSOR, useExisting: Child, multi: true}
]
})
export class Child implements ControlValueAccessor {
childControl = new FormControl('', Validators.maxLength(10));
writeValue(value: any) {
this.childControl.setValue(value);
}
registerOnChange(fn: (value: any) => void) {
this.childControl.valueChanges.subscribe(fn);
}
registerOnTouched() {}
}
#Component({
selector: 'my-app',
template: `
<div>
<h4>Hello {{name}}</h4>
<form [formGroup]="form" (ngSubmit)="sayHello()">
<my-child formControlName="username"></my-child>
<button type="submit">Register</button>
</form>
{{form.value | json }}
</div>
`
})
export class App {
form = new FormGroup({
username: new FormControl('username', Validators.required)
});
constructor() {
this.name = 'Angular2';
}
sayHello() {
console.log(this.form.controls['username'])
}
}
I have no clue how to solve this problem in a proper way
There exists a Validator interface from Angular to forward the validation to the parent. You need to use it and provide NG_VALIDATORS to the component decorator's providers array with a forwardRef:
{
provide: NG_VALIDATORS,
useExisting: CustomInputComponent,
multi: true
}
your component needs to implement the Validator interface:
class CustomInputComponent implements ControlValueAccessor, Validator,...
and you have to provide implementations of the Validator interfaces' methods, at least of the validate method:
validate(control: AbstractControl): ValidationErrors | null {
return this.formGroup.controls['anyControl'].invalid ? { 'anyControlInvalid': true } : null;
}
there is also another to sync the validators when their inputs change:
registerOnValidatorChange(fn: () => void): void {
this.validatorChange = fn;
}

Ionic 2 Slides Component - How to Access Swiper API

Using ion-slides component (4 slides) on app welcome page/slides. I need ability for user to skip to last slide. Docs say ion-slides implementation of Swiper API. I need to access methods like: mySwiper.slideTo(index, speed, runCallbacks);
Tips on how to implement?
You can pass a function within the options property.
Original answer.
#Component({
selector: 'my-component',
template: '<ion-slides [options]="options">
<ion-slide>Slide1</ion-slide>
<ion-slide>Slide2</ion-slide>
</ion-slides>',
directive: [IONIC_DIRECTIVES]
})
export class MyComponent {
public slider: any;
constructor() {
this.options = {
onlyExternal: false,
onInit: (slides: any) =>
this.slider = slides
}
}
click() {
this.slider.sliderNext(true, 250);
}
}
For further options have a look at the swiper api.
If you are looking for a simple solution without custom directives, you can try this
constructor(
private _app: IonicApp
){}
ngAfterViewInit() {
this._slider = this._app.getComponent('my-slider');
}
goToSlide(slideIndex){
this.slider.slider.slideTo(slideIndex);
}
You can make a service with your Swiper
#Injectable()
export class HomeSwiper {
swiper = null;
initSwiper(selector) {
this.swiper = new Swiper(selector, {
pagination: '.home-swiper-pagination',
speed: 400,
spaceBetween: 100,
nextButton: '.swiper-button-next',
prevButton: '.swiper-button-prev'
});
}
goTo(index) {
this.swiper.slideTo(index);
}
}
And use it into your #Page
#Page({
templateUrl: 'build/pages/home/home.html',
providers: [HomeSwiper]
})
export class Home {
constructor(private swiperService: HomeSwiper) {
this.swiperService.initSwiper('.home-modal-swiper-container');
}
skipSlides() {
this.swiperService.goTo(indexOfTheLastSlide);
}
}

how to access super component class variable into sub component Class?

How i access super component class variable into sub component in Angular2?
super Component Article.ts
#Component({
selector: 'article'
})
#View({
templateUrl: './components/article/article.html?v=<%= VERSION %>',
styleUrls : ['./components/article/article.css'],
directives: [CORE_DIRECTIVES, AmCard, NgFor]
})
export class Article{
articleArr : Array;
constructor() {
this.articleArr = new Array();
}
articleSubmit(articleSubject, articleName, articleUrl)
{
this.articleArr.push({title: articleSubject.value, user : articleName.value, url : articleUrl.value});
}
}
super Component article.html
<div *ng-for="#item of articleArr">
<am-card card-title="{{item.title}}" card-link="{{item.url}}" card-author="{{item.user}}"></am-card>
</div>
sub component amcard.ts
#Component({
selector: 'am-card',
properties : ['cardTitle', 'cardLink', 'cardAuthor']
})
#View({
templateUrl: './components/card/card.html?v=<%= VERSION %>',
styleUrls : ['./components/card/card.css'],
directives: [CORE_DIRECTIVES]
})
export class AmCard {
constructor() {
}
}
sub Component amcard.html
<div class="card">
...
</div>
So my question is how to access articleArr of Article Class in AmCard class ?
advanced
Thanks for helping me.
You can inject a parent component into a child using angular2 Dependency Injection. Use #Inject parameter decorator and forwardRef to do it (forwardRef allows us to refer to Article which wasn't yet defined). So your AmCard component will look like (see this plunker):
#Component({
selector: 'am-card',
template: `
<span>{{ articleLength }} - {{ cardTitle }}<span>
`
})
export class AmCard {
#Input() cardTitle: string;
#Input() cardLink: string;
#Input() cardAuthor: string;
constructor(#Inject(forwardRef(() => Article)) article: Article) {
// here you have the parent element - `article`
// you can do whatever you want with it
this.articleLength = article.articleArr.length;
setTimeout(() => {
article.articleSubmit({ value: Math.random() }, {}, {});
}, 1000)
}
}
But, IMHO, it's a bad pattern. If possible, it's much better to use output property (event binding) to pass message to a parent component and in a parent component handle that message. In your case it would look like (see this plunker):
#Component({ /* ... component config */})
class AmCard {
// ... input properties
#Output() callSubmit = new EventEmitter();
constructor() {
setTimeout(() => {
// send message to a parent component (Article)
this.callSubmit.next({ value: Math.random() });
}, 1000)
}
}
#Component({
// ... component config
template: `
<h3>Article array:</h3>
<div *ng-for="#item of articleArr">
<am-card
[card-title]="item.title"
[card-link]="item.url"
[card-author]="item.user"
`/* handle message from AmCard component */+`
(call-submit)=" articleSubmit($event, {}, {}) "
></am-card>
</div>
`
})
class Article{
// ... properties and constructor
articleSubmit(aa, an, au) {
this.articleArr.push({ title: as.value, user: an.value, url: au.value });
}
}