I have a problem with angular 2 for two days and I came to get some help from you guys.
The problem is when I try to access a directive child declared within a with ContentChild or ContentChildren from a parent component the Angular doesn't find any child. If I don't use template tag my parent component find the children without a problem.
Plunk
The parent ngAfterContentInit and ngAfterViewInit show that children QueryList has no results
My code is:
import { Component } from '#angular/core';
import {ParentComponent} from './components/parent.component';
import {ChildDirective} from './components/child.directive';
#Component({
selector: 'my-app',
template: `
<parent>
<template>
<button child>button 1 with child directive</button>
<button child>button 2 with child directive</button>
</template>
</parent>
`,
directives: [ParentComponent, ChildDirective]
})
export class AppComponent { }
The Parent Component:
import {Component, ContentChild, ContentChildren, QueryList,
TemplateRef, AfterContentInit, AfterViewInit} from '#angular/core';
import {ChildDirective} from './child.directive';
#Component({
selector: 'parent',
template: `
Parent Template
<br/>
<br/>
<template [ngTemplateOutlet]="template"></template>
`
})
export class ParentComponent implements AfterContentInit, AfterViewInit{
#ContentChild(TemplateRef)
template:TemplateRef;
#ContentChildren(ChildDirective)
children:QueryList<ChildDirective>;
ngAfterContentInit(){
console.log(this.children);
}
ngAfterViewInit(){
console.log(this.children);
}
}
The Child Directive
import {Directive, OnInit, HostListener, EventEmitter, Output} from '#angular/core';
#Directive({
selector: '[child]',
})
export class ChildDirective implements OnInit{
ngOnInit(){
console.log('child started');
}
#Output()
onClick:EventEmitter = new EventEmitter;
#HostListener('click')
onMouseEnter() {
console.log('clicked');
this.onClick.emit('the child has been clicked.');
}
}
Well, that's it, I wait some help cause I don't know what to do to solve this.
Thanks
Related
I'm writing some Octane-style components in Ember v3.13, together with the {{did-insert}} ember-render-modifier. However, when the function tied to did-insert is called, I get TypeError: this is undefined. What am I doing wrong?
Here's my component template:
<div class="cardhost-monaco-container" {{did-insert this.renderEditor}}></div>
And here's the component's JavaScript class:
import Component from '#glimmer/component';
export default class CodeEditor extends Component {
renderEditor(el) {
console.log(this.args.code)
}
}
Methods that are used as actions in templates need to be decorated with #action to have correct this-context:
import Component from '#glimmer/component';
import { action } from '#ember/object';
export default class CodeEditor extends Component {
#action
renderEditor(el) {
console.log(this.args.code)
}
}
The action decorator binds the component context to the method. This is described in more detail in the API docs for action.
I understand there are similar questions based on this. I believe my scenario is little different. I am trying to check a value in native storage, if it is true. I am trying to navigate to the HomePage. By now after reading couple of articles,I know i wont be able to use the NavController a dependency injector.
I have also tried using #ViewChild , but for that I assume I would need to define a variable in the template that I will use. However I am using an html template. I am new to Ionic 2 and Angular 2, so please go little easy on me :-).
Any other way in which i can accomplish that? Please elaborate a bit more if you guys have a solution to this.
This is my code from app.components.ts (below). Currently I have just commented the navCtrl code.
import { Component,ViewChild } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { LoginPage } from '../pages/login/login';
import { NativeStorage } from '#ionic-native/native-storage';
import { NavController } from 'ionic-angular';
import { HomePage } from '../pages/home/home';
#Component({
templateUrl: 'app.html',
})
export class ClassetteApp {
#ViewChild("appNav") nav: NavController;
rootPage:any = LoginPage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, private nativeStorage : NativeStorage) {
platform.ready().then(() => platform.ready().then(() => {
this.nativeStorage.getItem("userId").then(function(data){
this.nav.push(HomePage);
},function(error){
this.nav.push(LoginPage);
})
statusBar.styleDefault();
splashScreen.hide();
})
)}
}
here is my app.html.
<ion-nav #appNav [root]="rootPage"></ion-nav>
check here. You can't inject NavController because any components that are navigation controllers are children of the root component so they aren't available to be injected.
You have the wrong id.
#ViewChild('appNav') nav: NavController
It should be the same as the #appNav id you have given in the html.
Lastly you are using a regular function as a callback. The this points to function object instead of the class. So it cannot find nav. Use Arrow function.
this.nativeStorage.getItem("userId").then((data)=>{
this.nav.push(HomePage);
},(error)=>{
this.nav.push(LoginPage);
})
An update to that answer, I found out we can use this below: Make sure you have the latest Ionic version. 3.3.0. Here you don't have to add a variable to your html template.
import { NavController, Platform } from 'ionic-angular';
import { LoginPage } from '../pages/login/login';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild('appNav') nav: NavController;
loginPage: any = LoginPage;
/* your code */
SomeFunction(){
this.nav.push(loginPage);
}
}
I'm trying to load MoreinfoPage from HomePage upon button click, I get the following error:
Typescript Error Cannot find name 'MoreinfoPage'.
home.html
<button [navPush]="moreinfoPage">More Info</button>
home.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { MoreinfoPage } from 'pages/moreinfo/moreinfo'
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
moreinfoPage: MoreinfoPage;
constructor(public navCtrl: NavController){}
}
I haven't made any changes to the default folder structure. Why can't it find the MoreinfoPage?
Check NavPush.
You need to set page in constructor.
export class HomePage {
moreinfoPage: MoreinfoPage;
constructor(public navCtrl: NavController){
this.moreinfoPage = MoreinfoPage;//here
}
}
Figured it out. The type should be set to any and the page needs to be assigned to the variable.
moreinfoPage: any = MoreinfoPage;
Starting out with angular 2 after spending time with angular 1. Not having unit tested this much as it's more of a side project thing, I'm trying at least start out OK... I started with the example from AngularClass if that makes a difference.
Struggling in app.component.ts already, which contains my navigation bits. Relevant bits of the template here:
<nav class="navbar navbar-light bg-faded">
<div class="container">
<div class="nav navbar-nav">
<a class="navbar-brand" [routerLink]=" ['./'] ">Navbar</a>
<loading class="nav-item nav-link pull-xs-right" [visible]="user === null"></loading>
</div>
</div>
</nav>
<div class="container">
<main>
<router-outlet></router-outlet>
</main>
</div>
<footer>
<hr>
<div class="container">
</div>
</footer>
Component itself does not contain much:
import { Component, ViewEncapsulation } from '#angular/core';
import { AuthService } from './_services';
import { User } from './_models';
import { Loading } from './_components';
#Component({
selector: 'app',
encapsulation: ViewEncapsulation.None,
template: require('./app.component.html'),
styles: [
require('./app.style.css')
]
})
export class App {
user: User;
constructor(private auth: AuthService) {
}
ngOnInit() {
this.auth.getUser().subscribe(user => this.user = user);
}
}
All modules, components and routes are bootstrapped through the App module. Can post if required.
The test I'm having to write for it has me hooking up basically everything from the router (so it seems). First, [routerLink] is not a native attribute of 'a'. Ok, I fix it. Then:
Error in ./App class App - inline template:3:6 caused by: No provider for Router!
So, I hook up router, only to find:
Error in ./App class App - inline template:3:6 caused by: No provider for ActivatedRoute!
Which I added, to find out that:
Error in ./App class App - inline template:3:6 caused by: No provider for LocationStrategy!
By now, the test looks like:
import { inject, TestBed, async } from '#angular/core/testing';
import { AuthService } from './_services';
import { Router, RouterModule, ActivatedRoute } from '#angular/router';
import { AppModule } from './app.module';
// Load the implementations that should be tested
import { App } from './app.component';
import { Loading } from './_components';
describe('App', () => {
// provide our implementations or mocks to the dependency injector
beforeEach(() => TestBed.configureTestingModule({
declarations: [App, Loading],
imports: [RouterModule],
providers: [
{
provide: Router,
useClass: class {
navigate = jasmine.createSpy("navigate");
}
}, {
provide: AuthService,
useClass: class {
getAccount = jasmine.createSpy("getAccount");
isLoggedIn = jasmine.createSpy("isLoggedIn");
}
}, {
provide: ActivatedRoute,
useClass: class { }
}
]
}));
it('should exist', async(() => {
TestBed.compileComponents().then(() => {
const fixture = TestBed.createComponent(App);
// Access the dependency injected component instance
const controller = fixture.componentInstance;
expect(!!controller).toBe(true);
});
}));
});
I'm already mocking the inputs, this seems wrong to me. Am I missing something? Is there a smarter way of loading the whole app on a test, instead of bolting in every single dependency, all the time?
For testing, you should use the RouterTestingModule instead of the RouterModule. If you want to add routes you can use withRoutes
imports: [
RouterTestingModule.withRoutes(Routes) // same any normal route config
]
See Also
Angular 2 unit testing components with routerLink
Second half of this post for an idea of mock the ActivatedRoute. Sometimes you don't want the whole routing facility when unit testing. You can just mock the route.
I see that Ember.js includes files using 'import name from "module-name"' syntax. For example, in app.js:
import Ember from 'ember';
import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers';
import config from './config/environment';
I want to include my JS files using this method. But I don't know how to do that. Where should I put my JS files? Or should I do something else?
Here is an example.
My component:
//file app/components/small-logo.js
import Ember from 'ember';
import Logo from 'library/logo';
export default Ember.Component.extend({
mouseEnter: function() {
var logo = new Logo();
logo.changeColor();
}
});
Logo - is a big class, that has many functions. On mouseEnter event I want change the logo's color. Also, I want to start animation when this component shows, but I don't know how to execute a function at this time, it will be another one question.
Here is my component's template:
//file app/templates/components/small-logo.hbs
{{#link-to 'index'}}
<img alt="Logo" src="img/pixel.gif" class="logo">
{{/link-to}}
Here is my js file with class Logo. I don't know how to include it:
//file app/library/logo.js
var Logo = function() {
...
}
Logo.prototype.changeColor = function() {
...
}
In order to be able to import a function/module you must export it in the file, adding an export default Logo; will make it available for import though.
I also recommend using absolute paths to reference it, in your case applicationName/library/logo.
Another more ember-ish way to do what you are trying to accomplish would be to move the logic into the component.
export default Ember.Component.extend({
mouseEnter: function() {
this.changeColor();
},
changeColor: function() {
// your color change logic, you can access the element via this.$()
}
});
And while we're at it, since you are using the ember-cli you can use ES6 syntax:
const { Component } = Ember;
export default Ember.Component.extend({
mouseEnter() {
this.changeColor();
},
changeColor() {
// your color change logic, you can access the element via this.$()
}
});