Angular2 directive wait for component to be initialised - javascript

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());
}
}

Related

Vue how to access props of parent component - the importer not a wrapper

Overall goal: from a child Vue component, get access to the component that it is imported and used within - which is not always the same as $parent. This should be done by only making changes within the child component (if possible).
We have PinButton Vue component meant to add a "pinning" functionality to other components. This component is used within many other components and we want to be able to access the parent props so they can be saved and rendered on a different page of "pinned content" by passing those props back into the parent component.
Note: I know this would be possible by manually passing the parent props down into the component (<pin-button :parent-props="$props" />), but we're trying to avoid having to do this every time the component is used.
A minimal reproduction of this with a single parent and child component will show that you can access parent props using $parent.$props. However, when the child component is nested as slot content of some other component within the parent, then the child will get the props of the wrapper component - not the component in which it is imported and actually used.
Sandbox reproduction - I want to get the props for ParentComponent from within ChildComponent. The expected value is shown by passing the props along (what I'm trying to avoid) and the actual value is the props of the SlotWrapper component, which doesn't import ChildComponent so I wouldn't consider it the true parent, but it is the direct parent element in the <template>
Update:
Seems like the suggested solution for "arbitrarily deep" access is provide/inject, but this would still seem to require changing all components that use the <pin-button />
To answer your question directly, you can access ParentComponent from ChildComponent via the "context" the component is rendered within:
// ChildComponent.vue
computed: {
expectedProps() {
return this.$vnode.context.$props
}
}
But this might be an "XY" kind of problem; I'm sure there's a better design solution for what you're trying to achieve.

How to get Vue app container div attribute

I have a page which is being generated by my app in java (SSR) depending on some data (e.g. a publisher for some entity). I would like to create some sort of a reusable Vue component that would call an API method and request some data about the entity that is currently opened. Also in some cases there could be more than one such component on one page.
The only thing I cannot really figure out being a most-of-the-time backend developer - is how to tell a component which entity I'm trying to get. The only solution that comes to my mind is to generate the parent <div class="my-vue-component"><div> with an additional attribute, e.g. <div class="my-vue-component" publisher-id="123"><div>.
But I cannot find if there is a way to access that attribute from inside the Vue instance. (Please note that I don't have a fixed id for this div as there can be many such components on the same page referring to different data).
Any kind of advice is appreciated.
As stated in the previous answer, you will need to use props. Although since you will pass down data to multiple components and the data can change, there should be a way to respond to those changes.
For that, you will have to bind the prop with a reactive variable in your page/parent component.
So your SSR code should look like
<my-vue-component :publisher-id="openId"></blog-post>
And inside your page/parent component will reside the openId, which you can change as needed, and your component will re-render if prop passed to it changes.
export default {
data(){
return {
openId:1
}
}
}
It seems like you are looking for components-props.
You can define a prop like
Vue.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
Your SSR code should then be generating:
<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>
Inside the component methods you can access the passed in value using this.postTitle

How to pass data from parrent constructor to child component in angular 2

I have a angular 2 page where i need to show 2 different components using same array of data from external API. Parent is regular component, and child is shared among several other components using same functionality.
In parent component class i have output property declared:
public weatherList: WeatherForecast[];
#Output() public weatherListData: any;
Inside constructor of parent component, i populate weatherListData property with data from an external API
http.get(url)
.subscribe(result => {
this.weatherList= result.json() as WeatherForecast[];
this.weatherListData = this.weatherList;
});
and i'm using it inside parent template with success, something like: {{ weatherList.someValue }}
Also, inside parent component template, i have a call to a child component
<daily-temperature-chart [weatherListData]='weatherListData'></daily-temperature-chart>
In child component class i have declared property
#Input() weatherListData: any;
but, when i try to access weatherListData property in constructor, or init of child component, i get undefined result.
EDIT: I have played with console.log() and noticed that child component Constructor and OnInit() methods return before http.get() from parent component. Maybe this is problem, but i'm still new to angular and can't tell.
Can someone point me how to solve this?
You've a service call so you can't go for constructor or OnInit because component initialization is not dependent on your service call for this situation angular provides OnChanges whenever your input value is updated OnChanges fired.
ngOnChanges(changes: any){
console.log(changes);
console.log(this.weatherListData);
}
OnChanges passes as well an argument which informs about the current state and pervious state now you are able to use input values. If your components are bases on input and input is based on any other operation you can handle it in this block.

Angular 2 Child1-Parent-Child2 event propagating

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?

How to use custom functions in a React class after it was rendered?

Let's say that I create additional functions in my React components, something like this:
class Cart extends React.Component {
render() {
return <div id="cart">My Cart Items</div>
}
getItem(id) {
// magically return the item
}
addItem(id, name, type, price){
// you get the idea
}
}
Now, what is the best way to access getItem function from outside the class? For example, something that can be used as window.Cart.getItem. It seems to me that these functions are part of the prototype (non-initialized) of nodeName property of what ReactDOM.render returns.
What is the correct way for this? Thanks.
That's not really how react is supposed to be used.
What you are trying to do looks like MVC, and react is a completely different thing.
a React component like your <Cart> receives props from its parent (could be the root reactDOM.render() or another react component)
a React component may have internal and private method to retrieve additional data from elsewhere (but NOT from other react components)
with these inputs (and only these inputs), the component knows what and how to render itself and possibly also children components to the DOM
the stuff the component (or its children components) render may include interaction handlers (e.g. remove from cart button like `
these interactions could fire an internal method inside the component
and the internal method could call a method in a parent component, if it was passed down as a prop.
or call some remote function to remove item from cart on server side.
In react terms, the <Cart> component is not the owner of the cart contents. It gets them as props, or retrieves them from somewhere else.
React really wants you to ONLY update the cart by passing a new set of props to the component to render.
You could try to shortcut react design by trying to expose any methods from the component to the outside world, but this goes against react design principles.
React has a one way data flow: a component gets props, and renders.
For reference, I can recommend this page on react principles.
You can make a reference to the instance :)
<Cart ref={function(instance){ window.CartInstance = instance }} />
window.CartInstance.getItem(foo);
ES6
<Cart ref={(instance)=>window.CartInstance = instance} />
window.CartInstance.getItem(foo);

Categories

Resources