I have a component named RedirectUserToMobileAppComponent , I want to share a boolean property from it named enableLoginForm with app.component.
When I execute, I get this error :
enableLoginForm is undefined property on ngAfterViewInit in app.component
this is RedirectUserToMobileAppComponent component:
import {
Component,
ComponentFactoryResolver,
ComponentRef,
Inject,
Input,
OnInit,
Output,
ViewChild,
ViewContainerRef,
} from '#angular/core';
import { Observable, Subscription } from 'rxjs';
import { filter, map, pluck, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '#angular/router';
import { MAT_DIALOG_SCROLL_STRATEGY_FACTORY } from '#angular/material/dialog';
#Component({
selector: 'redirect-user-to-mobile-app',
templateUrl: './redirect-user-to-mobile-app.component.html',
styleUrls: ['./redirect-user-to-mobile-app.component.sass'],
})
export class RedirectUserToMobileAppComponent implements OnInit {
constructor(
) {}
enableLoginForm = false;
ngOnInit(): void {}
OnLogin(): void {
this.enableLoginForm = true;
this.router.navigate(['../login']);
}
}
and this is app.component:
import {
Component,
HostListener,
OnDestroy,
OnInit,
ViewChild,
AfterViewInit,
} from '#angular/core';
import { MatIconRegistry } from '#angular/material/icon';
import { DomSanitizer } from '#angular/platform-browser';
import { FirebaseService } from './services/firebase/firebase.service';
import {
SnakeMessage,
SnakeMessageService,
} from './services/snakeMessage/snakeMessage.service';
import { MatSnackBar } from '#angular/material/snack-bar';
import { Subscription } from 'rxjs';
import { StorageService } from './services/storage/storage.service';
import { AuthService } from './services/auth/auth.service';
import { RedirectUserToMobileAppComponent } from './redirect-user-to-mobile-app/redirect-user-to-mobile-app.component';
#Component({
selector: 'app-component',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
favIcon: HTMLLinkElement = document.querySelector('#appIcon');
private snakeMessageSub: Subscription;
isLoading = true;
isLogged: boolean;
#ViewChild(RedirectUserToMobileAppComponent)
redirectComponent!: RedirectUserToMobileAppComponent;
constructor(
private matIconRegistry: MatIconRegistry,
private firebaseService: FirebaseService,
private snakeMessageService: SnakeMessageService,
private _snackBar: MatSnackBar,
private storageService: StorageService,
private domSanitizer: DomSanitizer,
private authService: AuthService
) {
this.registerCustomIcons();
this.storageService.initDB();
this.storageService.onLoaded$.subscribe((loaded) => {
if (loaded) {
this.isLoading = false;
}
});
this.isLogged = this.authService.isLoggedIn;
}
ngAfterViewInit() {
if (this.redirectComponent.enableLoginForm) {
this._is = this.redirectComponent.enableLoginForm;
}
}
ngOnInit(): void {
this.snakeMessageSub = this.snakeMessageService.messageSub.subscribe(
(snakeMessage: SnakeMessage) => {
this._snackBar.open(snakeMessage.message, snakeMessage.action, {
duration: 3000,
horizontalPosition: 'center',
verticalPosition: 'top',
});
}
);
}
this is my app.component.html
<ng-container *ngIf="!isLoading">
<ng-container *ngIf="isMobileDevice() && !isLogged">
<redirect-user-to-mobile-app> </redirect-user-to-mobile-app>
<router-outlet
*ngIf="enableLoginForm"
></router-outlet>
</ng-container>
<router-outlet *ngIf="!isMobileDevice()"></router-outlet>
This is how you use ViewChild:
#ViewChild('templateId', { static: false }) redirectComponent: RedirectUserToMobileAppComponent;
You should have the templateId set in the template part :
<redirect-user-to-mobile-app #templateId> ... </redirect-user-to-mobile-app>
EDIT: Though I agree with skyBlue, you should use a service to shared data between components
ViewChild returns a reference to the HTML element.
I will quote from angular.io:
Property decorator that configures a view query. The change detector looks for the first element or the directive matching the selector in the view DOM. If the view DOM changes, and a new child matches the selector, the property is updated.
So you cant access it's controller variables with ViewChild.
My suggestion for you is to use a service for passing data.
I have changed the method, I have used #Output() component and it works fine:
this is RedirectUserToMobileAppComponent component after changing the method :
import {
Component,
ComponentFactoryResolver,
ComponentRef,
Inject,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild,
ViewContainerRef,
EventEmitter,
} from '#angular/core';
import { Observable, Subscription } from 'rxjs';
import { filter, map, pluck, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '#angular/router';
import { MAT_DIALOG_SCROLL_STRATEGY_FACTORY } from '#angular/material/dialog';
#Component({
selector: 'yobi-redirect-user-to-mobile-app',
templateUrl: './redirect-user-to-mobile-app.component.html',
styleUrls: ['./redirect-user-to-mobile-app.component.sass'],
})
export class RedirectUserToMobileAppComponent implements OnInit {
constructor(
private router: Router,
private componentFactoryResolver: ComponentFactoryResolver
) {}
#Output() _enableLoginForm: EventEmitter<boolean> = new EventEmitter();
variable: any;
login: boolean;
enableLoginForm = false;
enableSignupForm = false;
ngOnInit(): void {}
sendDataToParent() {
this.enableLoginForm = true;
this._enableLoginForm.emit(this.enableLoginForm);
console.log(this.enableLoginForm + ' From redirect ');
}
I added this to RedirectUserToMobileAppComponent.html:
<a class="login-text" (click)="sendDataToParent()">
Login
</a>
I added this code to app.component :
receiveChildData($event) {
this.enableLoginForm = $event;
}
I added this code to the app.component.html :
<redirect-user-to-mobile-app
(_enableLoginForm)="receiveChildData($event)"
>
</redirect-user-to-mobile-app>
I´m making an angular library with 3 components :
FrameComponent (not public)
ApplicationComponent
SectionComponent
ApplicationComponent extends FrameComponent.
SectionComponent should extend FrameComponent as well BUT ApplicationComponent contains x sectionComponent(s).
So if SectionComponent extends FrameComponent, I got this error:
ERROR Error: "Circular dep for SectionComponent"
Angular 10
SectionComponent_Factory section.component.ts:14
Angular 5
AppComponent_Template app.component.html:2
Angular 20
Angular 17
The exaclty reason, it´s because in SectionComponent, I have a Provider to get parent attributes.
#Component({
selector: 'ui-section',
templateUrl: './section.component.html',
styleUrls: ['./section.component.scss'],
providers: [ provideParent( SectionComponent ) ]
})
export class SectionComponent extends FrameComponent implements OnInit, AfterViewInit {
...
constructor( elRef: ElementRef, #Optional() public parent: Parent){
}
}
if I comment /*#Optional() public parent: Parent*/ The error disappeared.
How can I do SectionComponent extend FrameComponent without circular dep error ?
The solution is #SkipSelf() for SectionComponent
export class SectionComponent extends FrameComponent implements OnInit, AfterViewInit {
...
constructor( elRef: ElementRef, #SkipSelf() #Optional() public parent?: FrameComponent ) {
}
}
I'm unable to pass a function as an argument to a base class from a child class when trying to build using ng build --prod. The build works fine without the --prod flag, which looks to indicate an issue with AOT. The error I get is:
ERROR in : Can't resolve all parameters for AppGridComponent in
/src/app/components/core/shared/app-grid.component.ts: (?, [object
Object])
I found this SO thread which has several different answers for solutions, but I haven't been able to get any to work. It appears that AOT wants to inject this argument as a service and can't resolve (which I don't need since I am passing the function as a value from the child).
Base Class - app-grid.component.ts
import { Component, OnDestroy, OnInit } from '#angular/core';
import { GlobalsService } from '../../../services/globals.service';
#Component({})
export class AppGridComponent implements OnInit, OnDestroy {
constructor(protected loadDataCallBack: any, protected globalsService: GlobalsService) {
}
ngOnInit() {
this.init();
}
// get api data
public init() {
this.loadDataCallBack()
.subscribe(result => result);
}
Child class - rules.component.ts
const loadApiData = function() {
return this.productRuleService.get();
};
#Component({
selector: 'app-rules',
template: `<div class="grid-wrapper">Data here...</div>`
})
export class RulesComponent extends AppGridComponent implements OnInit, OnDestroy {
constructor(protected globalsService: GlobalsService, protected productRuleService: ProductRelationshipRuleService) {
super(loadApiData, globalsService);
}
Any suggestions on how to get this to build would be appreciated.
I was able to get this to work by creating a class that extends Function and then using this class as the provider in the component.
// Base Class - app-grid.component.ts
export class LoadDataCallBack extends Function {
}
#Component({
template: '',
providers: [{provide: LoadDataCallBack, useValue: () => {}}]
})
This ultimately satisfied the compiler and it is able to identify the type to inject into the first argument: loadDataCallBack.
I'm working on an Ionic 2 project and I'm using a component called "offre".
I have got a problem running this component. It works correctly in the home page but not in the views (check this picture to see the error)
The data from firebase shown correctly in home.html but not in the other views!
Any suggestions?
Offre.ts code:
import { Component } from '#angular/core';
import { AngularFireDatabase, FirebaseListObservable } from "angularfire2/database";
import { IonicPage, NavController, NavParams} from 'ionic-angular';
import { NativeStorage } from '#ionic-native/native-storage';
#Component({
selector: 'offre',
templateUrl: 'offre.html'
})
export class OffreComponent {
text: string;
datas: FirebaseListObservable<any>;
user : any ;
constructor(public navCtrl: NavController, public db: AngularFireDatabase, public nativeStorage: NativeStorage) {
this.datas=db.list('/posts');
console.log('Hello OffreComponent Component');
}
I have an image element that I am trying to use ViewChild with:
<img class="postimage" #imagey [src]="">
My controller is this:
import { Component, ViewChild, ElementRef, Renderer } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
/**
* Generated class for the PostpagePage page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
#IonicPage()
#Component({
selector: 'page-postpage',
templateUrl: 'postpage.html',
})
export class PostpagePage {
#ViewChild('imagey') image:ElementRef;
imageHolder;
constructor(public myrenderer: Renderer, public navCtrl: NavController, public navParams: NavParams) {
}
ionViewDidLoad() {
this.imageHolder = this.navParams.get("path");
this.myrenderer.setElementAttribute(this.image.nativeElement, 'src', this.imageHolder);
console.log(JSON.stringify(this.image));
}
pushPage(){
// push another page on to the navigation stack
// causing the nav controller to transition to the new page
// optional data can also be passed to the pushed page.
//this.navCtrl.push(SignUpPage);
}
}
The result of the console message in ionViewDidLoad is:
{"nativeElement":{}}
It doesn't seem to be returning an element.
I had to remove the ion-item that was containing the image - then it worked. It is actually still loggin an empty object to the console.