angular component load multiple time when ngModel is added in template - javascript

I'm trying to create angular form.
I have referenced the form in that angular module.
The problem is when i add ngModel in input textbox, the angular component loads multiple times in the page.
Following is the module code
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { Routes, RouterModule } from '#angular/router';
import { HeaderProfileComponent } from './header-profile.component';
import { LayoutModule } from '../../../../layouts/layout.module';
import { DefaultComponent } from '../../default.component';
import { FormsModule } from "#angular/forms";
import { BrowserModule } from '#angular/platform-browser';
import { UserService } from '../../../../../_services/user.service';
const routes: Routes = [
{
"path": "",
"component": DefaultComponent,
"children": [
{
"path": "",
"component": HeaderProfileComponent
}
]
}
];
#NgModule({
imports: [
FormsModule,CommonModule, RouterModule.forChild(routes), LayoutModule
], exports: [
RouterModule
], declarations: [
HeaderProfileComponent
],providers:[
UserService
]
})
export class HeaderProfileModule {
}
following is the component code
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { Helpers } from '../../../../../helpers';
import { UserService } from '../../../../../_services/user.service';
import { ApplicationUser } from '../../../../../Entities/UserDetail';
#Component({
selector: ".m-grid__item.m-grid__item--fluid.m-wrapper",
templateUrl: "./header-profile.component.html",
encapsulation: ViewEncapsulation.None,
})
export class HeaderProfileComponent implements OnInit {
appUser: ApplicationUser = null;
newUser: ApplicationUser = null;
constructor(private _userService: UserService) {
this.appUser = new ApplicationUser();
this.newUser = new ApplicationUser();
}
ngOnInit() {
this._userService.getCurrentUser()
.subscribe((data: ApplicationUser) => {
this.appUser = data;
}, (err: Response) => {
});
}
createUser(){
debugger;
}
}
in angular component HTML:
<form (ngSubmit)="createUser()" class="m-form m-form--fit m-form--label-align-right">
<input class="form-control m-input" type="password" [(ngModel)]="newUser.FirstName" >
</form>
Wen i add only without mgNodel, it works fine.

The behavior was due to theme i was using.
Theme handled the exceptions and never throw in console so angular tried to load the component even after exceptions caused this behavior.
Removed the code block and let angular throw in browser shows input should contain name attribute with ngModel , adding attribute -> Fixed

Related

angular form.io form-builder custom component get reference

