Angular: Dynamic creation of components in HTML doesn't work - html

I am able to dynamically create components:
import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, OnInit, ViewChild, ViewContainerRef } from '#angular/core';
import { FilterComponent } from '../filter/filter.component';
export enum FilterType {
DateRangeFilter, SensorSelectFilter
}
#Component({
selector: 'app-filter-collection',
templateUrl: './filter-collection.component.html',
styleUrls: ['./filter-collection.component.css']
})
export class FilterCollectionComponent implements OnInit {
filters: Array<ComponentRef<FilterComponent>> = [];
#ViewChild("messagecontainer", { read: ViewContainerRef }) entry!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
onAddDateRangeFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter = this.entry.createComponent<FilterComponent>(factory);
filter.instance.filter = FilterType.DateRangeFilter;
this.filters.push(filter);
}
onAddSensorSelectFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter = this.entry.createComponent<FilterComponent>(factory);
filter.instance.filter = FilterType.SensorSelectFilter;
this.filters.push(filter);
}
ngOnInit(): void {
}
}
while FilterComponent looks like this:
#Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.css']
})
export class FilterComponent {
#Input() filter!: FilterType;
_FilterType = FilterType;
range = new FormGroup({
start: new FormControl(),
end: new FormControl()
});
constructor() {
}
ngOnInit(): void {
}
}
with this HTML:
<div *ngIf="filter === _FilterType.SensorSelectFilter">
<mat-form-field class="sensorFilter" appearance="fill">
<mat-label>Cars</mat-label>
<select matNativeControl required>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
</mat-form-field>
</div>
<div *ngIf="filter === _FilterType.DateRangeFilter">
<mat-form-field class="dateFilter" appearance="fill">
<mat-label>Enter a date range to filter data</mat-label>
<mat-date-range-input [formGroup]="range" [rangePicker]="picker">
<input matStartDate formControlName="start" placeholder="Start date">
<input matEndDate formControlName="end" placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
<mat-error *ngIf="range.controls.start.hasError('matStartDateInvalid')">Invalid start date</mat-error>
<mat-error *ngIf="range.controls.end.hasError('matEndDateInvalid')">Invalid end date</mat-error>
</mat-form-field>
</div>
However, I am not sure how to instantiate those components into HTML, I tried it like this in FilterCollectionComponent.html:
<div *ngFor="let filter of filters">
<app-filter [filter]="filter.instance.filter"></app-filter>
</div>
But this doesn't work.
Any ideas why? Any help is very much appreciated!

filter-collection.component.html:
<div #dynamicFilters></div>
filter-collection.component.ts
import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef, OnInit, ViewChild, ViewContainerRef } from '#angular/core';
import { FilterComponent } from '../filter/filter.component';
export enum FilterType {
DateRangeFilter, SensorSelectFilter
}
#Component({
selector: 'app-filter-collection',
templateUrl: './filter-collection.component.html',
styleUrls: ['./filter-collection.component.css']
})
export class FilterCollectionComponent implements OnInit {
filters : Array<ComponentRef<FilterComponent>> = [];
#ViewChild('dynamicFilters', { read: ViewContainerRef }) dynamicInsert!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
onAddDateRangeFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter = this.dynamicInsert.createComponent<FilterComponent>(factory);
filter.instance.filter = FilterType.DateRangeFilter;
this.filters.push(filter);
}
onAddSensorSelectFilter() {
const factory: ComponentFactory<FilterComponent> = this.resolver.resolveComponentFactory(FilterComponent);
const filter = this.dynamicInsert.createComponent<FilterComponent>(factory);
filter.instance.filter = FilterType.SensorSelectFilter;
this.filters.push(filter);
}
ngOnInit(): void {
}
}

Related

Angular autocomplete is not working MySQL API

