Create a Dynamic Component in Angular 2 - javascript

Hey I am new to angular 2, I have played with angular 2 for the past week and wondered if it possible to generate a dynamic component with a dynamic template and dynamic styles. This means that the follwoing should be like this
#Component({
// 2a
selector: 'dynamic placeholder',
// 2b
styles: [`dynamic styles`]
// 2c
template: `dynmic template`
})
is it possible to do it in angular 2, I remember that such this is maybe possible in angular 1.
Any Help will be appreciated(Escpecially plunkers with simple code)
This is what I have achieved so far: try using ComponentFactoryResolver:
#NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {
}
#Component({
selector: 'my-app',
template: `
<div>Hello, world!</div>
`
})
export class AppComponent {
}
#NgModule({
declarations: [HomeComponent],
exports: [HomeComponent]
})
export class HomeModule {
}
#Component({
selector: 'home',
template: `
<div>This is home</div>
`
})
export class HomeComponent {
}
#Component({
selector: 'hello-world',
template: `
<div>
Hello, world!, {{name}}
The answer is: {{getAnswer()}}
</div>
`
})
export class HelloWorldComponent implements AfterViewInit {
private name:string = 'You';
constructor(private helloWorldService: HelloWorldService) {
}
ngAfterViewInit(): void {
this.name = 'Me';
}
private getAnswer() {
return this.helloWorldService.giveMeTheAnswer();
}
}
#NgModule({
declarations: [HomeComponent, HelloWorldComponent],
providers: [HelloWorldService],
exports: [HomeComponent]
})
export class HomeModule {
}
#Component({
selector: 'home',
template: `
<button (click)="sayHello()">Say hello</button>
<div>This is home</div>
`
})
export class HomeComponent {
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef) {
}
private sayHello() {
const factory = this.componentFactoryResolver.resolveComponentFactory(HelloWorldComponent);
const ref = this.viewContainerRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
}
Here is a plunker which enables to created dynamic component, I don't know if creating dynamic css is possible,I would be pleased if some can I answer my question:
http://plnkr.co/edit/ZXsIWykqKZi5r75VMtw2?p=preview

With TypeScript and latest version of Angular2 (I believe that feature has been released in 2.4.0) you can create 1 base component and then extend it. All decorators/annotations on properties (#Input/#Output/#ViewChild) will be copied. However, you must specify for each ancestor #Component properties, i.e. you cannot overwrite only selector, but everything. That is RIGHT approach.
Another approach -> use reflect-metadata to update decorators on Component classes (probably that is what you are looking for, as it that case you can overwrite 1 property at time), but be careful to export (i.e. make public) all Components/Directives/Services that are used inside of Component that you want to overwrite. For example, some libraries have multiple Components, and some of them are supposed to be used only internally (i.e. there is no way to import it into your module in normal way... however, you can try providers). If you try to "overwrite", say, css with reflect-metadata and it uses internal components -> angular/typescript will crash, as it cannot resolve "internal" stuff. You can start with this answer: StackOverflow

Related

Using ngx-toastr globally in angular application

I am using the following toastr implementation in my Angular 7 app: https://www.npmjs.com/package/ngx-toastr
I am trying to figure out on how can I make all the toasts append to the body, or other div element which will be in my root app component (I want to keep them displayed even in case where the component from which they are called will be destroyed).
Is there any way to archive it?
As the readme in your link already states, you need to provide your own ToastrContainer.
import {
ToastrModule,
ToastContainerModule // Add this one
} from 'ngx-toastr';
#NgModule({
declarations: [AppComponent],
imports: [
//...
ToastContainerModule // Add this one
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
And add a div to your root component (or anywhere you want the container to be) like this:
#Component({
selector: 'app-root',
template: `
<h1><a (click)="onClick()">Click</a></h1>
<div toastContainer></div> <!-- Add this line here, above should be your router -->
`
})
export class AppComponent implements OnInit {
// Get a reference to the directive
#ViewChild(ToastContainerDirective) toastContainer: ToastContainerDirective;
constructor(private toastrService: ToastrService) {}
ngOnInit() {
// Register the container
this.toastrService.overlayContainer = this.toastContainer;
}
onClick() {
this.toastrService.success('in div');
}
}
Declare the module on your root module (usually app.module.ts)
import { ToastrModule } from 'ngx-toastr';
#NgModule({
imports: [ ToastrModule.forRoot({ ...global options... }) ],
...
})
The toasts can the be called for anywhere (granted you've injected the service in your component) and should be shown where you have defined them to be shown (and that no element covers them).

Angular Drag and Drop component

I try use this component in angular 4
https://github.com/jellyjs/angular2-file-drop
I have something like this
import { Component, OnInit } from '#angular/core';
import { FileDropDirective } from 'angular2-file-drop';
#Component({
selector: 'app-drag-and-drop',
template: `
<div fileDrop
[ngClass]="{'file-is-over': fileIsOver}"
[options]="options"
(fileOver)="fileOver($event)"
(onFileDrop)="onFileDrop($event)">
Drop file here
</div>
`,
directives: [ FileDropDirective ],
styleUrls: ['./drag-and-drop.component.scss']
})
export class DragAndDropComponent implements OnInit {
}
I have error that import FileDropDirective from path has no exported member 'FileDropDirective' and also error in line directives: [ FileDropDirective ],
that Object literal may only specify known properties, and 'directives' does not exist in type 'Component'.
I change
import { FileDropDirective } from 'angular2-file-drop;
to
import { FileDropModule } from 'angular2-file-drop;
and also
directives to providers and working fine