I'm developing in ANGULAR 10
I use form.io form-builder to render components
I add custom component that drow grid (ag-grid) According to this link add custom Components with Angular Elements
every think work well
I drag and drop the table several times to draw multiple tables.
I enclose a photo of how the form looks like after the drag and drop action into the form
The problem .
when form.io render my custom component that draw the ag-grid , i need to get the connection string and the sql statement from the component definition (json).
I do not know how to get this information from my custom component at the time it's be rendering. Without this information, I do not know generate the column names and row's content.
This is my project
builder component contain the formio tag
aggrid component is my custom component for display ag grid
formio.ts
import { Injector } from '#angular/core';
import { FormioCustomComponentInfo, registerCustomFormioComponent } from 'angular-formio';
import { AggridWrapperComponent } from './aggrid-wrapper.component';
export function minimalEditForm() {
return {
components: [
{ key: 'type', type: 'hidden' },
{
weight: 10,
type: 'textarea',
input: true,
key: 'key',
label: 'sql statement',
tooltip: 'please enter your sql statement',
}
],
};
}
const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
type: 'sqlaggrid',
selector: 'sql-grid',
editForm: minimalEditForm,
title: 'sql-grid',
group: 'basic',
icon: 'fa fa-star',
};
export function registerAgGridComponent(injector: Injector) {
registerCustomFormioComponent(COMPONENT_OPTIONS, AggridWrapperComponent, injector);
}
aggrid-wrapper.component.html
<ag-grid-angular style="width: 500px; height: 500px;" class="ag-theme-alpine"
[gridOptions]="gridOptions">
</ag-grid-angular>
AggridWrapperComponent
import { Component, EventEmitter, Input, ElementRef, Output ,ViewChild} from '#angular/core';
import { FormioCustomComponent } from 'angular-formio';
import { Grid, GridOptions } from "ag-grid";
#Component({
selector: 'app-aggrid-wrapper',
templateUrl: './aggrid-wrapper.component.html',
styleUrls: ['./aggrid-wrapper.component.css']
})
export class AggridWrapperComponent implements FormioCustomComponent<number> {
#Input()
value: number; //number is missing (null)
#ViewChild('aggrid') input;
#Output()
valueChange = new EventEmitter<number>();
#Input()
disabled: boolean;
private _value: number;
public gridOptions: GridOptions;
constructor(private elRef: ElementRef) {
this.gridOptions = <GridOptions>{
columnDefs: this.createColumnsDefs(),
onGridReady: (params) => {
this.gridOptions.api.setRowData(this.executeStatement());
}
}
}
createColumnsDefs() {
/* return the grid columns */
/*If I could get the field definition containing the SQL statement then I could return the columns of the grid */
return ???;
}
executeStatement(){
/* get the grid rows */
/*If I could get the field definition containing the SQL statement then I could execute the statement and back the rows */
return ??? */
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule ,Injector } from '#angular/core';
import { RouterModule } from '#angular/router';
import {AppConfig} from './formio-config';
//import { AppRoutingModule } from './app-routing.module'
import { FormioModule } from 'angular-formio';
import { AppComponent } from './app.component';
import { BuilderComponent } from './builder/builder.component';
import { FormsModule } from '#angular/forms';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { RatingWrapperComponent } from './rating-wrapper/rating-wrapper.component';
import { registerAgGridComponent} from './aggrid-wrapper/formio'
import { AggridWrapperComponent } from './aggrid-wrapper/aggrid-wrapper.component';
import { AgGridModule } from 'ag-grid-angular';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
BuilderComponent,
AggridWrapperComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
FormioModule,
BrowserAnimationsModule,
NgbModule,
AgGridModule.withComponents([])
],
exports: [RouterModule],
providers: [ ],
bootstrap: [AppComponent]
})
export class AppModule{
constructor(injector: Injector) {
registerAgGridComponent(injector)
}
}
I am missing the reference to the component definition
any idea ?
have you tried getting the object by its key?
component = form.getComponent('keyName')
The problem is:
from Where did the form object come from.
I drag and drop the same component several times, and I do not know within the component (AggridWrapperComponent) which instance I am.
Hey I was able to achieve by creating a Subject and pushing the data at the OnChange of App Component and then retrieving where ever I want to.

Angular 6 when component is routed to the d3/event-drops data points don't display in Safari

