I want to make my website modern with some cool features.
For this I used jQuery and self made things. But that's slow and everything I developed is easier in Angular.
So I started to implement the Angular 2 code into my normal website.
The things (app.component, main.ts, app.module) work. I have the angular functionality on my page.
That's the first try.
Now I found out that I can't use second and third components (this ones which are not in bootstrap array) not within the selector.
So the bootstrapped component is the root-element for everything I think.
The next step was to replace the whole body as a component.
For example with angular 1 it was easy: ng-controller="app" and finish.
I can do this with <body> and everything.
Now with Angular 2 I must define a template for the component. else I get this error Uncaught Error: No template specified for component AppComponent
So the questions are...
Is it possible to use the as selector for the app so that I can use components within the app without bootstrapping them?
Or is there a way to use the components flexible?
The main reason is:
I want to define components globally which must not exist on each page. So I can have a component which is called test-component which is only used on test.html and a about-component which is only used on about.html
You understand?
Currently the problem is that I get this error: app.js:116448EXCEPTION: Error in :0:0 caused by: The selector "my-app" did not match any elements and ORIGINAL EXCEPTION: The selector "my-app" did not match any elements
If I define them both it works. But if one component is missing then I get this error.
Current scripts:
main.ts
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
app.component.ts
import { Component } from '#angular/core';
import {Http} from "#angular/http";
#Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1> <a (click)="test()">:)</a>`
})
export class AppComponent {
constructor (private http: Http) {}
name = 'Angular';
test() {
alert('Yay');
return this.http.get('/asd/aaa/test').subscribe((a) => {
console.log('A', a);
}, (b) => {
console.log('B', b);
});
}
}
app.module.ts
import {NgModule} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {AppComponent} from './app.component';
import {JsonpModule, HttpModule} from "#angular/http";
import {FormsModule} from "#angular/forms";
import {TestComponent} from "./test.component";
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
JsonpModule
],
declarations: [
AppComponent,
TestComponent
],
bootstrap: [
AppComponent,
// TestComponent // this shouldnt be a bootstrap because else the component MUST exist on the page!
]
})
export class AppModule {
}
test.component.ts
import { Component } from '#angular/core';
import {Http} from "#angular/http";
#Component({
selector: 'test-app',
template: `<h1>Hello {{name}}</h1> <a (click)="test()">:)</a>`
})
export class TestComponent {
constructor (private http: Http) {}
name = 'Angular !!!!!!!!!!!!';
test() {
alert('yyyyy');
return this.http.get('/asd/aaa/test').subscribe((a) => {
console.log('A', a);
}, (b) => {
console.log('B', b);
});
}
}
As I said if I want to use the body as root element it replaces everything of the body. And this is not for my use case.
It's only a normal website. Multiple HTML (background = PHP) pages.
I could implement this platformBrowserDynamic().bootstrapModule(AppModule); on each page where I need something. Then I could write a module for each page. I think this could solve the problem, too. But it's bad... Having a lot of modules...
Related
Current behavior
I declared those dynamic components as entry components in the module where I also want to render them. With JIT it works fine.
Following structure has the part of my app I want to render them: app -> home (lazy) -> contracts (lazy) -> search.
So I added those components to the module I use for the search component/route. When I'm compiling with AOT, everytime I visit the search route, the app tells me there is no component factory. Of course I searched google and found some results:
I tried adding them to the ANALYZE_FOR_ENTRY_COMPONENTS provider, I tried to import a ModuleWithProviders with .forRoot() in my app.module and I also tried simply importing and declaring my dynamic and all of its dependant components in the root module (app.module). Everything resulting in the same error.
I declare my dynamic components as entry components like so:
#NgModule({
imports: [SharedComponentsModule, FinoSchemaFormsModule, TabGroupModule, FinoInputModule],
declarations: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent, ComparisonDetailSectionComponent],
entryComponents: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent],
exports: [EnergySearchSettingsComponent, DslSearchSettingsComponent, MobileSearchSettingsComponent, ComparisonDetailSectionComponent],
providers: [CategoryMappingProvider]
})
export class ComparisonComponentsModule { }
This module gets imported in the SearchModule, where also my SearchComponent is declared. In this component I want to render those components dynamically using the ComponentFactoryResolver I inject in the SearchComponent.
ngOnInit() {
this.searchSettingsComponent = this.comparisonService.getSearchComponent(); // returns either EnergySearchSettingsComponent, DslSearchSettingsComponent or MobileSearchSettingsComponent
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(searchSettingsComponent);
this.searchSettingsComponent = this.searchContainer.createComponent(componentFactory);
this.searchSettingsComponent.instance.comparisonSettings = comparisonSettings;
this.searchSettingsComponent.instance.onSave.subscribe(settings => this.saveSearchSettings(settings));
}
The SearchComponent is the routing component of the search route, which is a child route of my contract route, which gets lazy loaded. This again is a child route of my home route (also lazy loaded) and this belongs to the main route.
Environment
Angular version: 5.2.4
For Tooling issues:
- Node version: 8.11.3
- Platform: Mac
It must be pretty simple. Just create the SharedModule and put all reusable dynamic component in it, export those components from SharedModule and import this Module in all required. Lazy loaded Modules.
Since it is imported direct to the Module, it must be available while creating the Dynamic Component.
Have you tried updating angular to latest 6.1.10? With version 5 I had issues with lazy loaded modules.
I had a similar task, and it worked fine under 6.1.4.
I've created a working example for you under 7.0.1
I've created both cases
Dynamic component is declared in the module which will create the dynamic component
Dynamic component is declared in a shared module and imported in the lazy-loaded module which will create dynamic components. You can create a shared module for every dynamic component, so you import only one component in a lazy loaded module
I don't feel as though there is enough information in your question to give you the exact answer to the problem you are facing.
I was able to create a solution with, what I feel is a similar setup to yours that you could use to solve your problem or to ask a more pointed question.
TLDR: Full GitHub repo here
I created an app structure as follows:
app/
app.module
app.component
/dynamic-provider --contains component that is dynamically loading other components
--module is lazy loaded by dynamic-one module
dynamic-loader.module
slot.component
/dynamic-one --contains lazy loaded module
--module is lazy loaded by app module
dynamic-one.module
/dynamic-loader --contains a component to be dynamically loaded
dynamic-provider.module
one.component
provider.service
app.module looks as follows
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { AppComponent } from './app.component';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
RouterModule.forRoot([
{ path: 'dynamic-loader', loadChildren: './dynamic-one/dynamic-one.module#DynamicOneModule' },
{ path: '', component: AppComponent }
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
dynamic-one.module looks as follows
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
#NgModule({
imports: [
RouterModule.forChild([
{ path: '', loadChildren: '../dynamic-loader/dynamic-loader.module#DynamicLoaderModule' }
])
]
})
export class DynamicOneModule {
constructor() {
console.log('one');
}
}
dynamic-loader.module looks as follows
import { NgModule } from '#angular/core';
import { RouterModule } from '#angular/router';
import { DynamicProviderModule } from '../dynamic-provider/dynamic-provider.module';
import { SlotComponent } from './slot.component';
#NgModule({
declarations: [ SlotComponent ],
imports: [
DynamicProviderModule,
RouterModule.forChild([
{ path: '', component: SlotComponent }
])
]
})
export class DynamicLoaderModule { }
dynamic-provider.module looks as follows
import { NgModule } from '#angular/core';
import { OneComponent } from './one.component';
import { ProviderService } from './provider.service';
#NgModule({
declarations: [ OneComponent ],
entryComponents: [ OneComponent ],
exports: [ OneComponent ],
providers: [ ProviderService ]
})
export class DynamicProviderModule { }
As you state, your dynamic creation of components is working when the module isn't loaded, so I haven't included that code here(though it is in the repo for completeness). As can be seen here though, the app module lazy loads the dynamic-one module which in turn lazy loads the dynamic-loader module. The dynamic-loader module dynamically creates components from the dynamic-provider module.
How this differs from your implementation is very hard to tell as you have provided only a small amount of information. I hope this helps you find the missing piece you are looking for though!
Creating shared modules allows you to organize and streamline your
code. You can put commonly used directives, pipes, and components into
one module and then import just that module wherever you need it in
other parts of your app.
By re-exporting CommonModule and FormsModule, any other module that imports this SharedModule, gets access to directives like NgIf and NgFor from CommonModule and can bind to component properties with [(ngModel)], a directive in the FormsModule.
EX:
import { CommonModule } from '#angular/common';
import { NgModule } from '#angular/core';
import { ReactiveFormsModule } from '#angular/forms';
import { SharedModule } from '../../shared/shared.module';
import { EntryModalComponent } from './entry-modal.component';
#NgModule({
imports: [
CommonModule,
SharedModule,
ReactiveFormsModule
],
declarations: [ EntryModalComponent ],
entryComponents: [ EntryModalComponent ],
exports: [ EntryModalComponent ]
})
export class EntryModalModule { }
Now you can use this EntryModalComponent for dynamic loading in some other component after importing the module where it's defined.
In the latest versions Angular has updated a lot of staff about lazy loaded modules. And with high probability this trouble is fixed now.
I am working on Electron app with angular 5 for the rendering process,
is there is a way to export the console programmatically?
I need a way to synchronize the logging data to file so, I can review it anytime
without opening electron devtools and save as option, I need it programmatically
I save my own logs, but what if there is a module that logging an error i need to get whole console log history and export it to log file
You can use electron-log, which is a logging module for Electron application. It can be used without Electron.
And you should use ngx-electron.
Firstly, install electron-log
npm install electron-log
Require it in the electron's main process.
const logger = require('electron-log');
Then install ngx-electron
npm install ngx-electron
ngx-electron is exposing a module called NgxElectronModule which needs to be imported in your AppModule.
import {NgModule} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {NgxElectronModule} from 'ngx-electron';
import {AppComponent} from './app.component';
#NgModule({
declarations: [],
imports: [
BrowserModule,
NgxElectronModule
],
bootstrap: [AppComponent]
})
export class AppModule {
}
Once the module has been imported, you can easily use angular DI to ask for ElectronService.
import {Component} from '#angular/core';
import {ElectronService} from 'ngx-electron';
#Component({
selector: 'my-app',
templateUrl: 'app.html'
})
export class AppComponent {
logger
constructor(private _electronService: ElectronService) {
// this should be in init()
if(this._electronService.isElectronApp) {
this.logger = this._electronService.remote.require("electron-log");
}
}
public testLogger() {
this.logger.info('this is a message from angular');
}
}
After that, you should be able to use electron-log in your components, just remember import {ElectronService} from 'ngx-electron';, and this.logger = this._electronService.remote.require("electron-log"); in the components.
I have an app with 2 modules so far: "Shared" and "Web".
All my components in web are working fine. I just created a simple component in the Shared module since I plan to re-use it throughout the app:
import { Component, Input, OnInit } from '#angular/core';
#Component({
selector: 'pb-ds-pluginstable',
templateUrl: './pluginstable.component.html',
styleUrls: ['./pluginstable.component.scss']
})
export class PluginstableComponent implements OnInit {
#Input() name: string;
#Input() version: string;
#Input() link: string;
constructor() {}
ngOnInit() {}
}
This is imported into the shared module, and exported:
...other imports...
import { PluginstableComponent } from './pluginstable/pluginstable.component';
#NgModule({
imports: [
CommonModule,
RouterModule,
NgbModule
],
declarations: [HeaderComponent, FooterComponent, HeaderShadowDirective, PluginstableComponent],
exports: [HeaderComponent, FooterComponent, HeaderShadowDirective, PluginstableComponent]
})
export class SharedModule { }
But when I use this in a component's template in the Web module, like this:
<pb-ds-pluginstable name="foo" version="2.2.2" link="bar"></pb-ds-pluginstable>
I get an error that the component is not recognized:
core.es5.js:1020 ERROR Error: Uncaught (in promise): Error: Template parse errors:
'pb-ds-pluginstable' is not a known element:
1. If 'pb-ds-pluginstable' is an Angular component, then verify that it is part of this module.
2. If 'pb-ds-pluginstable' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component to suppress this message. ("
</section>
[ERROR ->]<pb-ds-pluginstable name="foo" version="2.2.2" link="bar"></pb-ds-pluginstable>
What am I missing here?
You need also to import the SharedModule in that(not parent, not child, exact that module) module, where you want to use it's exported elements
#NgModule({
imports: [
SharedModule,
...
]
})
export class YourModule { }
I asume that you are not importing the shared module into the web module. Most likely you added the import only into the app module
in shared module
you have to export that component.
suppose you have moduleone module which has component componentone you want to add moduleone in shared module moduleshared, which will be added into moduletwo.
I have to export component in moduleshared
that was the problem.
I added my module moduleone in moduleshared but forgot to import, once I added then its available in other component of moduletwo
I'm starting an Angular 2/Material Design project and have run into a problem when trying to use components with <md-tab-group>. I've seen the general structure examples like the following:
<md-tab-group>
<md-tab label="Gallery">
//gallery content here
</md-tab>
<md-tab label="Settings">
//setting content here
</md-tab>
</md-tab-group>
However, I'd like to modularize the functionality of each tab and it's contents into separate Angular 2 components like this:
app.component.html
<md-tab-group>
<app-gallery></app-gallery>
<app-settings></app-settings>
</md-tab-group>
The structure I have at the moment follows the basic Angular 2 component conventions, for example:
gallery.component.ts
import { Component, OnInit } from '#angular/core';
import {Http} from '#angular/http';
#Component({
selector: 'app-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.css']
})
export class GalleryComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
I then use the <md-tab> element inside each component's template like so.
<md-tab label="gallery">
<p>Gallery here...</p>
</md-tab>
Finally, I've made sure everything is imported and declared properly.
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { MaterialModule } from '#angular/material';
import { AppComponent } from './core/app.component';
import 'hammerjs';
import { GalleryComponent } from './gallery/gallery.component';
#NgModule({
declarations: [
AppComponent,
GalleryComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
MaterialModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Is this possible? At the moment I'm suspecting maybe it's not possible to extract the <md-tab> element from the <md-tab-group>. To me it seems cumbersome to include all tabs and their contents in one single html file, not to mention defeating the purpose of Components. If it is possible to do I'd be grateful for any guidance on how to set it up correctly. Thanks in advance!
The components you put into tabs should not depend on the tabs. Therefore, they should not be wrapped in md-tab in their templates. Material tabs are designed as a container you can put anywhere with almost any content you desire. It serves the purpose of single responsibility, not defeats it.
EDIT
As requested, a sample of how it might look like. I assume there are three components: GaleryComponent (gallery with tabs), PicturesGridComponent (displays a grid of pictures) and PictureComponent (displays formatted picture). Templates are simplified to show the structure of nesting components.
GaleryComponent template:
<md-tab-group>
<md-tab label="cats">
<app-pictures-grid [pictures]="catPictures"></app-pictures-grid>
</md-tab>
<md-tab label="dogs">
<app-pictures-grid [pictures]="dogPictures"></app-pictures-grid>
</md-tab>
</md-tab-group>
PictureComponent template:
<app-picture *ngFor="let picture of pictures" [picture]=picture>
If tabs are an enumerable object, you can always try to create them with ngFor (I did not test it).
I have a pages.service.ts
import { Injectable } from '#angular/core';
import { ApiService } from '../../apiService/api.service';
import { Playlists } from '../shared/playlists.model';
#Injectable()
export class PagesService {
private currentPlaylists: Subject<Playlists> = new BehaviorSubject<Playlists>(new Playlists());
constructor(private service: ApiService) {
}
}
This pages service needs another service called ApiService, I inject the way as shown above, it works.
I bootstrap ApiService in main.ts
import { ApiService } from './apiService/api.service';
import { AppComponent } from './app.component';
bootstrap(AppComponent,[
disableDeprecatedForms(),
provideForms(),
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
ApiService
]).catch((err: any) => console.error(err));;
But When I try to inject the PagesService to another component, it gives me error, No Provider for PagesService.
I write that component like this.
import { Component, ViewChild, ElementRef, Input, Output, EventEmitter } from '#angular/core';
import { CORE_DIRECTIVES } from '#angular/common';
import { MODAL_DIRECTVES, BS_VIEW_PROVIDERS } from 'ng2-bootstrap/ng2-bootstrap';
import { ApiService } from '../../apiService/api.service';
import { PagesService } from '../../pages/shared/pages.service';
#Component({
selector: 'assign-playlist-modal',
exportAs: 'assignModal',
providers: [ PagesService ],
directives: [MODAL_DIRECTVES, CORE_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES ],
viewProviders: [BS_VIEW_PROVIDERS],
styleUrls: ['app/channel/shared/assignPlaylist.css'],
templateUrl: 'app/channel/modals/assignPlaylistModal.html'
})
export class AssignPlaylistModalComponent {
constructor(private apiService: ApiService, private pageService: PagesService, fb: FormBuilder) {
}
}
Update: this is my file structure
channel/
--channel.component.ts
--shared/
----assignPlaylist.modal.ts
----addPlaylist.modal.ts
pages/
--shared/
----pages.service.ts
--pages.component.ts
Channel component is the parent of addPlaylist, addPlaylist is the parent of assignPlaylist. This structure will not work
ChannelComponent
|
AddPlaylistComponent
|
AssignPlaylistComponent ----PagesService----ApiService
I found one solution but don't know why I need to do that,
I add the provider 'PagesService' to ChannelComponent, and also the AssignPlaylistComponent, it will work, no errors.
Even this will work
ChannelComponent ----PagesService-------------
|
AddPlaylistComponent
|
AssignPlaylistComponent ----ApiService---------------
However, I just want to use PagesService in AssignPlaylistComponent, so I think it not make sense to import PagesService in channelcomponent.ts and make a providers array in it.
It is a bit strange, but only components can configure dependency injection in Angular (well, and bootstrap()). I.e., only components can specify providers.
Each component in the component tree will get an associated "injector" if the component has a providers array specified. We can think of this like an injector tree, that is (normally much) sparser than the component tree. When a dependency needs to be resolved (by a component OR a service), this injector tree is consulted. The first injector that can satisfy the dependency does so. The injector tree is walked up, toward the root component/injector.
So, in order for your PagesService to inject a ApiService dependency, that ApiService object first has to be registered with an injector. I.e., in a component's providers array. This registration must occur somewhere at or above the component where you want to use/inject the ApiService .
Your service should then be able to inject the registered ApiService object, because it will find it in the injector tree.
See also Angular 2 - Including a provider in a service.