we tried Autocomplete in angular using codeigniter3 controller as a api. but it not reflected in the angular home page.
Autoserivce.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class AutoService {
private baseURL = 'http://localhost/travelgate/api/item';
constructor(private http: HttpClient) {}
getData() {
return this.http.get(this.baseURL);
}
}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { AutoService } from './auto.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
title = 'ng-auto-complete';
posts!: any;
name: any;
constructor(private service: AutoService) {}
ngOnInit() {
this.getAllData();
}
getAllData() {
this.service.getData().subscribe((res: any) => {
this.posts = res;
console.log(this.posts);
});
}
nameValue(name: any) {
this.name = name;
console.log(this.name);
}
}
app.Component.html
<div class="container">
<div class="row">
<div class="col-md-12">
<form class="form mt-5">
<label for="exampleDataList" class="form-label">Datalist example</label>
<input class="form-control" list="datalistOptions" id="exampleDataList" placeholder="Type to search..."
(change)="nameValue($any($event.target).value)">
<datalist id="datalistOptions">
<option *ngFor="let post of posts" [value]="post.name">
</datalist>
</form>
</div>
</div>
</div>
item.php
<?php
require APPPATH . 'libraries/REST_Controller.php';
class Item extends REST_Controller {
public function __construct() {
parent::__construct();
$this->load->database();
}
public function index_get($search = 0)
{
if(!empty($name)){
$this->db->select('*');
$this->db->from('rezlive_hotels_city_list');
$this->db->like('name', $name);
$data = $this->db->get()->result_array();
}else{
$query = $this->db->get("rezlive_hotels_city_list");
$resultList=$query->result_array();
$data= json_encode($resultList);
}
$this->response($data, REST_Controller::HTTP_OK);
}
}
screenshot

How to get values from a list inside a JSON in Angular?

I need to get the values that are inside cities from this JSON, but i canĀ“t:
{
"id":0,
"department":"Amazonas",
"cities":["Leticia","Puerto Bayarta",]
},
{
"id":1,
"department":"Antioquia",
"cities":["Medellin","Bello",]
}
These are the components and services that I made:
cities.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class CitiesService {
constructor(private http: HttpClient) {}
getJSON(url: string) {
return this.http.get(url);
}
}
Component has an interface:
nueva-organizacion.component.ts
import { Component, OnInit } from '#angular/core';
import { CitiesService } from 'src/app/services/cities.service';
interface City{
department: string;
cities: string[];
}
#Component({
selector: 'app-nueva-organizacion',
templateUrl: './nueva-organizacion.component.html',
styleUrls: ['./nueva-organizacion.component.css'],
})
export class NuevaOrganizacionComponent implements OnInit {
public cities: City[] = [];
constructor(
public cities: CitiesService,
) {}
ngOnInit(): void {
this.cities
.getJSON(
'https://raw.githubusercontent.com/marcovega/colombia-json/master/colombia.min.json'
)
.subscribe((res: any) => {
this.cities = res;
});
}
And finally, i would like to show the cities in a selector:
nueva-organizacion.component.html
<div class="form-input">
<select id="city" class="custom-select">
<option selected>Choose one...</option>
<option *ngFor="let city of cities">
{{ city.cities }}
</option>
</select>
</div>
I would like to get something like this in the selector:
Choose one...
Leticia
Puerto Bayarta
Medellin
Bello
But I get this:
Choose one...
Leticia, Puerto Bayarta
Medellin, Bello
Maybe the correct way is using the index {{ city.cities[] }} but I don't know how.
Please keep in mind that inside NuevaOrganizacionComponent you have to properties with the same name: 'cities', first the cities array and second the cities service.
Also I recommend you to use two selects instead of one, the first to select the department and the second to select the city.
The code will look like this:
nueva-organizacion.component.ts:
import { Component, OnInit } from '#angular/core';
import { CitiesService } from 'src/app/services/cities.service';
#Component({
selector: 'app-nueva-organizacion',
templateUrl: './nueva-organizacion.component.html',
styleUrls: ['./nueva-organizacion.component.css'],
})
export class NuevaOrganizacionComponent implements OnInit {
public departmentsArr = []
public departmentSelected:any = null;
public citySelected:any = null;
constructor(
public citiesServ: CitiesService,
) {}
ngOnInit(): void {
const urlCities = "https://raw.githubusercontent.com/marcovega/colombia-json/master/colombia.min.json"
this.citiesServ.getJSON(urlCities)
.subscribe((res: any) => {
this.departmentsArr = res;
});
}
getCities(){
return this.departmentsArr.find(department => department.id == this.departmentSelected).ciudades
}
alertSelection(){
const departmentName = this.departmentsArr.find(department => department.id == this.departmentSelected).departamento;
const cityName = this.citySelected;
alert(`
You selected the department: ${departmentName} and the city: ${cityName}`)
}
}
nueva-organizacion.component.html
<div class="form-input">
<label for="departmentSelector">Select your department</label>
<select id="departmentSelected" class="custom-select" name="departmentSelector" [(ngModel)]="departmentSelected">
<option value="null">Choose one department...</option>
<option
*ngFor="let department of departmentsArr"
[value]="department.id">
{{ department.departamento }}
</option>
</select>
</div>
<div class="form-input" *ngIf="departmentSelected">
<label for="citySelector">Select your city</label>
<select id="citySelector" class="custom-select" name="citySelector" [(ngModel)]="citySelected">
<option selected>Choose one city...</option>
<option
*ngFor="let city of getCities()"
[value]="city">
{{ city }}
</option>
</select>
</div>
<button (click)="alertSelection()">WHAT WAS SELECTED?</button>
Also please verify FormsModule is imported into your app.module.ts or yourSubmodule.module.ts. This is important to enable the ([ngModule]) functionality.
import {FormsModule} from "#angular/forms";
// Other things
imports: [
// Other modules
FormsModule
],
ngOnInit(): void {
this.cities
.getJSON(
'https://raw.githubusercontent.com/marcovega/colombia-json/master/colombia.min.json'
)
.subscribe((res: any) => {
this.cities = [];
foreach(entry in res){
foreach(citi in entry.cities){
this.cities.push(citi);
}
}
});
}
On this side, you have an array of entries that has an array of cities inside. You need to flatten first all the cities inside ans, something like the code above. My code isn't exact but should give you an idea of what to write.

