StencilJS: Can #Watch be used on #State - stenciljs

Is the following code valid ?
#State() state;
#Watch('state')
stateHandler(s) {
...
}
From what I can read on the doc, it doesn't mention it. However, the #Watch does get triggered in my example, so I does assume it does work ?

The code is valid. When a component's Prop or State property changes, the #Watch decorator will fire the method it's attached to. It is true that this is not well specified in the documentation.

Related

Vue mutate prop correctly

I'm trying to create a simple component whose focus is to display an element in an array, but I'm having issues with Vue's philosophy.
As you may know, if a mutation on a prop is triggered, Vue goes crazy because it doesn't want you to update the value of a prop. You should probably use a store, or emit an event.
The issue is: that since I'm adding functionalities to my codebase (for instance the possibility to start again when I reach the last element of the array), it would be wrong to have an upper component be responsible for this management, as it would be wrong to ask an upper component to change their variable, given that my component is supposed to manage the array, so an emit would be a bad solution.
In the same way, given that I'm making a generic component that can be used multiple times on a page, it would be incorrect to bind it to a store.
EDIT: the reason why the prop needs to be updated is that the component is basically acting as a <select>
Am I missing an obvious way to set this up?
To give an example of my end goal, I'm aiming for a component looking like the one in the picture below, and I think a 2 way bind like in v-model would be more appropriate than having to set an #change just to say to update the value of the passed prop.
If you have a prop the correct way to update the value is with a sync, as in the following example
Parent:
<my-component :title.sync="myTitle"></my-component>
Child:
this.$emit("update:title", this.newValue)
Here is a very good article talking about the sync method.
By the other hand you can alter a Vuex state variable by calling a Vuex mutation when you change the value:
computed: {
title: {
// getter
get() {
return this.$store.state.title
},
// setter
set(newValue) {
this.setTitle(newValue) // Requires mutation import, see the methods section.
// Or without import:
this.$store.commit('setTitle', newValue);
}
}
},
methods: {
...mapMutations("global", ["setTitle"]) // It is important to import the mutation called in the computed section
}
In this StackOverflow question they talk about changing state from computed hook in Vue. I hope it works for you.

How could i get event type in vue when watch listener triggered?

I have created a wacther and when <Input v-model="computedData" /> changes the data, I can get the old and new values. Data can also be changed via XMLHttpRequest. I need to know who changed the data. I can't get event as parameter via watcher when data changes. Because there is no argument to get to event on whatcher. I know, I can access the event directly using event. But I also know it's deprecated. So I'm researching how the event type can be accessed as InputEvent nor XMLHttpRequest.
#Options({
name: 'dx-table',
watch: {
computedData: {
handler(newData: any, oldData: any) {
console.log(event); // is there any way to access `event` without using `event` directly
},
deep: true,
immediate: true,
},
},
})
export default class DxTable extends Vue.with(Props) {}
There is no way to get the event or cause of the data change in a watcher. A watcher is simply a function that executes whenever some reactive property changes and all you are given is the old value and the new value.
Based on the information given, there's two ways the data can be changed:
input event: Register a listener for the input event on the component, like #input="handleInput". The event object is passed to the function.
XMLHttpRequest: Wherever you are changing the property in code, just call a method to handle that specific mutation.
I don't know specifics about your code, but this might be a situation where instead of mutating the data freely throughout your code, use one or more "setter" methods to do this so that you know exactly where and how the data is being mutated. Using a watcher gives you no information about where or how the data changed, and if you're mutating the data in many random places in your codebase then you're going to have a difficult time trying to trace through your code to find the cause of the mutation.

How does the next() guard function in vue-router work?

I'm trying to understand the solution in this SO post. The solution allows the user to keep track of the previous route in the current route.
Below is the snippet of Vue code that I'm trying to understand. If I understand correctly, next accepts a callback function that receives the current component's vue instance. We then set the prevRoute data property of this vue instance to from. Is this interpretation correct? If not, what is actually happening?
If someone could also add a brief explanation as to what the Vue API is doing behind the scenes that would also be very helpful for me to actually understand the snippet of code.
...
data() {
return {
...
prevRoute: null
}
},
beforeRouteEnter(to, from, next) {
next(vm => {
vm.prevRoute = from
})
},
...
As per the documentation...
The beforeRouteEnter guard does NOT have access to this, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
However, you can access the instance by passing a callback to next. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument
So vm is the component instance assigned to the destination route.
From your question...
We then set the prevRoute data property of this vue instance to from. Is this interpretation correct?
Almost. All you're doing is setting a direct object property on the Vue component which is after all, just a JavaScript object at heart. For example
const vm = { name: 'I am totally a Vue component' }
vm.prevRoute = from
This property will not be reactive but you can certainly access it within your component via this, just as you can other non-data properties like $el, $refs, etc.

