How to remove item from list in VUE? - vue.js

I have the container which consist of different div. I want to remove specific ones on click. My code (by example from vue page) not working and I cant figure why... May be there is some suggestions?
<div class="m-t-15">
<div v-for="(tag, index) in $parent.post.tags" v-on:remove="$parent.post.tags.splice(index, 1)" class="btn-group btn-group-sm m-b-5">
<button type="button" class="btn btn-default" disabled="disabled">#{{ tag.name }}</button>
<button type="button" class="btn btn-default" v-on:click="$emit('remove')">
<i class="fa fa-trash"></i>
</button>
</div>
</div>

You shouldn't need to access parent's data like that. Mutating parent's data is not a good practice. Just pass props down, and emit event back to the parent component.
Child components emit remove event:
<button #click="$emit('remove', i)">...</button>
The parent component:
<child :list="list" #remove="remove"></child>
remove (i) {
this.list.splice(i, 1)
}
Please see the working example here: http://codepen.io/CodinCat/pen/PmYNBb?editors=1010

Related

ngFor alway returning id of first object in array

I'm trying to get the ID of an object by assigning it to a variable declared in the component when a button is clicked. However, it seems to only be getting the first objects ID every time.
Here is a link to StackBlitz which replicates the issue I am having:
https://stackblitz.com/edit/angular-vcbzxf
Any assistance would be greatly appreciated.
Update 1 : You can actually achieve what you wanted using bootstrap modal with few changes like below.
comment.component.html:
<div *ngFor="let comment of post.comments; let i = index">
{{ comment.content }}
<button data-toggle="modal" [attr.data-target]="'#confirmDeleteCommentModal' + comment.id">Get ID</button>
<div class="fade modal" [attr.id]="'confirmDeleteCommentModal' + comment.id" tabindex="-1" role="dialog" aria-labelledby="confirmDeleteCommentModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmDeleteCommentModalLabel">Delete Comment</h5>
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" (click)="checkId(comment.id)">Delete</button>
</div>
</div>
</div>
</div>
</div>
Notice [attr.data-target]="'#confirmDeleteCommentModal' + comment.id" added to Get ID button and [attr.id]="'confirmDeleteCommentModal' + comment.id" which is added to bootstrap modal. Moreover, modal is now included in *ngFor div element.
Previously, data-target of Get ID button was always set to confirmDeleteCommentModal, and we technically just had one bootstrap modal with id set to confirmDeleteCommentModal, I think that is what caused the issue, we always loaded the same (and first) bootstrap modal.
Updated stackblitz: https://stackblitz.com/edit/angular-3tmtwa
Original answer (this did not solve the issue):
If you're going with per your most recent comment (as implemented here - https://stackblitz.com/edit/angular-vcbzxf), I would just like to point out that you could write your comment.component.html as follows:
comment.component.html
<div *ngFor="let comment of post.comments">
<div>
{{showDelete ? 'Are you sure you want to delete?' : comment.content + ' id = ' + comment.id}}
<button *ngIf="showDelete" (click)="showDelete = false">Cancel</button>
<button (click)="(showDelete == false && showDelete = true) || checkId(comment.id)">Delete</button>
</div>
</div>
Stackblitz to give it a quick test : https://stackblitz.com/edit/angular-q27xn7
While this may not be a great improvement over what you've done, thought I would just point out other ways of achieving similar results.

how to pass index to sibling div/tag