I have created a test app to replicate the issue I am experiencing.
codebase: https://github.com/mohammadfarooqi/event-drops-d3-test-app
sample demo deployed (view in safari to see the issue): https://mohammadfarooqi.github.io/event-drops-d3-test-app/
I am using event-drops timeline to display some 'drops' (points on timeline). I have created the timeline in a component called 'comp-a'. I have also created a component called 'comp-b' that uses 'comp-a'. I also have a button in comp-b that routes the user to 'comp-a'.
The issue that I am having is that, in comp-b the event-drops timeline displays with no issues including the 'drops' (points on timeline). However, when we click on the button to go to 'comp-a' from 'comp-b', the comp-a component renders however, the 'drops' on the timeline do not show in Safari (mobile/tablet) however all works in Chrome.
comp-a.component.html
<div id="eventdrops-demo"></div>
comp-a.component.ts
import { Component, OnInit } from '#angular/core';
import * as d3 from 'd3v4';
import eventDrops from 'event-drops';
#Component({
selector: 'app-comp-a',
templateUrl: './comp-a.component.html',
styleUrls: ['./comp-a.component.css']
})
export class CompAComponent implements OnInit {
constructor() { }
ngOnInit() {
const chart = eventDrops({
d3,
drop: {
date: d => d.date
}
});
const repositoriesData = [
{
name: 'admin-on-rest',
data: [{ date: new Date('2018/01/15 14:21:31') } ],
},
{
name: 'event-drops',
data: [{ date: new Date('2018/01/15 13:24:57') } ],
},
{
name: 'sedy',
data: [{ date: new Date('2018/01/15 13:25:12') } ],
},
];
d3
.select('#eventdrops-demo')
.data([repositoriesData])
.call(chart);
}
}
comp-b.component.html
<p>
comp-b works!
</p>
<app-comp-a></app-comp-a>
<button (click)="goto()">test</button>
comp-b.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-comp-b',
templateUrl: './comp-b.component.html',
styleUrls: ['./comp-b.component.css']
})
export class CompBComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
}
goto() {
this.router.navigate(['a']);
}
}
app-routing.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { RouterModule, Routes } from '#angular/router';
import { CompAComponent } from './components/comp-a/comp-a.component';
import { CompBComponent } from './components/comp-b/comp-b.component';
const routes: Routes = [
{ path: '', component: CompBComponent },
{ path: 'a', component: CompAComponent }
];
#NgModule({
imports: [
CommonModule,
RouterModule.forRoot(routes)
],
exports: [ RouterModule ],
declarations: []
})
export class AppRoutingModule { }
Just for future reference for someone else running into this issue. Basically as mentioned the 'dots' were not being shown in safari. The reason for that was that in SPA the paths are generated on the fly (virtual paths). The event-drops lib add's a css style called 'filter': 'url(#metaballs)', and safari did not understand that the path to #metaballs in the svg is actually domain/virtual/route/#metaballs. Therefore the fix was to grab the returned d3 object as shown in comp-a.component.ts and simply over write the filter property for all g.drops with filter should be 'url(' + window.location.href + '#metaballs)'.
ie: d3.select('#eventdrops-demo').selectAll('g.drops').style('filter', 'url(' + window.location.href + '#metaballs)')
Hope this helps.

ERROR while adding custom module to parent app module in angular

I am trying to add new module to make my angular application more modular and for lazy loading.
app.module
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule,BrowserXhr } from "#angular/http";
import { LabService } from './lab/lab.service';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { OphistoryModule } from './Ophistory/ophistory.Module' //after adding this module ERROR is thrown
#NgModule({
imports:[
BrowserModule,
HttpModule,
FormsModule,
RouterModule.forRoot([
{path: 'labDetails/:labName',component:LabDetailsComponent},
{path:'showRoboLog/:labName',component:RoboLogComponent},
{path:'',component:LabComponent}
]),
OphistoryModule
],
declarations: [
AppComponent
],
bootstrap:[ AppComponent ]
})
export class AppModule {}
app.component
import { Component } from '#angular/core';
import { Http } from '#angular/http';
import { LabComponent } from './lab/lab.component';
import { LabService } from './lab/lab.service';
#Component({
selector: 'my-app',
template: `
<router-outlet></router-outlet>
`,
providers: [LabService]
})
export class AppComponent {
constructor() {
}
pageTitle: string = 'ABC';
}
The problem I am facing after adding the module OphistoryModule is an error message saying
my-app' is not a known element:
But when I give the same name to the selector of my newly added module it is working fine.
Here are the custom module and component
ophistory.module
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser'
import { OphistoryComponent } from './ophistory.component'
import {CommonModule} from '#angular/common'
#NgModule({
imports: [RouterModule.forChild([
{path: 'test',component:OphistoryComponent}
]),CommonModule],
declarations:[OphistoryComponent]
})
export class OphistoryModule {}
ophistory.component
import { Component } from '#angular/core'
import { LabService } from '../lab/lab.service'
#Component({
selector:'my-ap',
templateUrl:'./ophistory.component.html'
})
export class OphistoryComponent {
constructor (private _service:LabService){}
}
Can anyone confirm if it is a bug in angular 2 or any other solution you have?

