I have an ember route that calls an ember component to display all the data.
I have a case in which the route's key changes and I reload using route.transitionTo. The data is loaded fine. But the problem is the component does stuff in the init and didInsertElement methods, which on the route.transitionTo aren't triggered.
How can I make sure on transitionTo the component is called again?
I can't use route.refresh because that doesn't do the model hook.
Any help will be appreciated, Thank you!
Well, there is a harder and better way, and there is a lazy and fast way:
Better way: I suggest you to create a property in the route and pass this parameter to the component, and in the component create a computed property for that parameter, what you've sent, and there you can run your code what you called in "init" and "didInsertElement" function.
E.g.:
router.hbs
{{component-name parameter=parameter}}
Component.js
lineWidth: Ember.computed('parameter', function() {
// I will run here if I get the parameter (and the parameter changed in route)
}),
Not the best way:
You can reload the whole component like this:
router.hbs
{{#if reload}}
{{component-name}}
{{/if}}
router.js
this.set('reload', false);
Ember.run.next(() => {
this.set('reload', true);
});
Related
I have a component called button-widget and it is registered globally.
Vue.component('button-widget', {
template: `<button>My Button</button>`
})
Now, how can I delete this component permanently?
I am not talking about v-if or $destroy() -- I just want to completely remove this component in such a way that it was never defined so that for example, I get this warning: Unknown custom element: <button-widget> - did you register the component correctly? For recursive components, make sure to provide the "name" option..
There is no public API to do this.
The correct solution is not to register it globally in the first place and instead just use it on a per-component basis by adding it to the components option of each component you want to use it in.
Otherwise you can unregister it like this:
delete Vue.options.components['button-widget']
Please, look the flux below, it's shows my problem. I'm using vue-router with this.$router.push to browsing on pages. I'm starting on PageA.
PageA -> PageB ( mounted() of PageB is called)
PageB -> PageA (returning to PageA)
PageA -> PageB (mounted() of PageB is not called)
It sounds that page (.vue component) is not closed and mainted on cache or other thing. I must use the mounted() method every time that page is open and maybe close the page and clean from cache. How I can solve it?
vue will re-use components were possible, this is expected.
Normally you would watch for route changes and update your component state accordingly.
To react to route changes you can use beforeRouteUpdate():
const Example = Vue.extend({
template: `
<div>
<p>This changes: '{{param}}'</p>
</div>`,
data(){
return {
param: this.$route.params.param
};
},
beforeRouteUpdate(to, from, next) {
this.param = to.params.param;
next();
}
});
const router = new VueRouter({
routes: [
{
path: '/:param', component: Example,
}
]
})
const app = new Vue({ router }).$mount('#app')
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/foo">foo</router-link><br>
<router-link to="/bar">bar</router-link><br>
<router-view></router-view>
</div>
Alternatively you can also watch the route and update the state accordingly:
Vue.extend({
watch: {
'$route'() {
// TODO: react to navigation event.
// params cotains the current route parameters
console.log(this.$route.params);
}
},
// ....
});
The vue-router documentation has a few great examples for this: Data Fetching - Vue Router
If you still want to use mounted(), you can do so by giving your router-view a key that will change when the route changes, e.g.:
<router-view :key="$route.fullPath"></router-view>
This will force the component to be re-created every time, so it does have a performance penalty - i would recommend using the route hooks described above, if possible.
Vue 3
For those that ended up here like me looking at why I'm not getting unmounted called on a component inside a page, when route changes, here's some guidance:
Let's say you have Page A that uses Component A in it, and Page B that does not use Component A.
When you open Page A for the first time, Component A will have, as expected, mounted method called.
However, when you navigate to Page B, Vue3 won't un-mount or destroy Component A, rather it will deactivate it, and deactivated method will be called instead of unmounted.
Once you navigate back to Page A, Component A will be reactivated and method activated will be called instead of mounted.
For those who are using the Composition API for Vue 3:
This use case is for fetching data from an API upon component mount and other dependencies. Instead of watch, you need to use watchEffect to automatically track the dependencies and perform side effects on mount.
watchEffect(fetchData);
watch(dependencies, callback) will trigger on first visit to the route. But if you go to another page, then come back to it, it won't trigger again because it does not count the initial state as an update. Also, the dependencies technically did not change from the time it was mounted.
Additional Notes:
If you are coming from React, watch is not exactly equivalent to useEffect. watchEffect is more similar to it.
For comparison, this code should have the same results as the watchEffect one. But watchEffect is more concise.
onMounted(fetchData);
watch([dependency 1, dependency 2, ...], fetchData);
I wonder how does this line work:
this.ref.markForCheck();
Or:
this.ref.detach();
While ref is: ChangeDetectorRef
I didn't see a way to get in a service the component that called you, so how does angular knows which component called it when I call this.ref.detach(); and detach the right component?
I would expect a call like this.ref.detach(this); so I pass the reference to the component, but seems like angular service has a way to access the caller?
Added jsfiddle https://jsfiddle.net/1hk7knwq/11788/
Look at the test() call, somehow the ref service is also getting the component instance without me explicitly passing it.
Thanks
I have a pretty simple question about ember components.
I'm defining my model in my route:
model() {
return this.store.createRecord('player');
},
I'm passing the model to my component in my template:
{{region-picker model=model region="A" regionName=AName teamNameMap=teamNameMap}}
Then, inside my component, I can succesfully get the correct data from my model, but nothing that I change in my model is getting sent to the database. I can only assume this is because the model is being passed by value instead of by reference. Is there a way to pass it by reference, and if not, what is the common workaround for editing a model inside of a component.
Thanks!
the model should be updated, even if you change the data in the component.
Can you verify the same in the EmberJS plugin ? or did you make an explicit copy in the component.
The other thing you might be missing is you need to trigger model.save()
after your component has updated the model.
I have a component that I want to trigger a route level action on so I can transition to a different route.
App = Ember.Application.create();
App.Router.map(function() {
});
App.IndexRoute = Ember.Route.extend({
actions: {
complete: function() {
// This never happens :(
console.log('Triggered complete!');
}
}
});
App.MyAreaComponent = Ember.Component.extend({
actions: {
clickMyButton: function() {
console.log('Triggering complete action.');
// Attempting to trigger App.IndexRoute.actions.complete here
this.sendAction('complete');
}
}
});
What I am trying to accomplish is when MyAreaComponent's 'clickMyButton' action is triggered, it will trigger the IndexRoute's 'complete' action.
I have set up a jsbin to demonstrate my issue:
http://emberjs.jsbin.com/wivuyike/1/edit
According to the EmberJs documentation on action bubbling:
If the controller does not implement a method with the same name as the action in its actions object, the action will be sent to the router, where the currently active leaf route will be given a chance to handle the action.
So with this in mind I would expect the component to say "Let's check my controller and see if it has an action called 'complete'. No? Ok, let's check my route (IndexRoute) to see if it has an action called 'complete'. Yes? Ok, trigger it!"
The only thing I can think of is that because of the way the component is set up, IndexRoute isn't set as the route of the component, so the action bubbling just stops at the controller.
I'm unsure where to go from here. Do I need to do something special to make my component aware of my IndexRoute?
Here is your updated sample -
http://emberjs.jsbin.com/wivuyike/3/edit
So from the component your action need to bubble herself up, this can be done by
this.sendAction('clickMyButton');
and then when you use your component, assign route action which needs to be triggered to your component action as below
{{my-area clickMyButton='complete'}}
in the template next to the route:
{{component-name refresh=(route-action "refresh")}}
then in component you can call it:
this.get('refresh')();
An action can be called from a component template by creating another action in the component.
So it saves me time today.
If you are doing nothing else in the action, except for sending an action to the route, you can try doing this directly from the template:
<button {{action "complete" target="route"}}>Complete</button>
(Untested code, make sure you test first!)
... otherwise it looks like you should do:
this.sendAction('action', 'complete');
instead of:
this.sendAction('complete');
So it looks like you were just missing that first parameter in the code posted in your question. Reference