Angular 4 jquery doesn't work

I am trying to use jquery to my Angular 4 app.I had followed all the steps to install jquery on my Angular 4.However jquery still dont work.
I had put the jquery code on the component like this.
home.component.ts
import * as jQuery from 'jquery'
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(db: AngularFireDatabase,public authService: AuthService,public
afAuth: AngularFireAuth,) {
$(document).ready(function(){
$("#showAppinfo").click(function(){
$("#appinfo").slideToggle();
});
});
ngOnInit()
{}
}
And my Html is the following
home.component.html
<h1>This is Home page!!</h1>
<h2 id="showAppinfo">Basic App-info</h2>
<ul class="list-group" id="appinfo">
<li class="list-group-item">Publiser: {{ (appinfo | async)?.Publisher }}</li>
<li class="list-group-item">Publication_Year: {{ (appinfo | async)?.Publication_Year }}</li>
<li class="list-group-item">Version: {{ (appinfo | async)?.Version }}</li>
<li class="list-group-item">Registered Users: {{ (appinfo | async)?.Rusers }}</li>
<li class="list-group-item">Languages: {{ (appinfo | async)?.Language }}(Only)</li>
</ul>
But nothing happens when I click on <h2 id="showAppinfo">Basic App-info</h2>. Can you tell my if I am using the jquery code in the correct place?? The problem is on code or on the jquery instalation??
The basic problem is that you're trying to manipulate your template in the constructor. But when your component constructor executes, #showAppinfo and #appInfo elements don't exist yet because the view has not been built.
Operations that depend on view elements need to be performed at the earliest in the ngAfterViewInit lifecycle hook
export class HomeComponent implements OnInit, OnAfterViewInit
...
ngAfterViewInit(){
// do your template manipulation here
}
You can test this with something like console.log($("#showAppinfo")) and you'll see that it doesn't log any element constructor(), but it does in ngAfterViewInit()
Following the steps that works for me:
Install jquery
npm install jquery
Install ts type
npm install #types/jquery
Add jquery.min.js in your .angular-cli.json:
"scripts": [
"../node_modules/jquery/dist/jquery.min.js"
]
Create a service to JQuery with the Token, Provider and Factory:
import { InjectionToken } from '#angular/core';
import * as $ from 'jquery';
export const JQUERY_TOKEN = new InjectionToken<JQueryStatic>('jquery');
export function jQueryFactory() {
return $;
}
export const JQUERY_PROVIDER = { provide: JQUERY_TOKEN, useFactory: jQueryFactory };
Add the Provider in Module:
#NgModule({
declarations: [
...
],
providers: [
JQUERY_PROVIDER,
...
]
})
Use DI in any component:
constructor(
#Inject(JQUERY_TOKEN) private $: JQueryStatic
)
Be happy :D
this.$('body').css('background-color', 'red')
Easiest and Shortest way possible to use jQuery in Angular 2/4
1st Step
From index.html
my-test-project\src\index.html
Type jQuery cdn below app-root tag.
...
<body>
<app-root></app-root>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</body>
...
2nd Step
my-test-project\src\app\test\test.component.ts
Go to your desired components .ts script.
import { Component, OnInit } from '#angular/core';
// this line will allow you to use jQuery
declare var $: any;
#Component({
...
})
3rd Step
my-test-project\src\app\test\test.component.ts
Test jQuery by logging 'I<3Cats' inside jQuery syntax $(() => { /* content here */ }).
export class TestComponent implements OnInit {
constructor() { }
ngOnInit() {
$(() => {
console.log('hello there!');
});
}
}
You can also use this technique with other javscript libraries. I don't know if this is safe but will sure help you. quite
i had an issue with jquery not working on bootstrap navbar and solved like this...
import { Component, OnInit, AfterViewInit, ElementRef } from '#angular/core';
//declare var $: any; //old code..
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit, AfterViewInit {
constructor(private elem: ElementRef) { }
ngOnInit() {
}
ngAfterViewInit() {
this.elem.nativeElement.querySelectorAll('.navbar-nav>li>a').forEach((el) => {
el.addEventListener('click', () => {
this.elem.nativeElement.querySelector('.navbar-toggler').classList.toggle('collapsed');
this.elem.nativeElement.querySelector('.navbar-collapse').classList.toggle('show');
});
})
//old code...
// $('.navbar-nav>li>a').on('click', function () {
// $('.navbar-collapse').collapse('hide');
// });
}
}
Not sure what slideToggle() is doing, but FYI in Angular if you added
#ref to h2..
you can then add
#ViewChild('ref')
h2El:Element;
in Typescript associated to the HTML.
to do equivalent of $("#showAppinfo")..
If you used this in the HTML
<h2 #ref (click)="handler()">...</h2>
you'd have click handler..
so in Typescript add
handler() {
this.h2El.slideToggle();
}
your onInit method was inside the constructor, try it in the following way
constructor(db: AngularFireDatabase, public authService: AuthService, public afAuth: AngularFireAuth) {
$(document).ready(function () {
$("#showAppinfo").click(function () {
$("#appinfo").slideToggle();
});
});
}
ngOnInit() { }}

