I have a vue component to delete items. function working fine.but that deleted items is remain the page until I press the reload in web browser. Following is the delete method in vue component.
deletedItem: function () {
let data = {
"_method" : 'delete',
}
axios.post('/delete-item' +this.id, data);
this.isDelete =false;
location.reload();
}
-Delete button
<button v-on:click="deletedItem" class="bg-red-500 text-gray-200 rounded hover:bg-red-400 px-6 py-2 focus:outline-none mx-1"> Delete</button>
Is there are anyway to remove deleted item once I click delete button.
Once I reload the page after the response came then it reload the page & remove deleted item. solution is as follows.
axios.post('/delete-item' +this.id, data).then( response => {
this.isDelete =false
location.reload()
});
As i understand it you have all the items stored remotely on a server somewhere else, and the way you fetch the items is by some other request. And what you are doing here is deleting the item on the server without doing anything else.
I don't see how the web-application would know that the item got deleted. Normally I solve this situation with deleting the object locally after i receive a 200 from the api.
If you want something even more simple you could just refresh the list of items after you receive a response of any kind. It all depends on what kind of features you want to have.
Please confirm the points below:
Did your api responded with a successful response?
Did you declare isDelete property in data function?
You should change the isDelete property after the ajax finished using promise or async/await because ajax is asynchoronous at the most time.And you should declare the isDelete property in data function like this:
data () {
return { isDelete: true }
}
because it makes Vue be able to collect the dependency relation and make it responsive.
Related
i have a button in my project when i click on it two queries added to URL
onClickBtn(){
this.$router.push({queries: {name:'kevin' , age:21} })
}
and I have a watch on $route
watch:{
$route:function(){
// call API
}
}
and when i click on that button severall time
watch calls my API every time although nothing has changed
and this make a problem form me
because nothing has changed in route But API is called and the same data is
received .
what should I do to avoid calling API in watch , when queries don't changed ??
The object you are pushing on the router is always different, that's why the $route watch is launched.
You can compare the data you receive in the watch, so that when they are different then you invoke the API:
watch:{
'$route' (newRoute, lastRoute){
// Check if query is different
// call API
}
}
On top of the answer that Cristian provided, you could also even double-check if your stuff has changed before even pushing a new object to your router.
Like this
checkIfUpdateNeeded && this.$router.push({queries: {name: 'kevin', age:21 } })
That way, you will have less moving parts and you won't have a trigger in the watcher for "nothing", especially if you're pushing a bigger object and want to make a deep-diff between 2 objects.
Using Vue2, I have a list of books on a page where the details are shown via a child component. The user can delete one of the books from within the child component. I want the list to update after the delete. I'm using an $emit from the child after the record is deleted from the database. I can see from the console that the $emit is sent with the correct new list of books in the payload. However, the callback function in the parent isn't being fired. I can tell because the console.log I have at the beginning of the callback isn't showing up in the console.
Any suggestions on what I'm doing wrong?
Here's what's in the parent:
<div v-for="book in books" :key="book.BookID" #book-refresh="refreshbooks">
<BookCard :book="book" />
</div>
Here's the callback function:
async refreshbooks(newbooks) {
console.log('in books.vue, starting refreshBooks, newbooks is: ', newbooks)
this.$set(this, "books", newbooks)
.bind(this)
},
Here's what's in the function (in the child component) that fires the $emit:
async deleteBook(bookID) {
this.dialogdelete = false
await (EventService.deleteBook(bookID, this.user.UserID))
.then((newbooks) => {
this.$emit('book-refresh', newbooks)
}
)
},
TJ, thank you for the suggestion. I did as you said and created the mre which worked. I then went back to see what was different. The only things I changed were the name(s) of the functions. I changed the book-refresh and refreshbooks all to a simple refresh. Now it works perfectly. I haven't taken the time to see if I just didn't have the names matched up properly or if there's just something about hyphenated names? I did read somewhere yesterday that mixed case makes a difference, so maybe? In any case, keeping it super simple has helped me to move on.
Thanks again for helping me see the forest instead of the trees!
In my application, I have this in my template:
<template>
<div>{{ report.imageId.length }}</div>
</template>
In my script:
created() {
this.loadReport();
},
methods: {
async loadReport() {
this.report = await reportSvc.getReport(this.$route.params.id);
},
}
When I load the page, I'm getting the error "Cannot read property 'length' of undefined" in the DevTool. The HTML does print the correct numbers though. Why does the error comes out though?
I assume the error is because the DOM tries to read from the 'report' variable, but since loadReport() is not finished yet, that is why it errors out. But I thought the 'created' comes first before the DOM is loaded and rendered?
This question isn't how to make the error goes away, I want to know why the DOM gets loaded before the 'created'.
i think you need this:
async beforeMount() {
await this.loadReport();
},
In created hook you are able to access reactive data and events that are active but templates and Virtual DOM have not yet been mounted or rendered.
if you call for an API however it doesn't wait for the result and then move on to the next life cycle hook, this is how it goes:
created hook starts and you have access to reactive data and events
you call the API
API is asynchronous so the code goes on you reach beforeMount and mounted hook but the data isn't updated yet since there's no response yet, DOM renders and you get the error
one way to get pass the error is to use a condition like this in your template:
{{ report.imageId ? report.imageId.length : '' }}
this way your user get to see the app and doesn't have to wait for the response to come if it's taking a long time and by the time the response comes, template gets updated
Just add a v-if check to make sure the data exists: <div v-if="report">....
The reason is that you're loading data asynchronously, so it won't exist when the component is created.
I am creating a tab component that loads its v-tab-item components dynamically, given an array of configuration objects that consist of tabName, id, and tabContent which is a resource location for the component. I have it successfully loading the components. However, they don't actually initialize (or run their created() methods) until I switch tabs. I just get empty tabs with the correct labels. Using the DOM inspector initially shows just <componentId></componentId>, and then when I switch tabs, those tags are replaced with all of the component's content.
How do I get the dynamic components to initialize as soon as they are loaded?
EDIT: I created a CodePen here:
https://codepen.io/sgarfio/project/editor/DKgQON
But as this is my first CodePen, I haven't yet figured out how to reference other files in the project (i.e. what to set tabContent to so that require.js can load them up). I'm seeing "Access is denied" in the console, which makes it sound like it found the files but isn't allowed to access them, which is weird because all the files belong to the same project. So my CodePen doesn't even work as well as my actual project. But maybe it will help someone understand what I'm trying to do.
Also, after poking around a bit more, I found this:
http://michaelnthiessen.com/force-re-render/
that says I should change the key on the component and that will force the component to re-render. I also found this:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Which has a pretty good example of what I'm trying to do, but it doesn't force the async component to initialize in the first place. That's what I need the async components to do - they don't initialize until I switch tabs. In fact they don't even show up in the network calls. Vue is simply generating a placeholder for them.
I got it working! What I ended up doing was to emit an event from the code that loads the async components to indicate that that component was loaded. The listener for that event keeps a count of how many components have been loaded (it already knows how many there should be), and as soon as it receives the right number of these events, it changes the value of this.active (v-model value for the v-tabs component, which indicates which tab is currently active) to "0". I tried this because as I noted before, the async components were loading/rendering whenever I switched tabs. I also have prev/next buttons to set this.active, and today I noticed that if I used the "next" button instead of clicking on a tab, it would load the async components but not advance the tab. I had already figured out how to emit an event from the loading code, so all I had to do at that point was capture the number of loaded components and then manipulate this.active.
I might try to update my CodePen to reflect this, and if I do I'll come back and comment accordingly. For now, here's a sample of what I ended up with. I'm still adding things to make it more robust (e.g. in case the configuration object contains a non-existent component URL), but this is the basic gist of it.
created: function() {
this.$on("componentLoaded", () => {
this.numTabsInitialized++;
if(this.numTabsInitialized == this.numTabs) {
// All tabs loaded; update active to force them to load
this.active = "0";
}
})
},
methods: {
loadComponent: function(config) {
var id = config.id;
var compPath = config.tabContent;
var self = this;
require([compPath], function(comp) {
Vue.component(id, comp);
self.$emit("componentLoaded");
});
}
}
I am working on a vuejs SPA.
I have a view that shows a list of items and another view that shows details for a specific Item.
when I click the item I switch views using:
this.$router.push('/item/' + event.ItemId );
The data is managed using vuex modules.
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
Where would be the appropriate place (code/lifecycle) to trigger the (async) retrieval of the data required for rendering the item details view?
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
One way to achieve this, is to define a state variable, named e.g. isLoading, in the data context of the Vue component. This variable would then be true while the data is retrieved asynchronously. In the template, you can use v-if to display a spinner while loading, and displaying the content after that.
If you are retrieving the data multiple times (refreshing the view), I would move the retrieving code into a method, e.g. called loadData. In the mounted section of the Vue component you then can just initially call this method once.
Here is some example code:
<template>
<div>
<button #click="loadData" :disabled="isLoading">Refresh</button>
<div class="item" v-if="!isLoading">
{{ item }}
</div>
<div class="spinner" v-else>
Loading...
</div>
</div>
</template>
<script>
import HttpService from '#/services/HttpService';
export default {
name: 'item-details',
data () {
return {
isLoading: false,
item: {}
};
},
methods: {
loadData () {
this.isLoading = true;
HttpService.loadData().then(response => {
this.item = response.data;
this.isLoading = false;
}, () => {
this.item = {};
this.isLoading = false;
});
}
},
mounted () {
this.loadData();
}
};
</script>
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
This issue you mentioned occurs if you are not using the HTML5 history mode, but an anchor (#) in the URL instead. If you are just changing the part after the anchor in the URL, the page is not actually refreshed by the browser. The Vue component won't be reloaded in this case and the state is still old. There are basically two ways around this:
You are switching from anchors in the URL to a real URL with the HTML5 history mode, supported by the Vue Router. This requires some back-end configuration, though. The browser then does not have this faulty behavior, because there is no anchor. It will reload the page on every manual URL change.
You can watch the $route object to get notified on every route change. Depending on if the user is changing the part after the anchor, or before, the behavior is different (it also depends where the cursor is, when you hit enter). If the part after the anchor is changed (your actual Vue route), only the component is notified. Otherwise, a full page refresh is made. Here's some example code:
// ...inside a Vue component
watch: {
'$route' (to, from) {
this.loadData();
}
}