Angular 2 Child1-Parent-Child2 event propagating - javascript

I am working on Angular 2 for only couple of weeks, so try to be considerate, please, and maybe give me some useful advice? Thank you. I have a main component(pageComponent), that inside its template has other two smaller components(tableComponent and filterComponent), each with its own functions and properties. How do I catch an event created in one of the child components and propagate it to another child component of the same parent? In other words, how to propagate and communicate events between two sibling components within the same parent component's template?

I think you've got a couple of options here.
The first child component could emit the event using the #Ouput decorator, and
have that event handler invoke an action on the sibling.
E.g. the sibling components
export class TableComponent {
#Output() anEvent: EventEmitter<any> = new EventEmitter<any>();
..
}
export class FilterComponent {
callMeWhenSomethingHappens($event){
..
}
}
The parent component template, which uses the #theFilter local variable to call the filter component when the event is emitted.
<app-filter #theFilter>
</app-filter>
<app-table (anEvent)="theFilter.callMeWhenSomethingHappens($event)">
</app-table>
You could also look at using a shared service if the sibling components don't have access to each other in the template (e.g. if they are created by a router-outlet).

You can use the Flux pattern. Basically have your components subscribe to events exposed from your service. Since services are singletons, you can share the data that it provides across components, no matter how deep they are in the component tree. Here is an example: How can I maintain the state of dialog box with progress all over my Angular 2 application?

Related

How call a method from an other component

I have made an example in Codesandbox
Is there a way to collapse the sidebar by clicking on the button 'Col-2'. The button 'Col-1' is working fine, but it's necessary that is works by clicking on 'Col-2'.
I've tried to call the collapsedButton but this didn't work.
<script>
export default {
name: 'template',
methods: {
setCollapsed () {
this.collapsed = !this.collapsed
},
collapseButton () {
this.$emit('setCollapsed')
this.collapsed = !this.collapsed
}
}
}
</script>
Can someone help me out with this?
You should make collapsed a prop and control it from the parent component. You'll need to listen for the event (this.$emit('setCollapsed')) in the parent component and update collapsed accordingly.
I hope this helps, good luck!
The recommended way to achieve this is to use a store.
The store is a module, external to any component, which can be imported in any number of components. When the state of the store (which is pretty much like the data function of a component) is changed, the change is propagated in every single component where the store is used.
Since you're using Vue 2, you could use a mixin.
Here's a mixin using a basic version of a store, under the hood: https://codesandbox.io/s/vue-forked-tz1yox?file=/src/components/sidebar.vue
In more detail, it uses a reactive object which serves as shared state for sidebar's collapsed status while exposing a writable computed (collapsed) and a toggleSidebar method.
Using the mixin in any component is the equivalent of writing the writable computed and the method into the component.
Note: mixins don't play nice with TypeScript. But the above solution doesn't need a mixin. I only used one so I didn't have to write the same computed and same method in both components.
The key to it working is the shared/external reactive object. In Vue 3 you would achieve this using Composition API.

Access Children in Angular

We want to write a little tab component in Angular.
I wrote a component like this:
<portal-flex-grid-tab-bar>
<portal-flex-grid-tab TabName="Apotheke" TabIdentifier="Apotheke" TabSelected="true" (TabClicked)="OnTabClick(0)"></portal-flex-grid-tab>
<portal-flex-grid-tab TabName="Bundesland" TabIdentifier="Bundesland" (TabClicked)="OnTabClick(1)"></portal-flex-grid-tab>
<portal-flex-grid-tab TabName="ARZ Einzugsgebiet" TabIdentifier="ARZ" (TabClicked)="OnTabClick(2)"></portal-flex-grid-tab>
</portal-flex-grid-tab-bar>
We want to offer the Tab Bar and clicked event which returns the identifier of the clicked tab. If a tab is clicked (selected) we want to deselect the other tabs.
To achieve this goal, we need a way for the parent to talk with his children components and the other way around we need the tabs to send an event to the tab bar. How can we achieve this goal? We would be glad for any ideas as we are starters in Angular.
Generally you shouldn't "talk to" child components, but if you have to, use #ContentChild directive - https://angular.io/api/core/ContentChild
In your case you can control children selection in parent component, e.g. with property selectedTab.
So in parent component in you ts:
public selectedTab: 0;
public onTabSelected(value: number): void {
this.selectedTab = value;
}
and in parent component in html:
<section>
<app-child-tab isSelected="selectedTab === 0" (tabClicked)="onTabSelected(0)">
</app-child-tab>
<app-child-tab isSelected="selectedTab === 1" (tabClicked)="onTabSelected(1)">
</app-child-tab>
</section>
In you child component you have to add EventEmitter:
export class ChildComponent {
#Output() tabClicked: EventEmitter<void> = new EventEmitter();
public click(): void {
this.tabClicked.emit();
}
}
The most common way for parent and child to communicate is via a shared service, you have the parent provide the service in it's providers array and then the same instance of the service will be dependency injected into all the child components.
The second method is for the child component the ask for the instance of the parent component in it's dependency list of it's constructor.
Either way you expose methods from either the service or the parent component and observables, usually a behavior subject that can be subscribed to.
If you don't know what I mean by observables or behavior subjects then you are not ready to be learning Angular just yet. If so pause you Angular journey for a day or two and come back after you have a sound understanding of the fundamentals of RxJs as Angular is heavily built upon RxJs. Go read up on Subjects, Behavior Subjects and the RxJs operators.

