Angular 2 access component variable from service, that is bound to another service, which is bound to component - variables

Question is simple: how do I access variable from a service without passing down that components variable through function itself?
I don't want to pass it through function, because I want to access the variable located in AppComponent called thingies from tabs.service BUT called from sitetree.service. Function f_in_sitetreeService is gona be called somewhere at some time, dynamically (in plunker I call it from appcomponent, but in reality it will be from another place).
In other words, I have a service which is calling another service function, which in turn accesses it's components variable.
What I want:
In tabs.service I commented out 2 lines, which pushes a new thingy, and then logs the array. Commented them out because I don't know how to correctly declare/access that components array there, and that's what I need to know.
Plunker:
http://plnkr.co/edit/rFSW1UXz7Gw1rMXlD2Md?p=preview

I'm not sure I understand your question, but I'll try answering...
It sounds like you want TabsService to manipulate a property defined on some component (in your example, the AppComponent). First comment: this is likely not a good approach. Normally services should own application data, not components.
However, if you really need/want to do that, you have to somehow pass a reference to the array to your service, say in ngOnInit in the AppComponent:
ngOnInit() {
this.tabsService.setArrayReference(this.thingies);
}
Then in TabsService:
setArrayReference(ref) { this.thingies = ref; }
Since we're dealing with a JavaScript reference type, both the AppComponent and the TabsService will now both reference the same/one array.
Now you can access and manipulate the array from the AppComponent and the TabsService.
Updated plunker.

Related

Issues with data method & context within Vue

I am getting some strange behavior in my Vue application that I am developing.
In my view I define my data initially:
...
data() {
return {
organization: {
selectedOption: null,
options: [],
},
};
},
...
Intention is to populate this via a call to my backend API, which I do using => notation via axios:
// The following snippet is in my methods:
...
axios.get('http://localhost:8000/api/org/types')
.then((response) => {
Object.keys(response.data).forEach((k) => {
this.organization.options.push({
value: k,
text: response.data[k],
});
});
this.organization.selectedOption = this.organization.options[0].value;
});
...
The data comes in, and I can see it indeed does set the values until I go elsewhere within the view.
I initially called the method above in the beforeMount() method however I moved it to the created() method (due to data context/reactivity matters) and all seemed to be working just fine.
Now I am having an issue where when accessing the data where it is always seemingly set to initial data I have defined. I am verifying this via debug/console.
Within mounted():
console.log(this.organization); // Returns observer that shows the data I would expect to be there via Console, but initial data when accessing anything.
console.log(this.organization.selectedOption); // Returns null
Is there something I am not understanding how the Vue data method works? I was under the assumption that after the context has been created the underlying data can then be mutated for the life-cycle of that view.
EDIT:
I did attempt to return the promise on the axios call, but to no avail.
There are a couple of keys things to note here.
Firstly, when you log an object to the console it is live. You'll probably see a little blue 'i' icon after you expand the object that explains this. What this means is that the object properties are not copied. Instead the console just has a reference to the object. It only grabs the property values when you click on the object in the console to expand it. You can work around this by logging out console.log(JSON.stringify(this.organization)) instead.
The second point to note is that it really doesn't matter which hook you use to load the data. The hooks beforeCreate, created, beforeMount and mounted will all run synchronously at the relevant stages. Meanwhile, your data is being loaded asynchronously. Vue won't wait for it, there's no support for that. No matter which hook you use the data won't be loaded until after the initial rendering/mounting is complete. This is a common problem and you just need to write your component in such a way that it can cope with the data being missing when it first renders.
To be clear, I'm not saying that the hooks are interchangeable in general. They most definitely aren't. It's just that when you're loading data using an AJAX request it doesn't make any real difference which you use. The AJAX request will always come back after all of those hooks have been run. So performing the request in an earlier hook won't make the data available in the later hooks.
A common alternative is to load data in a parent component and only create the child once the data is loaded. That's usually implemented using a v-if and the data is then passed using a prop. In that scenario the child doesn't have to deal with the data being missing.

Vue and Vuex state checking

I am experimenting with Vue and VueX and while everything is working well, there is one aspect that is troubling with regards to the store mechanism.
I have a component that loads a set of data from a remote service via axios. It works correctly and is called when the component is created.
export default {
created() {
this.$store.dispatch('foo/getBar');
}
...
}
This correctly populates the "bar" variable in the component with the valeus returned from the api call.
When I next view the component in the application, the created function is called again and the api called again, which returns the same data.
What is the best practice way of avoiding subsequent calls until we know that there is different data to be collected? Or more precisely, how do I invalidate data in the store when necessary so that api call is made only when it needs it?
You can put your api call to the parent or root component then place a refresh button to the child component.
Or you can check if variable bar is empty then make the api call.