Angular: Fill Dropdown with Database

I'm new in coding and wanted to make a small Raid Planner.
Now I try to fill my Dropdown with the Raidnames from my database and could need some help with this step. I have problems with adding the data in a dropdownlist.
raid.service.ts
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { RaidItem } from 'src/app/classes/raid-item';
import { environment } from './../environments/environment';
import { publishReplay, refCount } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class RaidService {
constructor(private httpClient: HttpClient) { }
private raidApiUrl = environment.webApiBaseUrl + '/api/Raid/';
getRaids(): Observable < RaidItem[] > {
return this.httpClient.get < RaidItem[] > (this.raidApiUrl + 'GetRaids').pipe(
publishReplay(1),
refCount());
}
}
raid.item.ts
export class RaidItem {
Id: number;
Name: string;
}
edit.component.ts
import { Component, OnInit } from '#angular/core';
import { NgbDateStruct, NgbCalendar } from '#ng-bootstrap/ng-bootstrap';
import { NgbDateStructAdapter } from '#ng-bootstrap/ng-bootstrap/datepicker/adapters/ngb-date-adapter';
import { NgbTimeStruct } from '#ng-bootstrap/ng-bootstrap';
import { RaidService } from 'src/services/raid.service';
import { RaidItem } from '../classes/raid-item';
#Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit {
time = {hour: 13, minute: 30, second: 0};
hourStep = 1;
minuteStep = 15;
model: NgbDateStruct;
date: {year: number, month: number};
raidItems: RaidItem[] = [];
constructor(private calendar: NgbCalendar, private raidService: RaidService) { }
ngOnInit() {
this.raidService.getRaids().subscribe(raidResult => {
this.raidItems = raidResult;
});
}
selectToday() {
this.model = this.calendar.getToday();
}
onSubmit() {
}
}
edit.component.html
With this step I have the most problems. Don't know exactly how to get the raidnames into the dropdown
<div class="container1">
<ngb-datepicker #dp [(ngModel)]="model" (navigate)="date = $event.next"></ngb-datepicker>
</div>
<div class="container2">
<ngb-timepicker [(ngModel)]="time" [seconds]="false"
[hourStep]="hourStep" [minuteStep]="minuteStep" [secondStep]="00"></ngb-timepicker>
</div>
<select formControlName="raids" id="raids">
<option *ngFor="let RaidItem of getRaids(); let i = index" [value]="getRaids[i].Name">
{{getRaids[i].Name}}
</option>
</select>
You already stored your output in raidItems inside the compoent. SO don't need to call function from template. Use variable to construct the loop.
<option *ngFor="let raidItem of raidItems" [value]="raidItem.Name">
{{raidItem.Name}}
</option>
NgFor already provides alias to each iteration, which in your case is RaidItem. getRaids is a method, but you tried to use it like a variable.
This should work:
<select formControlName="raids" id="raids">
<option *ngFor="let RaidItem of getRaids(); let i = index" [value]="RaidItem.Id">
{{RaidItem.Name}}
</option>
</select>

Setting selected in dropdownlist with Angular

