I have some data that I am passing from my child to parent component and I want it to check if its empty or it has some value inside automatically.
this is in my login.compunent.ts - child ts
#Output() update=new EventEmitter<string>();
userEmail = "a#b.com";
authUser(loginForm) {
this.update.emit(userEmail);
this.router.navigate(['/home']);
}
this is in my app.compunent.ts - parent ts
emailData:string;
onUpdate(userEmail:string){
console.log("userEmail")
if(userEmail !== ''){
this.emailData = userEmail
console.log(userEmail)
}
}
this is in my app.compunent.html - parernt html
{{emailData}}
<router-outlet (update)="onUpdate($event)"></router-outlet>
I'm not sure I understand you completely but if you want to pass data from your child to your parent "automatically" I believe you have to implement a two-way bindable property.
You do that like this
child.ts
export class SomeComponent {
...
#Input() something;
// it's important to name it with the 'Change' suffix
#Output() somethingChange = new EventEmitter();
...
parent.html
<some-component [(something)] = "someFieldYouWantToTwoWayBindTo"></some-component>
now whenever you update something from your child the field someFieldYouWantToTwoWayBindTo will also be updated
Now if you want to check what's in something and only filter certain cases then implement a setter for someFieldYouWantToTwoWayBindTo
parent.ts
_someFieldYouWantToTwoWayBindTo
set someFieldYouWantToTwoWayBindTo(value) {
if(value !== '')
this._someFieldYouWantToTwoWayBindTo = value;
}
Related
So basically I have a modal component with an input field that tells it which modal should be opened (coz I didn't want to make a component for each modal):
#Input() type!:string
ngOnChanges(changes: SimpleChanges): void {
this.type = changes["type"].currentValue;
this.openModal();
}
that field is binded to one in the app component:
modalType = "auth";
HTML:
<app-modal [type] = modalType></app-modal>
In the beginning it's got the type "auth" (to login or register), but when I click on an icon I want to open a different modal, I do it like so:
<h1 id="options-route"
(click) ="modalType = 'settings'"
>⚙</h1>
but this only works the first time, when modalType already has the value "settings" the event doesn't trigger even though the value has technically changed
I think the problem is that it's the same value because i tried putting a button that does the exact same thing but with the value "auth" again and with that it was clear that the settings button only worked when tha last modal opened was auth and viceversa
any ideas? I want to be able to open the settings modal more than once consecutively possibly keeping onChange because ngDoCheck gets called a whole lot of times and it slows down the app
You need to include the changeDetectorRef, in order to continue in this way.
More about it https://angular.io/api/core/ChangeDetectorRef
Although, a better and a faster alternative is the use of a behavior Subject.
All you have to do is create a service that makes use of a behavior subject to cycle through each and every value exposed and then retrieve that value in as many components as you want. To do that just check for data changes in the ngOnInit of target component.
You may modify this for implementation,
private headerData = new BehaviorSubject(new HeaderData());
headerDataCurrent = this.headerData.asObservable();
changeHeaderData(headerDataNext : HeaderData) {
this.headerData.next(headerDataNext)
console.log("subscription - changeUserData - "+headerDataNext);
}
Explanation:
HeaderData is a class that includes the various values that can be shared with respective data types.
changeHeaderData({obj: value}), is used to update the subject with multiple values.
headerDataCurrent, an observable has to be subscribed to in the target component and data can be retrieved easily.
I mean i'm too l-a-z-y to use your slightly-not-so-much-tbh complicated answers so I just did this:
I added a counter that tops to 9 then gets resetted to 0 and I add it to the value
screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
//gets called onClick
openSettings(){
if(this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding === 9){
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
}
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding + 1;
this.modalType = "settings"+this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding;
}
then in the child component I just cut that last character out:
ngOnChanges(changes: SimpleChanges): void {
let change = changes["type"].currentValue as string;
change = change.substring(0, change.length - 1);
this.type = change;
this.openModal();
}
works like a charm 😂
I just created my first test in React.
Following an example of a tutorial I have created several buttons that activate a function by which they receive the index of the selected button. The first button selected must show one of the operators of the aray and the next one the opposite, and so on...
const operators = ['+', '-'];
const placeHolder = 'o';
function Boxes(props){
return (
<AppContext.Consumer>
{context => {
const value = context.boxes[props.index];
const icon = value !== null ? operators[value] : placeHolder;
const isDone = icon !== placeHolder ? 'done' : '';
return (
<button className="box-active"
onClick={() => context.boxAct(props.index)}>
{operator}
</button>
)
}}
</AppContext.Consumer>
)
}
and here is de function
boxAct = (index) => {
if (this.state.boxes[index] === null) {
this.state.boxes[index] = '+';
}
}
How can I achieve this? Following the steps of the example I only get the placeholder value in all the buttons and I can't get them to change.
What am I doing wrong?
thanks for your help
Functional components are different from class components. As you can read from official documentation react components.
Your Boxes component is declared as functional component. this.state syntax is valid just inside class component. In that case correct way to update state would be to call this.setState(<newstate>) function avilable to all class components. You can read more here react state.
You can provide state to functional components via hooks. In particolar useState hook. Here is explained how to do useState hook.
In your case (functional component) you can dop like this:
import hook with:
import React, { useState } from 'react';
then you need to initialize boxes state like this
const [boxes, setBoxes] = useState(context.boxes)
The function you will set as onClick handler is:
boxAct = (index) => { if (boxes[index] === null) {
let newBoxes = boxes;
nexBoxes[index] = '+';
setBoxes(newBoxes);
}
}
Be aware also that you should not pass data via context api, context api is used to provide global information such as application language or theme. Read more here react context
So I want to show an icon based on whether or not the number of projects in my list is > 3. I am using this getProjects() function that I need to subscribe to in order to get the data. I am setting a boolean when I subscribe that checks the number of projects in the list, then in my HTML, I use a ngIf to show the icon based on the boolean. I am able to get it to show correctly, however, I think I am constantly polling in my subscribe, and setting this boolean over and over again because it is making my webpage run really slow.
I have already tried the take(1) method which doesnt seem to stop the subscription, as well as set it to a "this.variable" scope inside my component. I am currently using event emitters however that is not working either.
This is my code so far,
Function that I subscribe to (in a different component):
getProjects(): Observable<ProjectInterfaceWithId[]> {
const organizationId = localStorage.getItem('organizationId');
return this.firestoreService.collection('organizations').doc(organizationId)
.collection('projects').snapshotChanges()
.pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data() as ProjectInterface;
const id = a.payload.doc.id;
return {id, ...data} as ProjectInterfaceWithId;
})),
map(list => {
if (list.length !== 0) {
this.buildProjectLookup(list);
this.projects = list;
return list;
}
})
);
}
Function that i use to get the data and set the boolean:
#Input() toggle: boolean;
#Output() iconStatus = new EventEmitter();
displayIcon() {
this.projectService.getProjects()
.pipe(take(1))
.subscribe(
list => {
if(list.length >= 3){
this.toggle = true;
this.iconStatus.emit(this.toggle);
}
});
}
HTML:
<i *ngIf="displayIcon()" class="material-icons">list</i>
Is there any way for me to literally just check the list length once so I don't get caught in this subscription loop? Thank you in advance!
It looks like it could be happening due to the ngIf referring to the displayIcon() method.
Every time change detection runs within your component, this method will be called. If your component is using default change detection, this will be very often.
see https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/ for more
One way this could be fixed is by making the ngIf refer to a variable instead.
For example, you could set a projects$ observable using
this.projects$ = this.projectService.getProjects()
.pipe(
take(1),
tap(projects => this.iconStatus.emit(projects.length >= 3))
);
This observable should likely be instantiated in your ngOnInit() method.
Then in your template you can use
<i *ngIf="(projects$ | async)?.length >= 3" class="material-icons">list</i>
In child component, I have a datatable, when I click on the row, I will get data and keep it in branchToDivision, I also have a button, when I hit that button, I can send branchToDivision to the parent component.
Child component
#Output() messageEvent: EventEmitter<BranchDto> = new EventEmitter<BranchDto>();
branchToDivision: BranchDto;
onSelect(record: BranchDto) {
this.branchToDivision = new BranchDto();
this.branchToDivision = record;
console.log(this.branchToDivision);
console.log(this.branchToDivision.brancH_CODE);
}
acceptBranch() {
this.onSelect(this.branchToDivision);
this.messageEvent.emit(this.branchToDivision);
this.branchModalDivision.hide();
}
Parent Component
branch: BranchDto;
getBranch(branch: BranchDto): void{
this.branch = branch;
console.log(this.branch);
console.log(this.branch.brancH_CODE);
}
Parent HTML
<branchModal #branchModal (messageEvent)="getBranch($event)" ></branchModal>
I try to log branch property but it is undefined, What's wrong? Any idea is helping me well.
This is a way to send information from child component to parent component:
parent.component.html:
<div>
<child (getData)="getData($event)"></child>
</div>
in parent.component.ts:
public getData(value): void {
console.log(value) // welcome to stackoverflow!
}
in child.component.ts:
import {Output, EventEmitter} from '#angular/core';
#Output() public getUserData = new EventEmitter<string>();
this.getUserData.emit('welcome to stackoverflow!');
I hope my help is effective ツ
I have created a custom element and placed on a page like this:
<my-custom-element [value]="100"></my-custom-element>
In the component definition, I have this:
#Input() value: number = 50;
At run-time, the value is always 50. I expect it to be 100. If I remove the default, value is undefined. What am I missing?
Thanks!!
In NG Elements you may not find in your OnIt but in OnChanges.
Please add below line and check it is defined.
public ngOnChanges(): void {
console.log('on changes value: ', this.value);
}
You can set data using HTML attributes and to change/update data in Angular Elements you have to use vanilla js, query selector and assign data like below
custom element tag with initial value = 0
<my-custom-element value="0" ></my-custom-element>
select custom element through the query selector and assign value.
var customElement = document.querySelector('my-custom-element');
customElement.value = 100;