Emit events between nested components grandchild to root component

I have wheels.component nested to car.component.
wheels.component:
export class WheelsComponent {
#Output() onLoaded : EventEmitter<string>() = new EventEmitter<string>();
private downloadAllFiles(url: string) {
this.onLoaded.emit('Hello, World 1!');
//some operations to wait
this.onLoaded.emit('Hello, World 2!');
};
}
Component car.component is not written at html page, but called through routing at car-routing.module.ts:
#NgModule({
imports: [
RouterModule.forChild([
{
path: 'sfactmessage/:id',
component: CarComponent,
resolve: {
card: cardResolver
}
}
])
],
exports: [RouterModule]
})
export class CarRoutingModule {}
What I want is to handle event emitted from wheels.component, not at car.component, but at app.component.
Is it possible to handle event at app.component?
The plunker sample is not working (sorry, this is my first plunkr example), but gives a view how my app is arranged.
Hello_ friend.
So basically if you want to use events globally in your application you can use a Service in combination with EventEmitter
In this case you create a service for example car.service.ts
import { Injectable, EventEmitter } from '#angular/core';
#Injectable()
export class CarService {
onLoaded : EventEmitter<string> = new EventEmitter<string>();
}
Then use this service in a child component to emit events like this wheels.component.ts
import { Component, EventEmitter } from '#angular/core';
import { CarService } from './car.service';
#Component({
selector: 'wheels',
template: '<a (click)="sendValues()"> Click me to send value </a>'
})
export class WheelsComponent {
constructor(private carService:CarService ){}
sendValues() {
/* Use service to emit events that can be used everywhere in the application */
this.carService.onLoaded.emit('Button in WheelsComponent was clicked ...');
};
}
and then capture this event from AppComponent for example app.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { CarService } from './cars/car.service';
import { Subscription } from 'rxjs';
#Component({
selector: 'my-app',
templateUrl: `src/app.component.html`
})
export class AppComponent implements OnInit, OnDestroy{
private subscription: Subscription;
private loading = true;
name = 'Angular';
constructor(private carService: CarService){}
ngOnInit(){
this.subscription = this.carService.onLoaded.subscribe((message) => {
/*
Here you receive events from anywhere where
carService.onLoaded.emit() is used
**/
alert(`From AppComponent -> ${message}`);
});
}
ngOnDestroy(){
/* Don't forget to unsubscribe when component is destroyed */
this.subscription.unsubscribe();
}
}
I M P O R T A N T______________
If you want your service to work globally you need to declare it in the top level providers for example app.module.ts is a good place:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { CarComponent} from './cars/car.component';
import { WheelsComponent} from './cars/wheels.component';
import { HomeComponent} from './home.component';
import { routing } from './app.routing';
import { CarService } from './cars/car.service';
#NgModule({
imports: [ BrowserModule, FormsModule, routing ],
declarations: [ AppComponent, CarComponent, WheelsComponent, HomeComponent ],
providers: [ CarService ], // <-------- SEE HERE
bootstrap: [ AppComponent ]
})
export class AppModule { }
CLICK HERE TO SEE THE DEMO

Angular 2: Passing Data to Routes?