I have a list displayed in a dropdownlist, but it displays the default as a blank and not as the first item in the dropdown.
I have tried adding let i = 0 and then [selected]="i = 0", but this does not seem to set the default item to the first item, however I am receiving the correct value back from i.
Below is my code:
<div class="form-group">
<label for="userName">User Name</label>
<select formControlName="userName" class="form-control" (change)="userChange($event)">
<option *ngFor="let row of usersModel;let i = index" value="{{ row.id }}" [selected]="i == 0">{{ row.userName }} {{ i }}</option>
</select>
</div>
Here is my TypeScript File:
import { Component, OnInit } from '#angular/core';
import { UserAdminService } from '../../services/user-admin.service';
import { FormBuilder, Form, FormControl, FormGroup } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'lib-add-user-to-role',
templateUrl: './add-user-to-role.component.html',
styleUrls: ['./add-user-to-role.component.css']
})
export class AddUserToRoleComponent implements OnInit {
addUserToRoleForm: FormGroup;
rolesModel: any[];
usersModel: any[];
selectedRole: string;
selectedUser: string;
constructor(private userAdminService: UserAdminService, private formBuilder: FormBuilder, private router: Router) {
var roleControl = new FormControl('');
var userControl = new FormControl('');
this.addUserToRoleForm = formBuilder.group({ roleName: roleControl, userName: userControl });
}
ngOnInit() {
this.userAdminService.getRoles().subscribe(roles => {
this.rolesModel = roles;
this.selectedRole = this.rolesModel[0].name;
this.userAdminService.getUsersNotInRole(this.selectedRole).subscribe(users => {
this.usersModel = users;
this.selectedUser = this.usersModel[0].id;
console.log(this.usersModel[0].userName);
this.addUserToRoleForm.controls['roleName'].setValue(this.rolesModel[0].name);
this.addUserToRoleForm.controls['userName'].setValue(this.usersModel[0].userName);
});
});
}
userChange(event: any) {
this.selectedUser = event.target.value;
console.log('Selected ' + this.selectedUser);
}
AddUserToRole() {
this.userAdminService.addUserToRole(this.selectedUser, this.selectedRole)
.subscribe(result => {
if (result.success) {
this.router.navigate(['/roleusermaint']);
}
else {
console.log('Error Received on adding user to role');
}
});
}
}
As you can see I added {{ i }} in the text to make sure it shows the value of i and it does:
What am I missing ?
Thanks for any help!
#Axle, if you're using a Reactive Form, you needn't use [selected] nor (change), just, when you create the form you give value to userName
Using the constructor
const firstId=usersModel[0].id
this.form=new FormGroup({
userName:new FormControl(firstId)
})
Using formBuilder
const firstId=usersModel[0].id
this.form=this.fb.group({
userName:firstId
})
Using setValue, after create the form
const firstId=usersModel[0].id
this.form.get('userName').setValue(firstId)
As you are using Angular reactive form, try to keep the logic in ts file itself.
Using setValue(), you can set the default value to a control.
To set the default value to form control you could to it like,
this.form.controls['country'].setValue(this.countries[0].id)
In template use it like,
<option *ngFor="let country of countries" [value]="country.id">
{{ country.name }}
</option>
Working Stackblitz
Ref:
A complete sample code would be something like,
app.component.ts
import { Component } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
import {Country} from './country';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
countries = [
{
id: 'us',
name: 'United States'
},
{
id: 'uk',
name: 'United Kingdom'
},
{
id: 'ca',
name: 'Canada'
}
];
form: FormGroup;
ngOnInit(){
this.form = new FormGroup({
country: new FormControl('')
});
this.form.controls['country'].setValue(this.countries[0].id)
}
}
app.component.html
<form [formGroup]="form">
<select formControlName="country">
<option *ngFor="let country of countries" [value]="country.id">
{{ country.name }}
</option>
</select>
</form>

InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe' at invalidPipeArgumentError

I want to get some data from database
Here is my service
export class CategoryService {
constructor(private db: AngularFireDatabase) { }
getCategories(){
return this.db.list('/categories');
}
$Component code
export class ProductFormComponent implements OnInit {
categories$;
constructor(categoryService: CategoryService) {
this.categories$ = categoryService.getCategories();
}
$here is my html
<div class="form-group">
<label for="category">Category</label>
<select id="category" class="form-control">
<option value=""> </option>
<option *ngFor="let c of categories$ | async" [value]="c.$key">
{{c.name}}
</option>
</select>
</div>
Firebase libraries return promises. Make them return observables.
import { from } from 'rxjs/operators';
export class CategoryService {
constructor(private db: AngularFireDatabase) { }
getCategories(){
return from(this.db.list('/categories'));
}
Try these code it should work
import {Injectable} from '#angular/core';
import {AngularFireDatabase} from '#angular/fire/database';
import {from} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class CategoryService {
constructor(private db: AngularFireDatabase) {}
getCategories() {
return from(this.db.list('/categories').valueChanges());
}
}