I'm developing a mobile app using phonegap, vuejs2 and onsenui. I installed vue2-touch-events plugin for touch events. In this app i'm rendering some data using v-for. Here is the div.
<v-ons-list-item class="swipeArea"
v-for="(list, index) in todoLists"
v-bind:key="list.todo"
v-if="!list.completed"
v-touch:swipe.left="deleteLisItem(index)"
v-touch:swipe.right="doneLisItem(index)"
v-touch:longtap="deleteOrDone(index)" tappable>
<label class="center">
<i class="zmdi zmdi-check" aria-hidden="true"></i> {{ list.todo }}
</label>
</v-ons-list-item>
And then when user longtap a list there will be a pop with 3 button. Done, Delete and cancel. When user tap done, the item will be mark as done, if click delete, the item will be deleted and click cancel will cancel the event. Here is the div
<div v-if="doneOrDelete" id="doneOrDelete">
<div class="action-sheet-mask"></div>
<div class="action-sheet">
<button class="action-sheet-button" #click="doneLisItem">Done</button>
<button class="action-sheet-button action-sheet-button--destructive" #click="deleteLisItem">Delete</button>
<button class="action-sheet-button" #click="doneOrDelete=false">Cancel</button>
</div>
</div>
Now all i have to do is pass the index to the method. Can anyone help me how pass the index? TIA

Vuejs - Event delegation and v-for context reference

I'm using the following snippet to render a list:
<div #click.prevent="myhandler($event)"><!-- Delegated event handler-->
<div class="tasks" v-for="(task, subName) in step.tasks">
<button type="button">
{{ task.caption }}
</button>
<span> {{ task.callableName }} </span>
</div>
</div>
methods: {
myhandler(e){
// Event target may be a button element.
let target = e.target;
// …
// Let's assume we know 'target' is a button element instance.
// I wish I could have access to the v-for item ("task") which is associated in some ways to the button that was clicked.
// let the_task_reference = ?;
}…
Is there a clean way so that I could reach the v-for scope specific task related to that button?
Thank you.
An alternative would be to store the index of the task on the button.
<div class="tasks" v-for="(task, index) in step.tasks">
<button type="button" :data-index="index">
{{ task.caption }}
</button>
<span> {{ task.callableName }} </span>
</div>
And in your handler get the task using the index.
myhandler(evt){
const task = this.step.tasks[evt.target.dataset.index]
...
}
Example.
If you had something stronger like an id, that would be even better.
Not Recommended
There is a hidden property, __vue__ that is added to Vue and Component root elements. Were you to iterate over a component, you would be able to do something like in this example.
I wouldn't recommend that approach because it relies heavily on Vue internals that could change from version to version, but it's there today.
The most straight-forward solution would be to put the event handler on the same div as the v-for directive and just pass in the task variable:
<div
class="tasks"
v-for="(task, subName) in step.tasks"
#click.prevent="myhandler(task, $event)"
>
<button type="button">{{ task.caption }}</button>
<span>{{ task.callableName }}</span>
</div>
If you really need to use an event handler on a parent element, you could keep track of the clicked task via a component property and add an extra click handler to the div with the v-for directive:
<div #click.prevent="myhandler($event)"
<div
class="tasks"
v-for="(task, subName) in step.tasks"
#click="clickedTask = task"
>
<button type="button">{{ task.caption }}</button>
<span>{{ task.callableName }}</span>
</div>
</div>
You would need to add a clickedTask property:
data() {
return {
clickedTask: null,
}
}
And then in the handler you could refer to the task via this.clickedTask.

Can't remove item from dynamically generated list

I'm having trouble with a function that I've created using scotch.io's VueJS guide. It's supposed to remove a function from the list on click, (triggering a view update) but I keep getting this console error:
[Vue warn]: Property or method "$index" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in root instance)
The guide doesn't say to declare anything in the data option, what am I missing?
Here's the HTML:
<a href="#" class="list-group-item" v-for="event in events">
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{event.name}}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{event.date}}
</h5>
<p class="list-group-item-next" v-if="event.description">{{event.description}}</p>
<button class="btn btn-xs btn-danger" v-on:click="deleteEvent($index)">Delete Event</button>
</a>
And the relevant JS:
new Vue({
...
methods: {
...
deleteEvent: function(index) {
if (confirm("Are you sure you want to delete this event?")) {
this.events.$remove(index);
}
}
}
});
There's already been a couple of cases where I've had to swap out deprecated directives that the guide uses, (v-repeat, v-on="click", etc) is the problem something similar?
The guide you are following is slightly outdated.
In Vue 2.0, using the index requires a little change in the syntax:
<a href="#" class="list-group-item" v-for="(event, index) in events">
<!-- You have to ask for it: ^^^^^^^^^^^^^^ -->
<button class="btn btn-xs btn-danger" v-on:click="deleteEvent(index)">
<!-- Then use it the way you named it: ^^^^^ -->
Delete Event
</button>
</a>