how blur or focus vuejs component based on a vuex state

I've got a little problem with vuejs.
My state is basically something like this.
state: ()=>({
activeSide : "from",
}),
I want a component to be focused or blurred based on whether activeSide has the value activeSide set to "from" or not
my idea at the moment, doesn't seem to be very elegant, I basically created a computed property in my component,
focusSide(){
console.log("changed active side")
this.$store.state.activeSide
}
and then I set up a watch to see if that property changes.
watch:{
focusSide : function(newV,_){
console.log("changing focus")
newV=="from" ? this.$refs.fromArea.$el.focus() : this.$refs.fromArea.blur()
}
},
the problems here is that apart from the fact that the solution doesn't look elegant the watch doesn't work either, I've seen that focusSide is changing its value correctly (or at least the body of the method is executed), but the watcher is not executed, I have the feeling that since focusSide state is never used in my template, vuejs thinks that it's not necessary to react and change values, something like reactive frameworks where if the value is not observated then don't change (maybe I'm wrong)
what would be the ideal way for achieve this???
thank you
You need return value of computed properties focusSide, otherwise it will always return undefined
focusSide () {
console.log("changed active side")
return this.$store.state.activeSide
}

Angular 2/4 Emitted Event Not Received?

(Subtitle: I think my code is haunted...)
I have a parent component with 2 children: Notes and Location. Location is emitting changes and they are received by the parent. That is working fine. Notes is emitting, but apparently it is never received by the parent.
Location input property:
#Input() public FactLocation :LocationInfo ; //object initialized later
Notes input property:
#Input() public FactNotes : string = "start";
Location HTML:
<location-field [(FieldLocation)]="FactLocation" ></location-field>
Notes HTML:
<notes-field [(FieldNotes)]="FactNotes"></notes-field>
Location components input/output:
#Input() FieldLocation: LocationInfo;
#Output() locationEmitter: EventEmitter<LocationInfo> = new EventEmitter<LocationInfo>();
Notes component input/output:
#Input() FieldNotes :string;
#Output() notesEmitter: EventEmitter<string> = new EventEmitter<string>();
Location emitter statement:
this.locationEmitter.emit(this.FieldLocation);
Notes emitter call:
this.notesEmitter.emit(this.FieldNotes);
When I change the value of the FieldLocation property in my Location child component, the value is reflected in the parent component. When I change the value of the FieldNotes property in the Notes child component, it is changed locally, but never changed in the parent.
Here are the methods that should be called when an event is emitted from the child components:
public FactLocationChange(evt :LocationInfo){
console.log(evt);
this.FactLocation = evt;
}
public FactNotesChange(evt :string){
console.log(evt);
this.FactNotes = evt;
}
As near as I can tell, everything is identical between these two, except for the property names and the fact that the Location emitter is signalled in response to a KeyDown event in the child, and the Notes emitter is on a timer (interval).
What am I missing?
Thanks,
Dave
PS: Here's the "haunted" part of the code...the two "Change" methods above that should be called when their event is emitted are actually both commented out, but Location still works. I noticed this while debugging - putting a "debugger" statement inside the FactLocationChange method made no difference, even though Location in the parent was being updated. The debugger never kicked in.
I've tried with the methods commented, uncommented, rebooted, deleted all JS files and had TSC re-generate them, all with no change. I'm baffled.
Your code is not haunted ;)
Actually even though you think that the other is emitting, it is in fact not.
Objects are mutable, therefore the parent will get the value "automatically" for your object FactLocation. That's why the parent gets updated even though you have commented the emitting out, like you mentioned at the end of your question. When passing an object like this, you are actually passing a reference of the object in your parent, that is why the change affects the parent.
Primitive types, like your string is not mutable, therefore it's not emitting changes the parent like your object does.
You are mixing the two-way-binding with "regular" Output one-way-binding, therefore it's not emitting. If you want to have two-way-binding, as it seems you want, you need to add the suffix Change in your emitter, which also needs to have the same prefix (name) as your input. So it should be:
#Output() FieldNotesChange: EventEmitter<string> = new EventEmitter<string>();
and then emit:
public FactNotesChange(evt :string){
this.FiedNotesChange.emit(evt)
}