Suppose I have the following component Child.
#Component({...})
class Child {
#Input() mySpecialFlag: boolean;
}
Child can either be used as a stand-alone component, or can be wrapped by a Special component, which "scopes" the behavior of Child. When Child is used stand-alone, the user can choose to either set [mySpecialFlag] to true or false. When Child is used within Special, I want the Special component to force the input [mySpecialFlag] to be true, via something like the following:
#Component({...})
class Special {
#ContentChild(Child) child;
...
ngAfterContentInit() {
this.child.mySpecialFlag = true;
}
}
The issue with the above is that it sets [mySpecialFlag] too late. I want to be able to set this flag to true before any of the child's lifecycle methods have run, or at least before the child's ngAfterContentInit has ran. Is this possible?
Setting the flag in ngOnInit of the parent appears to work: the flag is set before ngOnInit of the child component.
ngOnInit() {
this.child.mySpecialFlag = true;
}
See this stackblitz for a demo.
An alternative method to access the child component as early as possible is to associate the #ContentChild decorator to a getter/setter property, and to set the child flag in the setter:
export class ParentComponent {
private _child: ChildComponent;
#ContentChild(ChildComponent) get child(): ChildComponent {
return this._child;
}
set child(val: ChildComponent) {
this._child = val;
this._child.mySpecialFlag = true;
}
}
See this stackblitz for a demo. Please note that the private variable _child and the getter are not needed to set the flag. I added them in case you need to refer to the child component for other purposes.
I believe you could use ngOnInit method. Refer to this link explaining angular Lifecycle hooks: https://angular.io/guide/lifecycle-hooks
Related
Not very experienced with Vue, and am struggling to fix an issue with an existing component made by a previous developer.
On the parent page, the stepper component is declared like this, with these as the names of the sub-components that make up the individual steps:
#Component({
name: 'switch-stepper',
components: {
Stepper,
StepIndicator,
FirstStep,
SecondStep,
ThirdStep
}
})
There's a property in ThirdStep that needs to be changed, either in the parent page or in FirstStep. The property in question is public, and declared like this:
#Prop({ default: true })
public myBooleanProperty: boolean;
However, inside the parent page, if I try this:
ThirdStep.myBooleanProperty
It is not recognised by the intellisense and is undefined. I've tried also creating a public method on ThirdStep that I can call to use ForceUpdate but public methods likewise seem to be inaccessible.
I've also tried setting the public property via a function in the parent page when the step is created:
<third-step :page="page"
:step="steps[4]"
:myBooleanProperty="setMyBooleanProperty()"
v-show="currentStep === steps[4]">
</third-step>
But as far as I can tell this is only called once when the step is created and never accessed again.
What can I do to set the property of this child step via other components in the stepper?
There's a property in ThirdStep that needs to be changed, either in the parent page or in FirstStep. The property in question is public,
and declared like this:
If a property needs to be changed, it should be changed in the component which returns the property in the data property. By this, I mean, the component that has
data() {
return {
myBooleanProperty: false // or true. This is the local data that will be initialised and passed as props to the ThirdStep component
}
}
If this is in the parent page, change ThirdStep.myBooleanProperty to this.myBooleanProperty = *enter value*. The change of the value of myBooleanProperty can be done in a method, watcher, computed property etc. The reason ThirdStep.myBooleanProperty is not working in the parent component, is that each vue component is a Vue instance and ThirdStep cannot have access to an instance property in the parent component.
ParentComponent.vue
#Component({
name: 'switch-stepper',
components: {
Stepper,
StepIndicator,
FirstStep,
SecondStep,
ThirdStep
}
})
<third-step>
...
:myBooleanProperty="myBooleanProperty" // my boolean property is passed as props from the parent component, I assume
....>
</third-step>
When myBooleanProperty is changed where it is initialised (parent component, I assume), this will cause the value of the myBooleanProperty props, passed into the ThirdStep component, to change and there will be a re-render of the parts of the ThirdStep component that use myBooleanProperty props.
As we know , in angular parent to child communication happened using #Input decorator but how to pass dynamic data from parent to child.for example 'name' property is defined in parent and it's value changes dynamically and updated value of 'name' we are going to using in child component so how to achieve it without services.
you can watch your input changes in the child component with ngOnChanges lifecycle
in your child component you should implements OnChanges
ngOnChanges(changes: SimpleChanges): void {
for (let propName in changes) {
let change = changes[propName];
if (propName === "YOUR_INPUT_NAME") {
this.YOUR_INPUT_NAME = (change.currentValue);
}
}
}
You can make the use of service to get the updated value in child when it gets changed in parent.
For that you need to declare the variable in the service of type observable.
your myService.service.ts would be having the following code:
#Injectable()
export class myService {
myInput:BehaviorSubject<boolean> = new BehaviorSubject(false)
}
now from your parent component change the value of this myInput by using:
myServie.myInput.next(true)
and subscribe this variable in your child component by using:
myService.myInput.subscribe((data)=>{console.log(data)})
In both the parent and child component add the line private mySerivce:MyService in the arguments of constructor.
Suppose I have components Parent and Child. Child can be used either as a standalone component, or within a Parent component. However, I want Child to have different behavior based upon where it lives.
Suppose Child has an optional #Input [isHappy], which is either true or false. However, whenever a Child component is hosted by a Parent component, suppose isHappy must always be true.
AFAICT there are two ways of doing this:
1) User must just know to always specify [isHappy]="true" whenever a Child is hosted by a Parent.
<parent>
<child [isHappy]="true"></child>
</parent>
2) Parent manually sets this.child.isHappy = true within its ngOnInit lifecycle hook.
Which approach is preferred? In my opinion, approach #2 makes more sense, users don't have to know to set [isHappy]="true" when a Child is hosted by a Parent. On the other hand, I'm aware that it's frowned upon in Angular for components to programmatically change one another, especially if all components are OnPush (please correct me if I'm wrong here).
I want Child to have different behavior based upon where it lives.
How about letting Child component know where it lives with ElementRef:
export class ChildComponent {
hasParent: boolean;
constructor (
private elRef: ElementRef
) {}
ngOnInit() {
const el = this.elRef.nativeElement.parentElement as HTMLElement
this.hasParent = el.localName === 'app-parent'
}
}
stackblitz: https://stackblitz.com/edit/angular-kagdsu
In my opinion 2nd way would work, but that could add couple of things into your solution
Tight coupling between two component
What if parent component don't have child component?
To solve it by better way, I'd suggest you to use Host decorator, that will ask for Parent component dependency from Child component. If that exists the make isHappy property to be true
#Component({...})
export class Child {
#Input() isHappy: boolean = false;
constructor(#Optional() #Host() private parent: Parent) {}
ngOnInit() {
// Do only if parent exists
if (this.parent) {
this.parent.isHappy = true
}
}
}
I understand even the way suggested above has tight coupling between the Parent component. We should think of removing that dependency from the inner child component.
Yes, we can do that by little hackish way, where we would be checking current component's immediate parent component name like below. For achieving the same you had to add ViewContainerRef dependency to get hold of parent/host component.
constructor(private viewContainer: ViewContainerRef) {}
ngOnInit() {
if(this.viewContainer[ '_data' ].componentView.parent.component.constructor.name === 'Parent') {
this.parent.isHappy = true
}
}
I have simple questions:
Is it possible to modify a prop (not state) within a React Native component (not from parent component)?
Following [1], if I want to modify a prop within the same component, how can I achieve that (or workarounds if answer in [1] is No)?
If I have the following:
//Parent
render(){
return <View><ChildComponent propA={this.state.propA} /></View>
}
triggerChangeInParent(val) {
this.setState({
propA: val
});
}
//Child (ChildComponent)
render() {
return <View><Text>{this.props.propA}</Text></View>
}
triggerChangeInChild(val) {
//To set props.propA here
}
Both parent and child components are allowed to modify "propA". Whoever triggers the latest will have its value taken precedence for modifying "propA" (eg if "triggerChangeInChild" is triggered after "triggerChangeInParent", then "val" from "triggerChangeInChild" will be used for "propA".
My question is, how do we achieve that (what possible solutions/alternatives to solve this problem)? What is the best practice/pattern?
Thanks!
State is mutable and props is immutable, so you can't modify the props within component. Please do it outside the current component. If you're using redux, you can do it in a reducer.
You should modify the propA on parent only, please write a method in parent, then pass it to child via prop. So you can call the method from child, mean that you make the change inside child.
Parent
contractor(props){
super(props)
this.modifyPropsOnParent = this.modifyPropsOnParent.bind(this)
}
modifyPropsOnParent(){
// do your change here
}
render() {
return <Child modifyPropsOnParent={this.modifyPropsOnParent} otherProp={yourProps} />
}
Child can call the parent method by this.props. modifyPropsOnParent()
Consider the following snippet of Parent's template:
<div *ngFor= "let event of events" >
<event-thumbnail [theEvent] = 'event'></event-thumbnail>
</div>
Also event-thumbnail component definition is:
export class EventThumbnailComponent{
intoroduceYourself(){
console.log('I am X');
}
}
In Parent component class, I want to iterate over all generated event-thumbnail elements, access the component beneath each, and call introduceYourself function on single one of them.
You want to use the #ViewChildren() decorator to get a list of all instances of a specific component type within the view:
class ParentComponent implements AfterViewInit {
#ViewChildren(EventThumbnailComponent)
eventThumbnails: QueryList<EventThumbnailComponent>;
ngAfterViewInit(): void {
// Loop over your components and call the method on each one
this.eventThumbnails.forEach(component => component.introduceYourself());
// You can also subscribe to changes...
this.eventThumbnails.changes.subscribe(r => {
// Do something when the QueryList changes
});
}
}
The eventThumbnails property will be updated whenever an instance of this component is added to or removed from the view. Notice the eventThumbnails is not set until ngAfterViewInit.
See the docs here for more information:
https://angular.io/docs/ts/latest/api/core/index/ViewChildren-decorator.html
Your child component should have #Input() theEvent to get access to the event you are passing. Then you can use the following lifecycle hook:
ngOnInit(){
introduceYourself(){
console.log('I am X');
}
}