Create ViewContainerRef object in angular Utility class - javascript

In one of my scenario I have moved duplicate Angular component code to one Utility class and I am re-using the logic by calling the method in all the components.
In that Utility file logic I am using ViewContainerRef and I am executing javscript code at runtime.
Like below I have multiple components and re-using logic from Utility class in all the components.
Here I am getting below error. After some research I came know that ViewContainerRef can be used only in Component or as a Directive.
Could some one please help how do I instantiate ViewContainerRef object in Utility class.
Error Message.
Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[NgIf -> ViewContainerRef]:
StaticInjectorError(Platform: core)[NgIf -> ViewContainerRef]:
NullInjectorError: No provider for ViewContainerRef!
Component class
import {ViewContainerRef} from '#angular/core';
#Component({
selector: 'dz-page-1',
templateUrl: './component-page-1.component.html',
styleUrls: ['./component-page-1.component.scss']
})
export class ContentInspectPageComponent{
constructor(protected viewContainerRef: ViewContainerRef,){
handleMenuAction(event){
let customUtilHandler = new CustomUtilHandler(this.viewContainerRef);
return customUtilHandler.handleCustomForm();
}
}
}
Utility Class
#Injectable({
providedIn: 'root'
})
export class CustomUtilHandler {
constructor( protected viewContainerRef: ViewContainerRef){
}
handleCustomForm(){
this.viewContainerRef.clear();
const component = this.viewContainerRef.createComponent("customComponent");
component.instance["data"] = inputData;
}
}
Thank you in Advance.

Related

Import js script to Angular from URL

i am building an app with angular but found a problem I don't know how to solve.
I need to import a js script and it has to be imported from the internet. (because these are their rules, so that they can do hotfixes)
The problem is, when I import the script into my index.html I can only use it in js files not in the typescript files. How can i use the libary in my ts files?
I can't install it via npm and i also can't download the file and add it to my project folder.
The libary is the airconsole API (https://developers.airconsole.com/#!/api)
Thanks for your help
it's only add in your component declare var AirConsole to use it, a fool Component
import { Component, VERSION } from '#angular/core';
declare var AirConsole //<--this is the "magic"
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
airConsole=new AirConsole() //<--now you can use airConsole in your component
}
Update Well, generally we can use a service to mannage the airConsole, this allow us that all components that inject the service can use the functions,variables,...
Disclamer: I don't know about "AirConsole", so I imagine control it like we can control others .js like Cordova)
As we need that Angular know when a function is executed in .js, our service can be like
import { Injectable,NgZone } from '#angular/core';
declare var AirConsole;
#Injectable({
providedIn: 'root',
})
export class AirConsoleService implements OnInit {
airConsole=new AirConsole()
message:Subject=new Subject<any>();
constructor(private ngZone: NgZone) { }
ngOnInit(){
this.airconsole.onMessage = (from, data)=>{
this.ngZone.run(() => {
this.message.next({from:from,data:data})
});
})
}
message(device:any,message:any){
this.ngZone.run(() => {
this.airConsole.message(device,message);
});
}
}
So, e.g. you can subscribe to airConsoleService.message

Angular Injectror