Can I specify multiple data targets for Twitter Bootstrap collapse?

I'd like to target two divs for expand when clicking on a single trigger? Is that possible?
You can simply add all the ids separated by commas in data-target:
<button type="button" data-toggle="collapse" data-target="#demo,#demo1,#demo2">
Hide them all
</button>
Use the same class for both div's and set your data-target according this. Give your div's also the same parent (can also be a class) and set data-parent according this.
<button type="button" data-parent="#wrap" data-toggle="collapse" data-target=".demo">
simple collapsible
</button>
<div id="wrap">
<div class="demo collapse">
test1
</div>
<div class="demo collapse">
test1
</div>
</div>
From the Bootstrap 3 documentation:
The data-target attribute accepts a CSS selector to apply the collapse to.
Therefore you can use a comma-separated list of id's, class selector etc. for the data-target attribute value:
<button class="btn btn-primary"
type="button"
data-toggle="collapse"
data-target="#target1,#target2,#target3"
aria-expanded="false"
aria-controls="target1,target2,target3">
You can see an extensive list of valid CSS selectors on the w3schools website.
If you want to hide one element and show another (like the bootstrap accordion but without a panel) you can add multiple targets but also add the class 'in' to have one element expanded on load!
<div class="collapse text-center clearfix in" id="section1">
<h1>This is section 1</h1>
<p>Are you excited about toggling?</p>
</div>
<div class="collapse text-center clearfix alert-warning" id="section2">
<h1>Boo!!</h1>
<p>This is a new sentence</p>
</div>
<button class="btn btn-block btn-primary" data-toggle="collapse" data-target="#section1,#section2">Toggle</button>
Live demo here
There is a solution in Bootstrap 4:
Bootstrap 4 documentation: Multiple targets
The least you need is:
data-toggle="collapse" data-target=".multi-collapse" for the toggle
button
class="collapse multi-collapse" for each element you need to
toggle
(While multiple ids in data-target indeed don't work).
The data-target answer did not work for me. However you can use JavaScript to do this.
Make your button call some JavaScript like:
$('.collapse-class').collapse('toggle')
See: https://getbootstrap.com/javascript/#via-javascript-3.
Bootstrap 4 uses document.querySelector instead of document.querySelectorAll. If this changes, then everything works.
In Bootstrap 4 using the same class for each respective div seemed to work over using id.
<button type="button" class="btn" data-toggle="collapse" data-target=".collapse-group">
Reveal Items</button>
<div class="row">
<h3>Visible Row div 1</h3>
<div class="collapse-group collapse">
<p>hidden item</p>
</div>
<h3>div 2</h3>
<div class="collapse-group collapse">
<p>hidden item 2</p>
</div>
</div>
I faced the same problem but bootstrap won't let data-toggle="tab" beside data-toggle="collapsed" so my problem was like this.
I have:
<a type="button" class="btn btn-default btn-lg btn-block " href="#ld-tab-pane-eye" aria-expanded="false" aria-controls="ld-tab-pane-eye" role="tab" data-toggle="tab" aria-haspopup="true" aria-expanded="false" style=" border: 0px ;"> </a>
and this was switching tabs:
<button type="button" class="navbar-toggle collapsed nav-trigger style-mobile" data-toggle="collapse" data-target="#main-header-collapse" aria-expanded="false" data-changeclassnames='{ "html": "mobile-nav-activated overflow-hidden" }'>
</button>
But this one closes the modal because the switch tab button was in the modal.
To solve the problem I add id="popout"(to first button) and id="popoutTrigger" (to secend button).
And this script (jQuery):
$( "#popout" ).click(function() {
$( "#popoutTrigger" ).trigger( "click" );
});
When the user clicked the switch button in the modal, after switching modal will close.
This a view of what I've done.
I hope my solution will help.