I have this very basic project:
https://stackblitz.com/edit/angular-rktmgc-ktjk3n?file=index.html
This is the code on: /index.html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div class="mat-app-background basic-container">
<br />
API reference for Angular Material slide-toggle<br /><br />
<select-reset-example>loading</select-reset-example>
<div style="margin-top:30px;">
<div style="color:#f00;margin-bottom:20px;">
Below is what I need to get it work like above (but it doesn't):
</div>
<mat-slide-toggle>Slide me!</mat-slide-toggle>
</div>
</div>
This is the code on: /app/select-reset-example.html
<mat-slide-toggle>Slide me!</mat-slide-toggle>
When loading the component: mat-slide-toggle through: select-reset-example it works, but when loading it directly on the index.html it doesn't.
My question is, how to configure the following /main.ts file in order to render the mat-slide-toggle directly on the index.html?
In case the scope be a problem, maybe is it possible to create a custom component which inherits from that mat-slide-toggle or MatSlideToggleModule class?
If possible, could you fork the project on stackblitz.com and give me the link
with a proper configuration?
import './polyfills';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { NgModule } from '#angular/core';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { MatSlideToggleModule } from '#angular/material';
import { SelectResetExample } from './app/select-reset-example';
import { HttpModule } from '#angular/http';
import { CdkTableModule } from '#angular/cdk/table';
#NgModule({
exports: [
MatSlideToggleModule,
]
})
export class DemoMaterialModule { }
#NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
HttpModule,
DemoMaterialModule,
ReactiveFormsModule,
],
entryComponents: [SelectResetExample],
declarations: [SelectResetExample],
bootstrap: [SelectResetExample],
providers: []
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule);
This is the structure of the project:
Thanks!
Indeed there are several options which could be applied here.
Attempt 1
At first glance we can easily solve your problem by simply importing MatSlideToggle component and adding it to the bootstrap array:
import { MatSlideToggle } from '#angular/material';
#NgModule({
...
bootstrap: [SelectResetExample, MatSlideToggle ]
^^^^^^^^^^^^^^
cool, it was very easy!!!
})
export class AppModule { }
https://stackblitz.com/edit/angular-rktmgc-7h51hh?file=main.ts
Hmm, seems we've broken everything:).
Why?
Angular bootstraps SelectResetExample component. During this process it creates mat-slide-toggle that is part of select-reset-example.html template.
So now our html has two mat-slide-toggle tags.
And then angular bootstraps second component (MatSlideToggle) that will be applied to the first mat-slide-toggle. This way we can see that the first working slider lost text Slide me!.
Attempt 2
Let's change order of our bootstrapping components:
#NgModule({
...
bootstrap: [ MatSlideToggle, SelectResetExample ]
})
export class AppModule { }
https://stackblitz.com/edit/angular-rktmgc-mkm7ry?file=main.ts
The second slider works now but wait... We again lost the text.
The main reason of this is that angular can't process projectable nodes on bootstrapping component.
Attempt 3
Angular gives the opportunity to override bootstrapping process by writing code within ngDoBootstrap method of #NgModule. Let's try...
import { ApplicationRef, ComponentFactoryResolver, Injector, NgModuleRef } from '#angular/core';
#NgModule({
// we replaced bootstrap option with entryComponents
entryComponents: [SelectResetExample, MatSlideToggle],
})
export class AppModule {
constructor(
private resolver: ComponentFactoryResolver,
private ngModule: NgModuleRef<any>) {}
ngDoBootstrap(appRef: ApplicationRef) {
const factory = this.resolver.resolveComponentFactory(MatSlideToggle);
const target = document.querySelector('mat-slide-toggle');
const compRef = factory.create(
Injector.NULL,
[Array.from(target.childNodes)], // passing projectable nodes
target,
this.ngModule);
appRef.attachView(compRef.hostView);
appRef.bootstrap(SelectResetExample);
}
}
https://stackblitz.com/edit/angular-rktmgc-ncyebq?file=index.html
Here i am bootstrapping our components throught custom method ngDoBootstrap. And it works but...
What is this? Do I really need to know this?
I don't think so. There must be some other way out.
Attempt 4
in order not to complicate our live we should follow the design of angular framework. For that it would be better to have one root component. Let's create it:
app.component.ts
#Component({
selector: '.mat-app-background.basic-container',
templateUrl: './app.component.html',
})
export class AppComponent {
}
app.component.html
<br />
API reference for Angular Material slide-toggle<br /><br />
<select-reset-example>loading</select-reset-example>
<div style="margin-top:30px;">
<div style="color:#f00;margin-bottom:20px;">
Below is what I need to get it work like above (but it doesn't):
</div>
<mat-slide-toggle>Slide me!</mat-slide-toggle>
</div>
module
declarations: [SelectResetExample, AppComponent],
bootstrap: [AppComponent],
index.html
<div class="mat-app-background basic-container"></div>
i moved styles to external resouces
Stackblitz Example
Related
While I have been programming since 1991 (yes I am THAT old), this is my first dive into Angular and I am attempting to use WebStorm 2021.2.3 to create a frontend restful interface. The end result, eventually, will be draggable images of desktop/server icons with a menu on each that enables additional actions along with lines representing connections between all of them. All based on a JSON object passed back and forth between my working restful middleware and this Angular frontend.
Of course, I cannot even get the basics working - so there is that.
I currently have the following code:
import {Component} from '#angular/core';
import {CdkDragExit} from '#angular/cdk/drag-drop';
#Component({
selector: 'app-device-component',
templateUrl: './device-component.component.html',
styleUrls: ['./device-component.component.css']
})
export class DeviceComponentComponent {
exited(event: CdkDragExit<string[]>) {
console.log('Exited', event.item.data);
}
}
As well as this for my component frontend
<div class="example-box" cdkDrag (cdkDragExited)="exited($event)">
<mat-card class="example-card">
<mat-card-header>
</mat-card-header>
<img mat-card-image src="https://icons.iconarchive.com/icons/dakirby309/simply-styled/256/Desktop-Windows-icon.png" alt="Windows 10">
<mat-card-content>
Windows 10 Machine
<br /> Location 192.168.1.1
</mat-card-content>
<mat-card-actions>
</mat-card-actions>
</mat-card>
</div>
The main app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import {BrowserAnimationsModule} from '#angular/platform-browser/animations';
import {FormsModule, ReactiveFormsModule} from '#angular/forms';
import {MatNativeDateModule} from '#angular/material/core';
import {HttpClientModule} from '#angular/common/http';
import {AppComponent} from "./app.component";
import {DeviceComponentComponent} from "./device-component/device-component.component";
import {MatCardModule} from "#angular/material/card";
import { DragDropModule } from '#angular/cdk/drag-drop';
import {MatDialogModule} from "#angular/material/dialog";
/* the AppModule class with the #NgModule decorator */
#NgModule({
declarations: [
AppComponent,
DeviceComponentComponent
],
imports: [
BrowserAnimationsModule,
BrowserAnimationsModule,
BrowserModule,
FormsModule,
HttpClientModule,
MatNativeDateModule,
ReactiveFormsModule,
MatCardModule,
MatDialogModule,
DragDropModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
And my crazy advanced main app
<div class="content" role="main">
<app-device-component></app-device-component>
</div>
When I launch this component using npm run start in the Terminal window, I can drag the card around all day long but I never get the console output.
Can somebody PLEASE help me out here?
As you can see in the official doc you need containers (using CdkDropList directive) in order to trigger the cdkDragExited event listener. For your case I would suggest to use the cdkDragMoved event listener.
i am building a mobile app with ionic 5, when I try to call ion-modal that has an *ng-If in it, i would get this error
Can't bind to 'ngIf' since it isn't a known property of 'ion-header'.
The modal is a comment section in comment.page.ts, here is the code for the comment.page.html
<ion-header class="ion-no-border" *ngIf="!isLoading">
<ion-toolbar>
<ion-title class="centerAM">{{no_comm | shortNumber}} comment{{no_comm>1?'s':''}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
....
here is the code for the comment.module.ts
import { NgModule } from '#angular/core';
import { IonicModule } from '#ionic/angular';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { NgxEmojModule } from 'ngx-emoj';
import { CommentPageRoutingModule } from './comment-routing.module';
import { CommentPage } from './comment.page';
import { PipesModule } from '../../pipes/pipes.module';
#NgModule({
imports: [
CommonModule,
NgxEmojModule,
PipesModule,
FormsModule,
IonicModule,
CommentPageRoutingModule
],
schemas: [],
declarations: [ CommentPage]
})
export class CommentPageModule {}
here is the function that calls the modal from the home.page.ts
async CommentModal(i, id) {
const modal = await this.modalCtrl.create({
component: CommentPage,
componentProps:{id},
swipeToClose: true,
cssClass: 'comment-modal'
});
await modal.present();
return
}
If i should add the comment.module.ts in the home.module.ts or the app.module.ts, when the page loads, it will automatically load the modal without the user clicking anything, and i also removed the page from the route and it didn't work, please what am i doing wrong
It is likely that your modules are being lazy-loaded. In that case the docs suggest that you should import your modal module (CommentPageModule) inside of the module, where you require this modal.
In other words, you need:
...
#NgModule({
imports: [
...
CommentPageModule, // <--- here
]
...
export class YourMainModule {}
Otherwise, the modal component doesn't get fully loaded.
Quote from the docs:
When lazy loading a modal, it's important to note that the modal will not be loaded when it is opened, but rather when the module that imports the modal's module is loaded.
I got the same issue on angular 10, Ionic 5, and the issue resolved by just import
modal page in app.modual.ts like
import { ModalPage } from './modules/core/modal/modal/modal.page';
#NgModule({
declarations: [
...
ModalPage
]
just add the component which will be used
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
UpdatePageRoutingModule],
declarations: [UpdatePage, DownloadModalComponent],
entryComponents: [DownloadModalComponent]
})
export class UpdatePageModule {}
in modal in your module declarations and entryComponents like this
where DownloadModalComponent is a component which is being used in a modal of UpdatePage.
You need to make sure that you've imported the BrowserModule to your module.
import {BrowserModule} from '#angular/platform-browser';
....
#NgModule({
imports: [
BrowserModule,
]
....
I'm working through Angular's upgrade guide to learn how to embed AngularJS components in an Angular app. I've created a bare-bones Angular app using the Angular CLI and added a simple AngularJS module as a dependency.
When I run ng serve, the application compiles with no errors. However, at runtime, I get this message in the console:
Error: Trying to get the AngularJS injector before it being set.
What is causing this error, and how can I avoid it? I haven't deviated from the steps detailed in the upgrade guide.
Here's how I'm upgrading my AngularJS component inside my Angular app:
// example.directive.ts
import { Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
// this is the npm module that contains the AngularJS component
import { MyComponent } from '#my-company/module-test';
#Directive({
selector: 'my-upgraded-component'
})
export class ExampleDirective extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
// the .injectionName property is the component's selector
// string; "my-component" in this case.
super(MyComponent.injectionName, elementRef, injector);
}
}
And here's my app.module.ts:
// app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UpgradeModule } from '#angular/upgrade/static';
import { ExampleDirective } from './example.directive';
import { myModuleName } from '#my-company/module-test';
#NgModule({
declarations: [AppComponent, ExampleDirective],
imports: [BrowserModule, AppRoutingModule, UpgradeModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) {}
ngDoBootstrap() {
this.upgrade.bootstrap(document.body, [myModuleName], {
strictDi: true
});
}
}
I'm using Angular 5.2.0.
I faced the same issue, and finally solved it. There are some steps to follow before bootstrap an hybrid Angular/angularjs application.
Install the UpgradeModule npm install #angular/upgrade
Wrap your "CompanyModule" (the module where all your company components are registered) into a new angularjs module (for instance: Ng1Shared). If you not have a module for your company components, it must be created. Than downgrade AppComponent as shown below.
const MyCompanyModule = angular
.module('MyCompanyModule', [])
.component('myComponent', MyComponent)
.name;
const Ng1Shared = angular
.module('Ng1Shared', [MyCompanyModule])
.directive('appRoot', downgradeComponent({ component: AppComponent }))
.name;
Configure AppModule with basic imports (BrowserModule, CommonModule, UpgradeModule). Provide the angularjs' Injector to Angular; declare an "entryComponent" and remove the default bootstrap for AppComponent.
#NgModule({
imports: [BrowserModule, CommonModule, UpgradeModule],
declarations: [AppComponent],
providers: [{provide: '$scope', useExisting: '$rootScope'}], // REQUIRED
entryComponents: [AppComponent], // ADD AN ENTRY COMPONENT
// bootstrap: [AppComponent] MUST BE REMOVED
})
Set angularjs globally with a function provided by UpgradeModule itself and manually bootstrap Angular with DoBootstrap method provided by #angular/core.
export class AppModule implements DoBootstrap {
constructor(private upgrade: UpgradeModule) { }
public ngDoBootstrap(app: any): void {
setAngularJSGlobal(angular);
this.upgrade.bootstrap(document.body, [Ng1Shared], { strictDi: false });
app.bootstrap(AppComponent);
}
}
Create a wrapper directive for every angularjs component and add it to AppModule's declaration array.
#Directive({
selector: 'my-component'
})
export class MyComponentWrapper extends UpgradeComponent {
#Input() title: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('myComponent', elementRef, injector);
}
}
I wrote a simple example available on stackblitz.
For example purposes I added angularjs MyCompanyModule to another angularjs module, called Ng1Module. As you can see also property binding between angularjs and angular component works fine.
I hope it can be useful.
https://github.com/angular/angular/issues/23141#issuecomment-379493753
you cannot directly bootstrap an Angular component that contains
upgraded components before bootstrapping AngularJS. Instead, you can
downgrade AppComponent and let it be bootstrapped as part of the
AngularJS part of the app:
https://stackblitz.com/edit/angular-djb5bu?file=app%2Fapp.module.ts
try to add an entryComponents to your AppModule like this :
...
#NgModule({
declarations: [AppComponent, ExampleDirective],
imports: [BrowserModule, AppRoutingModule, UpgradeModule],
entryComponents: [
AppComponent // Don't forget this!!!
],
providers: [],
// bootstrap: [AppComponent] // Delete or comment this line
})
...
i have created a new angular 4 and material project using cli. The main page contains a mdToolbar and a mdSidenav. So far it looks great but adding some attributes to the html tags like color or mode for the sidenav doesn't have any effect. Webstorm recognize as well that these attributes are not known.
any idea whats missing?
Here is the module which includes the Material modules and my custom Components. In the app.component.html there is the md-toobar and the md-sidenav. The custom component app-sidebar and app-announcement1 shows only the title so far.
The result is a toolbar in color primary, a hidden sidenav and the title of the announcement component.
thanks and best regards,
Max
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { Announcement1Component } from './announcement1/announcement1.component';
import { SidebarComponent } from './sidebar/sidebar.component';
import { MdToolbarModule, MdSidenavModule } from '#angular/material';
#NgModule({
declarations: [
AppComponent,
Announcement1Component,
SidebarComponent
],
imports: [
BrowserModule, MdToolbarModule, MdSidenavModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
<md-toolbar color="warn">
<h1>
{{title}}!!
</h1>
</md-toolbar>
<md-sidenav-container>
<md-sidenav mode="side" opened="true">
<app-sidebar></app-sidebar>
</md-sidenav>
<!-- primary content -->
<app-announcement1></app-announcement1>
</md-sidenav-container>
Just moved over to Angular 2 recently and i am just trying to get my head around pretty much all of it.
I need to build and that just uses stand-alone components, I want to be able to utilise my components as follows.
<body>
<component-one></component-one>
<component-two></component-two>
</body>
I have got as far as getting these components to render out on the page the problem is when one of these component selectors are not present on the current page i get the following console error...
core.umd.js:2838 EXCEPTION: Error in :0:0 caused by: The selector "component-one" did not match any elements
Is there a way to only bootstrap only the relevant components?
Also, the "Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode." console message comes in multiples times depending on how many components i have on the page, which makes me feel like i am missing something.
Module config
// Modules
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
// Components
import { ComponentOne } from './components/componentOne';
import { ComponentTwo } from './components/componentTwo';
#NgModule({
imports: [ BrowserModule ],
declarations: [ ComponentOne, ComponentTwo ],
bootstrap: [ ComponentOne, ComponentTwo],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule {
constructor() {
}
}
You can omit the bootstrap option and implementing ngDoBootstrap() yourself.
And to conditionally bootstrap components, just do a querySelector before calling appRef.bootstrap(SomeComponent); to check whether the component is already on the page.
#NgModule({
imports: [ BrowserModule ],
declarations: [ ComponentOne, ComponentTwo ],
entryComponents: [ ComponentOne, ComponentTwo ]
})
export class AppModule {
ngDoBootstrap(appRef: ApplicationRef) {
if(document.querySelector('component-one')) {
appRef.bootstrap(ComponentOne);
}
if(document.querySelector('component-two')) {
appRef.bootstrap(ComponentTwo);
}
}
}
Note: entryComponents option is required
Finally in your index.html you can omit second tag and angular won't raise error:
<body>
<component-one></component-one>
</body>
Plunker Example
If you don't want to see message Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode. you can just enable prod mode or use the following (Since 2.3.0) which is similar as above (i recommend to use the first solution):
#NgModule({
imports: [ BrowserModule ],
declarations: [ ComponentOne, ComponentTwo ],
entryComponents: [ComponentOne, ComponentTwo]
})
export class AppModule {
constructor(private resolver: ComponentFactoryResolver, private inj: Injector) {}
ngDoBootstrap(appRef: ApplicationRef) {
if(document.querySelector('component-one')) {
const compFactory = this.resolver.resolveComponentFactory(ComponentOne);
let compOneRef = compFactory.create(this.inj, [], 'component-one');
appRef.attachView(compOneRef.hostView);
compOneRef.onDestroy(() => {
appRef.detachView(compOneRef.hostView);
});
}
if(document.querySelector('component-two')) {
const compFactory = this.resolver.resolveComponentFactory(ComponentTwo);
let compTwoRef = compFactory.create(this.inj, [], 'component-one');
appRef.attachView(compTwoRef.hostView);
compTwoRef.onDestroy(() => {
appRef.detachView(compTwoRef.hostView);
});
}
appRef.tick();
}
}
It's just the same that angular does internally when bootstraping component
Plunker Example
See also
https://github.com/angular/angular/issues/11730
Angular2 - Component into dynamicaly created element