Why use props in react if you could always use state data?

I understand that there's two ways to pass components data: props and state. But why would one need a prop over a state? It seems like the state object could just be used inside the component, so why pass the prop parameters in markup?
Props are set externally by a parent component. E.g.;
render() {
return <ChildComponent someProp={someValue}/>;
}
State is set internally, and often triggered by an user event within a child. E.g.;
handleUserClickedButton: () {
this.setState({
buttonClicked: true
});
},
render() {
return <button onClick={this.handleUserClickedButton}/>;
}
So, props are a way for data to go from parent to child. State is a way for data to be managed within a singular component, and possibly have changes to that data triggered by children. In effect, they represent data traveling in 2 opposite directions, and the way in which they are passed is entirely unique.
There are two ways to "pass" or access data from outside your component but state is not one of them.
The two ways are:
Props - which a parent component pass down to the child component.
Context - which you can "skip" the direct parent in the tree.
The state is an internal object which no other component has access to it unless you pass it explicitly (via the two ways mentioned above).
So basically your question is not accurate as you can't really compare the two.
I think what you are really asking is why using a state-less instead of a state-full component.
Which you can find an answer here in Stack-overflow or in other websites.
Edit
A followup to some of your comments.
why does the child not just have a shared state? for example, each
component (or sub-component) could just do a "this.state" to get the
current state of the program
The same way you can't share or access private objects in other
functions.
This is by design, you share things explicitly and you will pass
only what the component needs. For example, look it this page of
stack-overflow, lets say the voting buttons are components, why
would i pass them the whole state if it only needs the vote count
and 2 onClick event listeners? Should i pass the current logged in
user or maybe the entire answers rendered in this page?
so you can't pass state between a parent to child? for example, can't
the parent change the state and then the child gets the new state
This is exactly what the props or context should do, provide an API for sharing data between parents and children though we keep it in a one way data flow, from parents to children, you can't pass props upwards. but you invoke handlers passed down to your child components and pass data through that handler.

Angular2 directive wait for component to be initialised

I've got an wrapper component loading multiple components by using createComponent and in every component i want to append an canvas element. Now i figured there are a few ways of doing so, and with every way of doing i get kind of stuck.
Number 1
In the wrapper component access the child component (which i've not been able to do because nativeElement in this.cmpRef = this.target.createComponent(factory); is a private property), and i need to know the child component is initialised (so i'll have to use a service for this).
Number 2
Create a directive and place the canvas element with in the loaded component: <canvas projectCanvas class="overlay"></canvas> Now i'm already able to access the template through the directive itself. (Which is great but kind of repetitive).
Only thing is the directive is constructed before the component's view is initialised, so i have to create an service that will keep track of the components status.
Actual question
How would you approach this? In my point of view there's no escaping a service, or is there a way to check if the view is initialised from the wrapper component?
---------------Update------------------
Tried to make a version with a service but ngAfterViewInit / ngOnInit seems not to work when the component is loaded by createComponent see Pliunkr
How did you try ngAfterViewInit ?
Did you try injecting the children components in the wrapper like so?
#Component({...})
export class WrapperComponent implements AfterViewInit{
#ViewChildren(InnerComponent) innerComponents : QueryList<InnerComponent>;
ngAfterViewInit(){
this.innerComponents.forEach(c => c.doSomething());
}
}

How can a parent component communicate with a child component in Vue.js?

This is what I have:
<div id='vnav-container'>
<input type="text" v-model="searchTerm" v-on:keyup="search" class="vnav-input">
<menu :items="menu"></menu>
</div>
The outer component contains a search-input and a menu component.
When the user performs a search on the outer component, I need to call a method on the menu component, or emit an event, or whatever, as long as I can communicate to the menu component saying it should filter itself based on the new criteria.
I've read somewhere that calling methods on child components is discouraged and that I should use events. I'm looking at the docs right now, but I can only see an example of a child talking to a parent, not the other way around.
How can I communicate to the menu component as the search criteria changes?
EDIT
According to some blog posts, there used to be a $broadcast method intended to talk to child components but the documentation about that just vanished. This used to be the URL: http://vuejs.org/api/#vm-broadcast
The convention is "props down, events up". Data flows from parents to child components via props, so you could add a prop to the menu, maybe:
<menu :items="menu" :searchTerm="searchTerm"></menu>
The filtering system (I'm guessing it's a computed?) would be based on searchTerm, and would update whenever it changed.
When a system of components becomes large, passing the data through many layers of components can be cumbersome, and some sort of central store is generally used.
Yes, $broadcast was deprecated in 2.x. See the Migration guide for some ideas on replacing the functionality (which includes event hubs or Vuex).
Or you can create the kind of simple store for that.
First off, let's create the new file called searchStore.js it would just VanillaJS Object
export default {
searchStore: {
searchTerm: ''
}
}
And then in files where you are using this store you have to import it
import Store from '../storedir/searchStore'
And then in your component, where you want to filter data, you should, create new data object
data() {
return {
shared: Store.searchStore
}
}
About methods - you could put method in your store, like this
doFilter(param) {
// Do some logic here
}
And then again in your component, you can call it like this
methods: {
search() {
Store.doFilter(param)
}
}
And you are right $broadcast and $dispatch are deprecated in VueJS 2.0

Categories

Resources