Semantic UI with Angular2 - How to set Sidebar settings from jQuery in a component?

I have an Angular2 application and I want to use Semantic UI. However, there are some jQuery configurations like below that I have to run after a component loaded:
$('#app .ui.sidebar')
.sidebar({context:$('#app')})
.sidebar('setting', 'transition', 'overlay')
It is not working by importing the js file in the head of index.html or writing it in a <script> tag inside of a component template. Is there a "typescript way" to do that or how can I use a js file inside of a component?
I found this link about using jQuery in directives, then I created a sidebar directive:
import {Directive, ElementRef, OnDestroy, OnInit, Input} from '#angular/core';
import {HostListener} from "#angular/core/src/metadata/directives";
declare var $: any
#Directive({
selector: '.ui.sidebar'
})
export class SidebarDirective implements OnInit, OnDestroy {
#Input() context: string;
constructor(private el: ElementRef) {
}
public ngOnInit() {
$(this.el.nativeElement)
.sidebar({context: this.context})
.sidebar('setting', 'transition', 'overlay');
}
public ngOnDestroy() {
}
}
Then, I used it in the template:
<div id="app">
<div context="#app" class="ui left vertical menu sidebar"></div>
<div class="pusher"></div>
</div>
I have spent quite some time to get this working although it is rather simple in the end. Hope to save you some time ...
There is no need to create a directive, you can use the jQuery command as you would use with JavaScript (described at https://semantic-ui.com/modules/sidebar.html#/usage). However, "$" has to be declared and the command has to be located in a TypeScript function ("toggle()"):
import {Component} from '#angular/core';
declare var $: any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
toggle() {
$('.ui.sidebar').sidebar('toggle');
}
}
The corresponding section of the template may look like this:
<div class="ui fixed inverted main menu">
<a (click)="toggle()" class="launch icon item">
<i class="content icon"></i>
<p style="padding-left:1em">Menu</p>
</a>
</div>
Don't forget to add jQuery to the scripts section of .angular-cli.json:
"scripts": [
"../node_modules/jquery/dist/jquery.js",
"../node_modules/semantic-ui-css/semantic.min.js"
],
I'm using Semantic UI 2.2.12 which already depends on jQuery 3.2.1. Angular version is 4.4.4 running on node.js 6.11.2.
import { Component, OnInit } from '#angular/core';
declare var $:any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app works!';
ngOnInit(){
$('#app .ui.sidebar')
.sidebar({context:$('#app')})
.sidebar('setting', 'transition', 'overlay') ;
}
}

How are do you handle dependencies for nested components in Angular2?

I'm running into this error:
browser_adapter.js:76 Error: Cannot resolve all parameters for NestedComponent(undefined). Make sure they all have valid type or annotations.
at NoAnnotationError.BaseException [as constructor]
Here's the breakdown:
I have a service
#Injectable()
export class MyService {
doSomething() {
console.log("This is a service, and it's doing stuff");
}
}
That can be injected into components, like this, without issue:
#Component({
selector: 'parent-component',
template: '<p>This works great!</p>',
providers: [MyService]
})
export class ParentComponent {
constructor(private _myService: MyService) {}
ngOnInit() {
this._myService.doSomething();
}
}
I have problems, however, when I try to inject the service into nested components, like this:
#Component({
selector: 'nested-component',
template: "<p>This doesn't work. :(</p>",
providers: [MyService]
})
export class NestedComponent {
constructor(private _myService: MyService) {}
ngOnInit() {
this._myService.doSomething();
}
}
When I try to plug the nested component into the parent component, I get the error up there ^. How can I achieve this.
#Component({
selector: 'parent-component',
template: '<nested-component></nested-component>',
directives: [NestedComponent]
})
export class ParentComponent {
}
For what it's worth, I still run into that error, even when I include MyService in the bootstrap function of my app:
function main() {
return bootstrap(App, [
// These are dependencies of our App
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
MyService,
ELEMENT_PROBE_PROVIDERS // remove in production
])
.catch(err => console.error(err));
}
document.addEventListener('DOMContentLoaded', main);
If you want to share single instance of service,
dont use,
providers: [MyService]
in each component. Look into this example which doesn't use providers:[...] ,shared instance, not providers used, registered into bootstrap
And if you don't want to share,
remove ,
providers: [MyService]
from nested-component.
Look into this example which uses providers:[...],not shared instance, not registered into bootstrap, used with providers in parent only and not in child,

Categories

Resources