I have a few components; I'm using Injector in constructor for encapsulation
import { Component, Injector, OnInit } from '#angular/core';
#Component({
selector: 'app-base',
templateUrl: './base.component.html',
styleUrls: ['./base.component.css'],
})
export class BaseComponent implements OnInit {
some = '';
constructor(injector: Injector) {
this.some = injector.get(this.some);
}
ngOnInit(): void {}
}
I'm using BaseComponent in other Component
import { BaseComponent } from '../base/base.component';
#Component({
selector: 'app-base-state',
templateUrl: './base-state.component.html',
styleUrls: ['./base-state.component.css'],
})
export class BaseStateComponent extends BaseComponent implements OnInit {
constructor(injector: Injector) {
super(injector);
}
ngOnInit(): void {}
}
BaseStateComponent I'm going to use in others component; Question is:
Is there any way, to make injector in BaseComponent or BaseSateComponent Optional;
I have a case, when I need a component, but I don't need an injector of it;
I know about feature
constructor(#Optional(), #Self() etc...);
But truly to say, I can't understand how it work's; I will be grateful if somebody can explain it Decorators;
stack
The problem is that you want to use #Optional #Self to make injector optional. But, #optional #self works well with something that is injectable which you can provide in providers array at some level. In Angular, services are injectable. So you can use #Optional #Self #SkipSelf with services or something injectable
When you use constrocutor(private someService:SomeService), Angular will check whether SomeService is provided at componoent level means in #component's providers, if not, is it provided at Module Level, if not , is it provided at Root level? This way angular checks entire Injector tree.
When you use #Optional, it will not perform this check and so on....
Keep in mind that Injector resolves a token into a dependency.
Answer to your question
You can use optional parameter approach in typescript by simply providing ? mark next to parameter as shown below,
export class BaseComponent implements OnInit {
some = '';
constructor(private injector?: Injector) { // ? to make parameter optional
this.some = injector.get(this.some);
}
ngOnInit(): void {}
}
it also makes sense because you are using OOPs over classes.
Forked Stackblitz (Fixed some other issues also)

Cannot read property of undefined #ViewChild not working Angular 5

I'm having a bit of trouble I cant seem to figure out why my #ViewChild isnt working..
Basically I want to call a function in one component from another component so in my sidebar component I have a function called sendData() and I want to be able to call that from a button click in my header component so what Ive done is..
Sidebar component
import { Component, OnInit, Input, OnChanges, SimpleChanges, AfterViewInit } from '#angular/core';
import * as _ from 'lodash';
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent implements OnInit, OnChanges {
constructor(
private contenfulService: ContentfulService,
private userService: UserService
) { }
ngOnInit() {
}
sendData(){
...do something
}
}
header.component.ts
import { Component, OnInit, Input, ViewChild, EventEmitter } from '#angular/core';
import { UserService } from '../../../user.service';
import { SidebarComponent } from '../sidebar/sidebar.component';
#Component({
selector: 'app-program-header',
templateUrl: './program-header.component.html',
styleUrls: ['./program-header.component.scss']
})
export class ProgramHeaderComponent implements OnInit {
#ViewChild(SidebarComponent) sidebar;
constructor() { }
ngOnInit() {
}
}
header.component.html
<div (click)="sidebar.sendData()"></div>
but it isnt working Im getting this error in the console...
ERROR TypeError: Cannot read property 'sendData' of undefined
I have removed code for brevity, so please let me know if there is more information you need
Im not sure what the problem is?
EDIT
Or if anyone knows another way to call a function from a seperate component let me know
Any help would be appreciated!
Thanks!
ViewChild is expected to be used to get target element from current component's view(template) which matches the selector.
But according to your comment above, it seems there is no app-sidebar placed in your header.component.html, so ViewChild is not able to get a valid element which results in your current error.
The solution should be place app-sidebar at least once.
<app-sidebar></app-sidebar>
<div (click)="sidebar.sendData()"></div>
If header and sidebar components are siblings, you can not pass data between them directly. Take a look at ‘Output’ and ‘Input’ from angular. Alternatively, you can use a Service to pass data between components. Check out services and observables.
<parent>
<app-program-header>
</app-program-header>
<app-sidebar>
</app-sidebar>
</parent>
Just as an addition to this conversation, I have been squirreled off on a couple of occasions chasing what seemed to be the lack of a functioning #ViewChild when the cause was the #ViewChild referencing a component in a module that was not being imported. The "cannot read property of undefined" can become extremely misleading and may not be related to #ViewChild at all; be sure to check your imports first.
In my case - it was the import from "#shared" which has caused this issue. You have to pay attention, that the component you are using the #ViewChild component reference is not in the same module with same shorthand path. If it is, import the #ViewChild component from
'shared/components/sidebar/sidebar.component'
and not from '#shared'

Angular 5: How to get Current queryparams [Routing]