I am working on this angular2 project in which I am using ROUTER_DIRECTIVES to navigate from one component to other.
There are 2 components. i.e. PagesComponent & DesignerComponent.
I want to navigate from PagesComponent to DesignerComponent.
So far its routing correctly but I needed to pass page Object so designer can load that page object in itself.
I tried using RouteParams But its getting page object undefined.
below is my code:
pages.component.ts
import {Component, OnInit ,Input} from 'angular2/core';
import { GlobalObjectsService} from './../../shared/services/global/global.objects.service';
import { ROUTER_DIRECTIVES, RouteConfig } from 'angular2/router';
import { DesignerComponent } from './../../designer/designer.component';
import {RouteParams} from 'angular2/router';
#Component({
selector: 'pages',
directives:[ROUTER_DIRECTIVES,],
templateUrl: 'app/project-manager/pages/pages.component.html'
})
#RouteConfig([
{ path: '/',name: 'Designer',component: DesignerComponent }
])
export class PagesComponent implements OnInit {
#Input() pages:any;
public selectedWorkspace:any;
constructor(private globalObjectsService:GlobalObjectsService) {
this.selectedWorkspace=this.globalObjectsService.selectedWorkspace;
}
ngOnInit() { }
}
In the html, I am doing following:
<scrollable height="300" class="list-group" style="overflow-y: auto; width: auto; height: 200px;" *ngFor="#page of pages">
{{page.name}}<a [routerLink]="['Designer',{page: page}]" title="Page Designer"><i class="fa fa-edit"></i></a>
</scrollable>
In the DesignerComponent constructor I have done the following:
constructor(params: RouteParams) {
this.page = params.get('page');
console.log(this.page);//undefined
}
So far its routing correctly to designer, but when I am trying to access page Object in designer then its showing undefined.
Any solutions?
You can't pass objects using router params, only strings because it needs to be reflected in the URL. It would be probably a better approach to use a shared service to pass data around between routed components anyway.
The old router allows to pass data but the new (RC.1) router doesn't yet.
Update
data was re-introduced in RC.4 How do I pass data in Angular 2 components while using Routing?
It changes in angular 2.1.0
In something.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { BlogComponent } from './blog.component';
import { AddComponent } from './add/add.component';
import { EditComponent } from './edit/edit.component';
import { RouterModule } from '#angular/router';
import { MaterialModule } from '#angular/material';
import { FormsModule } from '#angular/forms';
const routes = [
{
path: '',
component: BlogComponent
},
{
path: 'add',
component: AddComponent
},
{
path: 'edit/:id',
component: EditComponent,
data: {
type: 'edit'
}
}
];
#NgModule({
imports: [
CommonModule,
RouterModule.forChild(routes),
MaterialModule.forRoot(),
FormsModule
],
declarations: [BlogComponent, EditComponent, AddComponent]
})
export class BlogModule { }
To get the data or params in edit component
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params, Data } from '#angular/router';
#Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private router: Router
) { }
ngOnInit() {
this.route.snapshot.params['id'];
this.route.snapshot.data['type'];
}
}
You can do this:
app-routing-modules.ts:
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { PowerBoosterComponent } from './component/power-booster.component';
export const routes: Routes = [
{ path: 'pipeexamples',component: PowerBoosterComponent,
data:{ name:'shubham' } },
];
#NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
In this above route, I want to send data via a pipeexamples path to PowerBoosterComponent.So now I can receive this data in PowerBoosterComponent like this:
power-booster-component.ts
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params, Data } from '#angular/router';
#Component({
selector: 'power-booster',
template: `
<h2>Power Booster</h2>`
})
export class PowerBoosterComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private router: Router
) { }
ngOnInit() {
//this.route.snapshot.data['name']
console.log("Data via params: ",this.route.snapshot.data['name']);
}
}
So you can get the data by this.route.snapshot.data['name'].
1. Set up your routes to accept data
{
path: 'some-route',
loadChildren:
() => import(
'./some-component/some-component.module'
).then(
m => m.SomeComponentModule
),
data: {
key: 'value',
...
},
}
2. Navigate to route:
From HTML:
<a [routerLink]=['/some-component', { key: 'value', ... }> ... </a>
Or from Typescript:
import {Router} from '#angular/router';
...
this.router.navigate(
[
'/some-component',
{
key: 'value',
...
}
]
);
3. Get data from route
import {ActivatedRoute} from '#angular/router';
...
this.value = this.route.snapshot.params['key'];

Categories

Resources