I need to pass two values from two different dropdowns to my service. And I would like to do it on click on the button. So I wrote component like this:
import { Component, Input, OnInit } from '#angular/core';
import { CategoryType } from '../models/category-type.enum';
import { CountryType } from '../models/country-type.enum';
import { NewsServiceService } from '../services/news-service.service';
#Component({
selector: 'app-dropdown',
template:
`
<div>
<select [(ngModel)]="catSelected" (ngModelChange) = "onClick($event)" >
<option *ngFor = "let type of CategoryType.values()" [ngValue] = 'type'>
{{type}}
</option>
</select>
<br>
<select [(ngModel)]="couSelected" (ngModelChange) = "onClick($event)" >
<option *ngFor = "let type of CountryType.values()" [ngValue] = 'type'>
{{type}}
</option>
</select>
<br>
<button ng-click="onClick()">Click to get news</button>
</div>
`
})
export class DropdownComponent implements OnInit {
CategoryType = CategoryType;
CountryType = CountryType;
title: string;
couSelected: string;
catSelected: string;
constructor(private newsService: NewsServiceService) {
}
ngOnInit() {
this.catSelected = 'Select_Category';
this.couSelected = 'Select_Country';
}
onClick(catSelected: string, couSelected: string) {
this.newsService.getNews(catSelected, couSelected).subscribe(
data => {
this.catSelected = data;
}
);
}
}
Like i wrote I have two dropdowns with values
CategoryType
CountryType
and one button to submit the click.
Every change on dropdown makes ngModelChange and send only one value to my services.
I wanna achive:
Select value from CategoryType
Select value from CountryType
Then Click the button and pass values CategoryType and CountryType to my service.
Right now only one value is passed and I dont know why.
onClick function does not working at all.
Becouse your are passing only one value to onClick() function.
Correct by passing the elements from inside of DropdownComponent instead of passing it as click($event)
import { Component, Input, OnInit } from '#angular/core';
import { CategoryType } from '../models/category-type.enum';
import { CountryType } from '../models/country-type.enum';
import { NewsServiceService } from '../services/news-service.service';
#Component({
selector: 'app-dropdown',
template:
`
<div>
<select [(ngModel)]="catSelected" >
<option *ngFor = "let type of CategoryType.values()" [ngValue] = 'type'>
{{type}}
</option>
</select>
<br>
<select [(ngModel)]="couSelected">
<option *ngFor = "let type of CountryType.values()" [ngValue] = 'type'>
{{type}}
</option>
</select>
<br>
<button ng-click="onClick()">Click to get news</button>
</div>
`
})
export class DropdownComponent implements OnInit {
CategoryType = CategoryType;
CountryType = CountryType;
title: string;
couSelected: string;
catSelected: string;
constructor(private newsService: NewsServiceService) {
}
ngOnInit() {
this.catSelected = 'Select_Category';
this.couSelected = 'Select_Country';
}
onClick() {
this.newsService.getNews(this.catSelected, this.couSelected).subscribe(
data => {
this.catSelected = data;
}
);
}
}
There are two main problems with your code:
You're getting catSelected and couSelected from parameters, but you could use simply this reference:
onClick() {
this.newsService.getNews(this.catSelected, this.couSelected)...
}
You're using ng-click instead of (click), that's why button's click isn't being fired. Do this:
<button (click)="onClick()">Click to get news</button>
That being said, I want to give you some tips:
Don't call functions in templates. Instead, declare CategoryType and CountryType like this:
readonly categoryTypes = CategoryType.values();
readonly countryTypes = CountryType.values();
Don't use [ngValue] for primitive types. It isn't necessary. You can just use [value].
Related
I am trying to make a raffle project. I'm stuck at the step where I have to create a button on click that will add the first name in a table of absentees.
I created my mock in which I declared my values (names etc.) then I imported my array into my component. I have my HTML in which there is my <select> and ` tag which allows to create a list. I would like a button that allows you to add an absentee to a list
<div>
<h1>Ajouter un.e absent.e </h1>
<select>
<option value="placeholder">Séléctionner une personne</option>
<option value="liste des étudiants" *ngFor="let option of students">
{{option.name}}
</option>
</select>
<button (click)="add()">Ajouter une personne</button>
import { Component } from '#angular/core';
import { Students, STUDENTS } from 'src/app/mock/students';
#Component({
selector: 'app-absents',
templateUrl: './absents.component.html',
styleUrls: ['./absents.component.css'],
})
export class AbsentsComponent {
students : Students[] = STUDENTS;
absents = [];
add() {
????
}
I use changeDetection strategy and it works ok but when my component is destroy when I come back I have the last value saved.
Example I props 3 values to child component and went to another component when i try again to prop data i see my last values ..
Example props values 1 , 2 and 3.
I see my last values 3.
How to destoy it ?
Check code and parent component:
<div class="row">
<app-poi-address [poiPin]="pinedAddress"></app-poi-address>
</div>
this.pinedAddress = $event;
Child component:
#Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PoiAddressComponent implements OnInit, OnChanges {
#Input () poiPin!: Object
public allPin: any[] = [];
constructor() { }
ngOnInit(): void {
console.log('datica' , this.poiPin);
}
ngOnChanges(): void {
console.log('HelloComponent: Change Detection count = ' , this.poiPin);
this.allPin.push(this.poiPin)
}
}
<div *ngFor="let d of allPin ">
<p> {{ d?.coords?.lat }} </p>
</div>
Saved last values. I want to clear all array of allPin...
This would clear your all pin variable on when recreating the component after desctruction.
ngOnInit(): void {
allPin = [];
console.log('datica' , this.poiPin);
}
I have a modal component with ng-content. In this ng-content I can pass other components with injector. I need to pass anyway an object inside it for editing data. But I need some help. This is the modal component html:
<div class="modal">
<div class="modal-body">
this is my Object: {{model | json}}
<ng-content></ng-content>
</div>
<div class="modal-footer">
<button (click)="sendMessage()">success</button>
<button>abort</button>
</div>
</div>
The components inside ng-content may vary. In my example I have an input. I need to pass a model inside that components.
I use a service with injector to pass the content into my modalComponent, and in the function "open" I defined a parameter obj that's the object that I need to see into ng-content component. (The parameter is stored inside "model" input on my Modal component).
#Injectable()
export class ModalService {
dialogComponentRef: ComponentRef<ModalComponent>;
private subject = new Subject<any>();
open(content: any, obj: any) {
const contentComponentFactory = this.cfResolver.resolveComponentFactory(content);
const modalComponentFactory = this.cfResolver.resolveComponentFactory(ModalComponent);
const contentComponent = contentComponentFactory.create(this.injector);
const modalComponent = modalComponentFactory.create(this.injector, [[contentComponent.location.nativeElement]]);
modalComponent.instance.model = obj;
this.dialogComponentRef = modalComponent;
document.body.appendChild(modalComponent.location.nativeElement);
this.appRef.attachView(contentComponent.hostView);
this.appRef.attachView(modalComponent.hostView);
}
}
This is my modalComponent Class:
export class ModalComponent {
#Input() model: any;
sendMessage() {
this.modalService.sendMessage();
}
constructor(private modalService: ModalService) {}
}
Therefore I defined a component with this html:
<input type="text" value="name" placeholder="name">
Inside this input I desire to see the object for editing this value.
I can't find a solution for having the object inside the content-component....I need it because I would have a form where I must edit some values.
This is my stackblitz example: Example
What you can do is assign the formData to a property of a service and then read that property in the content component's ngOnInit(), I tried below code and its working for me.
#Injectable()
export class ModalService {
dialogComponentRef: ComponentRef<ModalComponent>;
formData:any;
private subject = new Subject<any>();
open(content: any, obj: any) {
this.formData = obj;
const contentComponentFactory = this.cfResolver.resolveComponentFactory(content);
const modalComponentFactory = this.cfResolver.resolveComponentFactory(ModalComponent);
const contentComponent = contentComponentFactory.create(this.injector);
const modalComponent = modalComponentFactory.create(this.injector, [[contentComponent.location.nativeElement]]);
modalComponent.instance.model = obj;
this.dialogComponentRef = modalComponent;
document.body.appendChild(modalComponent.location.nativeElement);
this.appRef.attachView(contentComponent.hostView);
this.appRef.attachView(modalComponent.hostView);
}
}
and in ngOnInit() of content-component,
ngOnInit() {
this.name = this.modalService.formData.name;
}
However you can try to wrap this property in some function inside service, rather than accessing the property directly(getFormData() or something)
So I am stuck on this. I am trying to get the Parent component to talk or integrate with the child component.
Here is the parent component which basically has the for loop used to iterate or generate more links if a user wants to add more or presses the button to add more.
<div class="section url-wrapper">
<div *ngFor="let url of urls; let i = index;" class="link input-wrapper">
<childComponent></childComponent>
<button class="button bad icon-only" (click)="removeURL(i)">
<i class="far fa-times"></i>
</button>
</div>
</div>
The parent component should only be able to register and display the output of the child component.
This is an example of the child component
<div class="section url-wrap">
<input aria-label="URL Title" placeholder="Title" type="text" [value]="urls[i].title" (ngModel)="urls[i].title" name="url.title.{{ i }}"
(input)="updateTitle(i, $event.target.value)">
<input aria-label="URL" placeholder="https://example.com" type="text" [value]="urls[i].url" (ngModel)="urls[i].url" name="url.url.{{ i }}"
(input)="updateUrl(i, $event.target.value)">
</div>
I need help both allowing the parent component to register input from the child component and being able to iterate from the for loop from the parent if it is possible.
Please let me know if you need more information such as the component files or clarification
The below code & example will demonstrate how data flows from parent -> child -> parent by using the #Input() and #Output() directives.
Working Example Here
parent.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
<div class="section url-wrapper">
<div *ngFor="let url of urls" class="link input-wrapper">
<app-child [url]="url" (updateUrl)="onUrlUpdate($event)"></app-child>
</div>
</div>
`
})
export class ParentComponent implements OnInit {
public urls = [
{url: "https://example.com", title: "Example1"},
{url: "https://example.com", title: "Example2"},
{url: "https://example.com", title: "Example3"},
]
constructor() { }
ngOnInit() {
}
onUrlUpdate($event) {
// completely overkill, but just used to demonstrate a point
var url = this.urls.find(_url => {
// we can see here that the $event.url is actually the same object as the urls[i] that was
// passed to the child. We do not lose the reference when it is passed to the child or back
// up to the parent.
return $event.url === _url
});
if (url) {
url[$event.prop] = $event.newValue;
}
console.log(`Updated URL's "${$event.prop}" property with the value "${$event.newValue}"`);
}
}
child.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<div class="section url-wrap">
<input aria-label="URL Title" placeholder="Title" type="text" [value]="url.title"
(input)="handleUrlUpdate($event, 'title')"/>
<input aria-label="URL" placeholder="https://example.com" type="text" [value]="url.url"
(input)="handleUrlUpdate($event, 'url')"/>
</div>
`,
})
export class ChildComponent implements OnInit {
#Input() url; // passed in from parent via [url] property on <app-child>
#Output() updateUrl = new EventEmitter();
constructor() { }
ngOnInit() {
// this.url is now available for the life of the child component (assuming it was passed by the parent)
}
handleUrlUpdate($event, propToUpdate) {
// overkill, but used to demonstrate a point
this.updateUrl.emit({url: this.url, prop: propToUpdate, newValue: $event.target.value});
}
}
The stardard way to let components speack each others is with input-output:
You can pass values from parent to children with #Input for example:
Parent code:
<childComponent [someInputValue]="hello"></childComponent>
Children code:
#Input() someInputValue; //this property will be "hello"
and you can pass values (after being triggered) from children to parent:
Children code:
#Output() itemSelectedOutput: EventEmitter<any> = new EventEmitter();
buttonClicked() {
this.itemSelectedOutput.emit("clicked");
}
Parent code:
<childComponent [someInputValue]="hello" (itemSelectedOutput)="someParentMethod($event)"></childComponent>
someParentMethod(event: any) {
console.log(event);
}
You can reach the same thing with ISubscription but I suggest you to use the way above
Hope it can help
I wouldn't do it this way in particular. If the children have to know about the parents, then your architecture should be adjusted
I'm creating a shopping list.
It will be made out of two components: shopping-cart and shopped-item.
The shopping-cart has a button that dynamically adds a new shopped-item in a <div>.
The shopped-item after being added can be marked as active or unmarked so I created an EventEmmiter that changes the value marked/unmarked.
But since the component is added dynamically I don't know where to add it in shopping-cart component...
How can I make it work like this:
After the shopped-item is added it appears in an array with marked/unmarked value that changes when it's clicked in the shopped-item component?
Cheers!
Shopped-item.ts file:
export class ShoppedItemComponent implements OnInit {
_ref:any;
removeObject(){
this._ref.destroy();
}
#Output() statusChange = new EventEmitter<{status: boolean}>();
marked;
unmarkItem () {
this.marked = !this.marked;
this.statusChange.emit({status: this.marked});
}
constructor() {
}
}
Shopping-cart.ts file:
export class ShoppingCartComponent implements OnInit {
#ViewChild('boughtItems', { read: ViewContainerRef }) boughtItems:
ViewContainerRef;
constructor(
private resolver: ComponentFactoryResolver
) { }
isMarked = [];
shoppedItemStatus (statusChange: {status: boolean}) {
this.isMarked.push({
status: statusChange.status
})
}
addItem() {
const shoppedItem =
this.resolver.resolveComponentFactory(ShoppedItemComponent);
const component = this.boughtItems.createComponent(shoppedItem);
component.instance._ref = component;
}
}
Shopping-cart.html file:
<div #boughtItems>
<button (click)="addItem()">ADD</button>
</div>
Why are you creating the components by hand?
I would use a *ngFor in the view
<div #boughtItems>
<button (click)="addItem()">ADD</button>
<div *ngFor="let item of items">
<app-item-bought xxxx="item" (markToggle)="myFunction(item)"></app-item-bought>
</div>
</div>
where xxxx is a field of your class ShoppedItemComponent decorated with Input('xxxx').
(markToggle) is the name of the emitter in ShoppedItemComponent and myFunction(item) is a function defined in the Shopping-cart that will receive the item that has fired the event.
Hope it helps!