I am having trouble trying to get the queryparams into a component. For now, I just want to console.log(...) it.
I am using the ActivatedRoute from #angular/router for this task.
I am redeveloping a certain platform for work so unfortunately some irrelevant code will have be to substituted with "..."
My Component.ts code:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { RelevantReportService } from './../reportServices/relevantReports.service';
import { ActivatedRoute ,Params, Router } from '#angular/router';
#Component({
selector: 'vr-reports',
templateUrl: './reports.component.html',
styleUrls: ['./reports.component.scss'],
providers: [RelevantReportService],
encapsulation: ViewEncapsulation.None
})
export class ReportsComponent implements OnInit {
reportSections: any;
constructor( private relevantReportService: RelevantReportService,
private router: Router,
private activatedRoute : ActivatedRoute
) { }
ngOnInit() {
...
console.log(this.activatedRoute.queryParams.value.reportName)
// console.log(this.activatedRoute.queryParams._value.reportName)
}
...
}
When I do console.log(this.activatedRoute.queryParams.value.reportName), the console spits out the queryparams (which is exactly what I wanted) HOWEVER it also says
"Property 'value' does not exist on type 'Observable' "
so I believe this not the correct way of tackling it.
It's observable in order to be able to monitor for changes in the params (by subscribing to observable). To get currently passed query params use:
this.activatedRoute.snapshot.queryParams
You could also use ActivatedRouteSnapshot instead of ActivatedRoute
Nothing surprising there!
activatedRoute.queryParams is an observable, and therefore you need to subscribe to it as per https://angular.io/api/router/ActivatedRoute#queryParams
You need to do the following :
ngOnInit() {
this.activatedRoute.queryParams.subscribe(values => {
console.log(values);//Which will print the properties you have passed
});
}
For Angular5 i would say the best option is using URL tree.
Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a serialized tree. UrlTree is a data structure that provides a lot of affordances in dealing with URLs
Details
https://angular.io/api/router/UrlTree

Angular service instantiated twice?

somehow I've got a service that seems to be instantiated twice (its properties are not in sync), by doing the following:
#Component
export class MyComponent extends someOtherComponent {
constructor(service: Service, service2: Service2) {
super(service, service2);
}
isStateEqual() {
return this.service.serviceState === this.service2.service.serviceState;
}
}
#Injectable
export class Service {
serviceState = {}
}
#Injectable
export class Service2 {
constructor(service: Service) {}
}
This is just a very basic example, but that's what it comes down to. To be more precise: We're building our own datepicker and extending NgbDatepicker component which has KeyMapService (this uses NgbDatepickerService) and a local NgbDatepickerService.Here is a link to the component: https://ng-bootstrap.github.io/#/components/datepicker/examples
In our app isStateEqual will always return false (even right after initialising the component) while in the demo you can find in the link above it will always return true (which is how it should be).
Anyone knows why it could be like that?
Thanks in advance.
Regards
Dennis
Application wide singletons must be defined on the bootstrapped module:
platformBrowserDynamic()
.bootstrapModule(AppModule)
#NgModule({
providers: [SingletonService1, SingletonService2],
bootstrap: [AppComponent]
})
export class AppModule {}
source
or by setting providedIn: 'root' on the service decorator:
#Injectable({
providedIn: 'root',
})
export class UserService {
}
source
In my case the service was instantiated twice, because I imported the service using two different approaches (my IDE (VS2019) mishelped me here by automatically generating the incorrect import):
import { Service } from '#mycompany/mymodule'
and
import { Service } from '../../../dist/#mycompany/mymodule/#mycompany-mymodule';
Visual code import my service in this way automatically
import { ConfigService } from 'src/app/services/config.services.js';
And the correct way is:
import { ConfigService } from 'src/app/services/config.services';
It depends where you declare the provider for your services - this determines the scoping. You don't specify in your question where you've done this - try doing it in AppModule, for example.
"service" must be a public propertie of Service2.
#Injectable
export class Service2 {
service:Service
constructor(service: Service)
{
this.service=service;
}
}

Categories