does ngOnChanges actually update input properties - javascript

I'm new to Angular, just a question on Lifecycle Hook Methods. I know that:
ngOnInit() is called after Angular has set the initial value for all the input
properties that the directive has declared.
ngOnChanges() is called when the value of an input property has changed and also just before the ngOnInit method is called.
let's say we have an input attribute like:
// custom directive class
#Input("pa-attr")
numOfProduct: number;
and
//template.html
<tr [pa-attr]="getProducts().length ...
and I click add button and add a new product, so the total number of products changes, so my question is:
in my case ngOnChanges() get called, ngOnInit() won't be called because ngOnInit() only get called in the first time the input property has been initialized with a value.
But how does the input property numOfProduct get updated after ngOnChanges() get called? Is it a special lifecycle method called ngUpdateInputProperties(the name won't match, but you get the idea) that does the job as:
ngUpdateInputProperties {
latestValue = ... //retrieve latest value
numOfProduct = latestValue ;
}
or such lifecycle method doesn't exist and the numOfProduct won't have the latest value, we can only get access to the latest value in ngOnChanges() method?

If you change any values in your component, it will change detection to recheck all the data bound values, then it will pass any updates to your child component.
You can test this yourself by placing a log entry inside if your getProducts() method, then tracking its value in the child component. You'll notice it gets called when updates happen in the component and you should see the updates in the child component.
Hope this helps.

Related

Angular 5+: Child Component Subscribed to Parent Observable Does Not Get Value On Unhide

EDIT 2: This appears to be my general problem, and solution (using setTimeout so Angular's lifecycle can happen). I'll either close this or post an answer to my own question when I can.
See EDIT for a simpler repro that doesn't involve Subjects/Observables but is essentially the same problem.
I have a parent component that's responsible for fetching data from a service.
export class ParentComponent implements OnInit {
public mySubject: Subject<Foo[]> = new Subject<Foo[]>();
public buttonClicked = false;
private currentValues: Foo[] = null;
constructor(private SomeService myService) { }
this.myService.get().subscribe(values => {
this.mySubject.next(values); // Does NOT work when a child component is hidden, as expected.
this.currentValues = values; // Keep value so we can manually fire later.
});
private buttonClickHandler() {
this.buttonClicked = true;
this.mySubject.next(this.currentValues);
}
}
This data is subscribed to in the HTML by a child component. This component is hidden by default via *ngIf, and only becomes visible on a button click:
<app-child-component [values]="mySubject.asObservable()" *ngif="buttonClicked" />
In the parent component above you see I'm trying to pass the current available data to the child by invoking next() when the component is made visible in some way:
this.mySubject.next(this.currentValues);
This does not work when initially un-hiding the component via *ngIf. If I click the button a second time, which then calls next() again, then it works as expected. But when Angular is in the current context of un-hiding something, observables aren't getting their data. (This also happens when things are unhidden by other means, but the result is the same: If in the same method, the subject/data passing does not work; the component has to already be visible as of the method call.)
I'm guessing the binding to the observable is not happening until after *ngIf shows the child component, after the method call resolves. Is there some place I can hook into that I can then pass child data down?
EDIT for clarification: I don't believe this is an issue of Subject vs. BehaviorSubject. I'm not having issue passing the data. The issue is that the data-passing (confirmed via console.log()) is not occurring at all in the first place. It's not that the child component is receiving a null value. The subscription just isn't firing to the child.
I found I can reproduce this in a simpler fashion too: Trying to select an element in the DOM of *ngIf HTML reveals undefined if I make *ngIf's value true within the same Angular method.
<div *ngIf="buttonClicked">
<div id="someElement">Foo</div>
</div>
public someMethod(): void {
this.buttonClicked = true;
const container = document.getElementById('someElement'); // DOES NOT WORK if this.buttonClicked was false at the start of this method!
}
You going to need to use a BehaviourSubject instead of Subject, which emits the previously set value initially.
What is the difference between Subject and BehaviorSubject?

Watcher not working with Vuex

I am having an issue with watchers not being triggered upon data change in my component. The property in the data is reactive, as it has been set upon component creation and not later on.
Here is the piece of code, in which the issue is:
https://codesandbox.io/s/nlpvz0y6m
To explain in more detail, the status property gets its status from the parent, which intern gets it from a Vuex state object, it is being passed to the component successfully, as I am able to log it and change it.
However, when I setup a watcher, to execute a function upon a change in it's value, it simply doesn't trigger. Regardless how I make the change - whether with an internal method of the component or an event.
What I need is for the watcher to trigger upon change of the status property, but am not certain why it does not reflect it at all.
The structure is as it follows: BottomBar is the parent, a bool value is passed as property to Spin.vue as a prop and then the prop is assigned to a data property on the child component.
The bool value itself, comes from index.js, where the Vuex instance is.
In the console, it is showing the following two errors
[vuex] unknown getter: isSpinning
[vuex] unknown mutation type: spinIt
The issue seems to be how the store is set up. Try this.
export const store = new Vuex.Store({
state: {
controls: {
spin: false
}
},
getters: {
isSpinning: state => {
return state.controls.spin;
}
},
mutations: {
spinIt(state) {
return (state.controls.spin = !state.controls.spin);
}
}
});
You had your mutations and getters sat inside your state. I have moved them outside, and updated the references inside to make the code work as expected.
https://codesandbox.io/s/8xyxmvr8jj

VueJs: How to pass a 'computed function' value from child component to the parent component using $emit(optionalPayload) and $on?

I am new to VueJs and I am doubtful about passing the optional payload
Could anyone please tell me how to pass the value returned by a computed function in the child component to the parent component using the optional payload.
I want to implement a separate independent search component which returns the search results to all other components. The computed function looks like this:
get computedSports () {
if (!this.searchModel || this.searchModel.length === 0)
return this.sports
else
return this.fuseSearch.search(this.searchModel)
}
This is how I am trying to pass the value returned by computed function to its parent component in the child template:
#input="$bus.$emit('computed-sports', computedSports)"
In the parent component, this is how I am trying to access the value of the child's computed function:
v-on:computed-sports=""
I am not quite sure how to access the value here. Could anyone help me out in this?
Thanks!
The argument to a v-on should be a method or inline function that takes the payload as an argument. For example
v-on:computed-sports="handleComputedSports"
and your method might be defined
handleComputedSports(theValue) {
console.log("The value is", theValue);
}
There's an example in this section of the documentation. Your emit is fine. The fact that the value comes from a computed makes no difference to anything.

What is the diff b/w this.name and this.state.name in react?

React has instance property and state property in react in constructor.
Instance property - Its not re-render the view. use to store the value.
State property - Its store and re-render the view.
Apart from above any other reason or difference or when should be use for both instance and state in constructor of React class component?.
Example:
class example extends Component{
constructor(){
this.state = {
name: 'albert'
};
this.name = 'albert';
}
}
When component state changes, it triggers component re-rendering (if it's not set to be ignored in shouldComponentUpdate()).
Changing instance property does not trigger re-rendering.
simple difference for both that is view part rendering.
EX: When State is update the view also update. Sometimes view is need not to reload that time we can store the value in component instance as you mentioned this.name.
Just check with below link to more about state and instance
https://medium.freecodecamp.org/where-do-i-belong-a-guide-to-saving-react-component-data-in-state-store-static-and-this-c49b335e2a00
It depends upon your requirement, which kind of data you are storing within it.
When any state variable is updated, react calls render to make changes in DOM element, so if you want to make any changes in DOM you should use state otherwise instance.
The current best practice is to use local state to handle the state of
your user interface (UI) state rather than data.
from this article
and instance properties when you just want to save some data to use in UI handling, calculations etc.
check this ref react-components-elements-and-instances for futher details
Whenever state is updated, react calls the render() method to update the DOM with required changes, and it should always be updated using setState(). The instance variable will be useful for block level manipulations which would then update the state if required. So if you want to re-render the DOM use state variables, else use instance variables.

Ember.js - Model inside component

I am making an app just for practice and i have a doubt in a component's function "didReceiveAttr". When i pass my MODEL in my template and then i erase some element in it the function doesnt work, but if i pass "model.length" in the template and then erase something the function work!
My component template
<h1>Tasks ({{totalTask}})</h1>
My component JS
totalTask: null,
didReceiveAttrs(){
this._super(...arguments);
this.set('totalTask', this.get('model.length'));
console.log(this.get('model'));
}
My primary template
{{task-list model=model}}
or
{{task-list model=model.length}}
This is indeed the expected behavior; just look at Ember guide about how didReceiveAttrs works. It is clearly stated that "didReceiveAttrs hook is called every time a component's attributes are updated". When you add to or remove from an array the array itself does not change; hence didReceiveAttrs is not executed. It is only executed when the initial assignment to model is performed.
I prepared this twiddle to illustrate you a better ember way to handle this case. You should rely on computed properties as much as you can; hence I added computedTotalTask as a computed property to my-component.js and it relies on model.length as you can see.
{{task-list modelLength=model.length}}
Here you are assigningmodel.length as modelLength property to the component. so initially didReceiveAttrs will be called as component is receiving modelLength property and when you add one more element to model then modelLength property itself changed so this will invoke didReceiveAttrs before re-render.
{{task-list modelTaskList=model}}
Here modelTaskList is pointing to array, so when you add/remove item through KVO compliant method such as pushObject it will be reflected in component too. but the modelTaskList is still pointing to the same array so didReceiveAttrs hook will not be called.
Suppose if you assigned different array then you can see the didReceiveAttrs is called.
You could always just set this as a computed property, ensuring updates in the event of the bound variable being updated.
Within your component, set up a computed property that will watch for a change to your model, then update the variable modelLength with the change
modelLength: Ember.computed('model', function(){
return this.get('model').length;
}
Then, within your handlebars template, reference this length
<h1>Tasks{{#if modelLength}} ({{modelLength}}){{/if}}</h1>

Categories