Use of _self attribute from Vue vm is reliable?

I need pass the main component to a deeper child. Currently I am doing on child this.$parent.$parent, but is not reliable for me, because this same child could be in a third+ deepth level.
My idea is transfer the main component reference between childs, like:
<v-some-child :main-component="_self"></v-some-child>
So I can use the main component instance where I think need by passing the _self or this.mainComponent data to a more inner child.
My doubt is:
_self attribute is reliable to points to component Vue instance? Seems that _ prefix is to internal attributes, and $ for public (reliable). If it is true, what I can do use?
There are some better way to do what I wants?
Thanks!
Edit 1: as an immediate more reliable workaround, I am using data() { self: this }, then I am using :main-component="self".
I'm unclear where _self is getting set in your case. Typically _self = this; is an approach used by developers to pass the the current Vue instance via closure to a callback function. I wouldn't trust a _self unless I knew exactly where it was getting set.
Your edit1 should absolutely work. As long as data(){ self: this } is happening on the main Vue instance and in the components you use self when setting a property via a : like :main-component="self" then that should work fine given that the main Vue component data properties are merged into all components used by that vue instance.
It's kinda a cleaver approach frankly. As #raina77ow mentioned it may make sense to look at VueX or an EventBus but the approach you propose in Edit1 should be a reliable way to pass around the main Vue instance.
As an aside, I wonder if root or main might be a better name then self. Given that it's the "self" of the main or root level Vue instance not the the "self" of the child passing the value.

Reacting to changes within observed object's unobserved properties

I have an external library with TypeScript class instances in observed variable of my app's store:
class Store:
#observable active_obj:ExternalClass;
This instance has a method which, when called, updates its internal state leaving the object as is:
store.active_obj.change()
Now, since Mobx is not observing this object itself nor its (I assume) properties, only changes happening directly on the field active_obj (like new value assigned to it). Since instances of MyClass are provided by external library and ultimately get rendered inside this library's components I can't add observables and observers to its class structure nor React components. Mind you, this is only what I think is the reason that changing the object's properties doesn't trigger re-render...
I had to cheat a bit by using other, observed variable I change directly with nonsense data at the same time I'm calling to unobserved instance for change. Adding references to this variable on components higher up the tree, I can trigger re-render that produces updates on the (unobserving) components of the external library.
My question is, how best to make Mobx aware of change so it can notify observers of store.active_obj instance?
I think this part of Mobx documentation warns about this, but there's no workarounds or solutions for it:
** If likes where objects instead of strings, and if they were rendered by their own Like component, the Likes component would not rerender for changes happening inside a specific like.
from here, at end of the page
Edit
As per #mweststrate's question, I provide some context:
My app controls its data, but it has to create external class' instances from that
Instance's data is encapsulated and mutated in place, but it's done by asking from my app's side through user triggered events (meaning, I know when data is updated)
Basically class uses app's data to provide different views into data based on user selection and renders it with its React components
I also use this changed data elsewhere in the app in components I control
Changed data is part of external class' internals and I can't depend on it
Since Mobx tracks mutations it can see, using Observable doesn't directly work
Some possible solutions I thought:
manually notify observers that observable active_object has changed when I have called the instance it references to change
have a container object that Mobx can track and when I change its sentinel property, that update is noticed and actual instance with it

How to access a component bound to a property in QML

It's come up several times that I have wanted to access a property of a component which is bound to another property. I've spent days trying to figure out how to do this and failed. Below is a simple example of what I'm trying to do.
TabView {
Component.onCompleted: console.log(style.frameOverlap)
// OR tvStyle.frameOverlap
style: TabViewStyle {
id: tvStyle
frameOverlap: 5
}
}
Nothing like this works. I'm completely baffled about how to access these members either statically or as an instance. Can someone please explain to me whether something like this is possible?
Thanks.
The short answer is that you need to write:
Component.onCompleted: console.log(__styleItem.frameOverlap)
The longer answer is that the 'style' property is a Component. Component is something that remembers a tree of declarations, and can create objects as needed. However, it does not expose that remembered declaration, so when you try to access the frameOverlap property, it's not there.
In theory, you can call style.createObject to create an object, and examine its properties, but that would create another unnecessary instance, so you can look at TabView.qml, notice that it creates an instance already using Loader, and stores that in a property called __styleItem, and so use the code I gave above.
Of course, accessing internal properties is not a particularly good idea, but might be OK in practice. Ideally, one should be able to instantiate TabViewStyle and bind the instance to the style property, with TabView figuring out whether it's Component or object, but I'm not sure it's possible.