Calling A function using vue.js components and properties - javascript

I am creating a shopping cart type button to count the number of times clicked, the button should call a function when clicked with the parameter of the id, however, it won't call the function with the correct parameter
I have tried adding {{id}} and :onClick="addThisToCart({{id}} but getting a ton of errors.
Heres my code
Vue.component('movietable', {
props: ['title', 'price', 'mid'],
template: `
<tr>
<td>{{title}}</td>
<td>{{price}}</td>
<td>
<div class="quantity">
<button onClick="addThisToCart({{mid}}">You clicked me {{ count }} times.</button>
</div>
</td>
</tr>
`,
data: function () {
return {
count: 0
}
},
});
mid is being defined in the properties section of the vue element
and then the function
var cart = 0;
function addThisToCart(movieId) {
var movieId = this.mid;
this.cart += 1;
this.cart += 1;
console.log(movieId);
console.log(cart);
}
It should add +1 to cart every time the button is clicked, however, getting a ton of errors and instead of sending '4434' it is sending {{mid}}

You can use
<button #click="addThisToCart(mid)">You clicked me {{ count }} times.</button>
No curly braces for the argument of the function.

Related

Angular How to delete a component from ngFor Loop

I read data from these child components of type custom note and i keep track of them using index, i.
<div *ngFor="let x of Pages; let i = index; trackBy: trackNotes">
<app-custom-note id="inputField{{ i }}" (pagesEvent)="testUpdate($event, i)"></app-custom-note>
<button size="small" color="danger" (click)="deletePage(i)">Delete Page</button>
</div>
<button size="small" color="success" (click)="addPage()">Add new Page</button>
when i click delete, i splice the elements at the parent component. but the information thats been deleted is still on the page
testUpdate(card: FormGroup | FormArray, index: number) {
let cards = new CustomPage(card.value);
this.CustomPageEvent[index] = (cards);
}
trackNotes(index) {
return index
}
deletePage(index) {
this.CustomPageEvent.splice(index,1) // holds data
this.Pages.splice(index,1) // holds length of array
}
addPage() {
this.Pages.push('A page');
if (this.eventComponents.find(e => e.component === 'CustomNoteComponent')) {
let orderOfPage = this.eventComponents.filter(ord => (ord.component == "CustomNoteComponent")).pop();
this.eventComponents.push({ id: definedId, component: "CustomNoteComponent", order: addedOrder })
}
}
when deletePage(0) is executed, the component at index 1 is deleted. how do I delete the <custom-component> representational to the index i thats been selected?
i tried deleting by using the trackby attribute but couldnt link it to an actual delete function that deletes the component

How to remove the selected data from saved data when we click on button in a selected one

In my application I have saved the data when we click on it(we can add the multiple data by entering some data and save the multiple data by clicking the save button).
.component.html
<ng-container *ngFor="let categoryDetail of selectedCategoryDetails">
<div class="__header">
<div>
<b>{{ categoryDetail.category }}</b>
</div>
</div>
<div
class="clinical-note__category__details"
*ngIf="categoryDetail.showDetails">
<ul>
<li class="habit-list"
*ngFor="let habits of categoryDetail.habitDetails" >
<div class="target-details">
<b>{{ clinicalNoteLabels.target }}: </b
><span class="habit-list__value">{{ habits.target }}</span>
</div>
</li>
</ul>
<div class="habit-footer">
<span class="m-l-10"
[popoverOnHover]="false"
type="button"
[popover]="customHabitPopovers"><i class="fa fa-trash-o" ></i> Delete</span>
</div>
<div class="clinical-note__popoverdelete">
<popover-content #customHabitPopovers [closeOnClickOutside]="true">
<h5>Do you want to delete this habit?</h5>
<button
class="btn-primary clinical-note__save" (click)="deletedata(habits);customHabitPopovers.hide()">yes </button>
</popover-content></div>
</div>
</ng-container>
In the above code when we click on delete button it will show some popup having buttons yes(implemented in above code) and now so my requirement is when we clcik on yes button in from the popover it has to delete the particular one.
.component.ts
public saveHealthyHabits() {
let isCategoryExist = false;
let categoryDetails = {
category: this.clinicalNoteForm.controls.category.value,
habitDetails: this.healthyHabits.value,
showDetails: true,
};
if (this.customHabitList.length) {
categoryDetails.habitDetails = categoryDetails.habitDetails.concat(
this.customHabitList
);
this.customHabitList = [];
}
if (this.selectedCategoryDetails) {
this.selectedCategoryDetails.forEach((selectedCategory) => {
if (selectedCategory.category === categoryDetails.category) {
isCategoryExist = true;
selectedCategory.habitDetails = selectedCategory.habitDetails.concat(
categoryDetails.habitDetails
);
}
});
}
if (!this.selectedCategoryDetails || !isCategoryExist) {
this.selectedCategoryDetails.push(categoryDetails);
}
this.clinicalNoteForm.patchValue({
category: null,
});
this.healthyHabits.clear();
public deletedata(habits){
if (this.selectedCategoryDetails) {
this.selectedCategoryDetails.forEach((selectedCategory) => {
if (selectedCategory.category ==categoryDetails.category) {
isCategoryExist = true;
this.selectedCategoryDetails.splice(habits, 1);
}
});
}
}
The above code I have written is for saving the data(we can enter multiple data and save multiple )
Like the above I have to delete the particular one when we click on yes button from the popover.
Can anyone help me on the same
If you're iterating in your html like:
<... *ngFor="let categoryDetails of selectedCategoryDetails" ...>
and your button with deletedata() is in the scope of ngFor, you can:
Change your iteration to include index of an item and trackBy function for updating the array in view:
<... *ngFor="let categoryDetails of selectedCategoryDetails; let i = index; trackBy: trackByFn" ...>
On the button click pass the index to deletedata() method like:
deletedata(index)
Create your deletedata method like:
deletedata(index:number){
this.selectedCategoryDetails.splice(index, 1);
// other code here, like calling api
// to update the selectedCategoryDetails source
// etc.
}
Create trackByFn method like:
trackByFn(index,item){
return index;
}
EDIT: Without index
If you want to iterate over selectedCategoryDetails in the ts file, without using ngFor with index in your html, you can have your deletedata like this:
deletedata(categoryDetails:any){
for (let i = this.selectedCategoryDetails.length - 1; i >= 0; i--) {
if (this.selectedCategoryDetails[i] === categoryDetails.category) {
this.selectedCategoryDetails.splice(i, 1);
}
}
}
It will iterate over selectedCategoryDetails backwards and remove the categoryDetails if it finds it in the array of objects.
Now, you only need to pass the categoryDetails to deletedata in your html:
(click)="deletedata(categoryDetails);customHabitPopovers.hide()"

How to pass computed function into v-bind and v-on?

I know it's simple example, but I'm new to Vue.
I'm sure that error is where I pointed. I'm trying to call function from computed method. There is no problem if write code straight to v:bind, but once I pass it throug func it gives me error. As well I know that from method functions call as func(), but I guess from computed they have another syntax.
<body>
<div class="container">
<hr>
<div class="sample">
<input type="text" v-bind:value="name" v-on:input="name = $event.target.value">
<hr>
<h2>Hello, {{ name }}</h2>
<input type="button" value="Click on it" v-on:click="addNumber()">
<hr>
<div v-for="num in numbers">
{{ num }}
</div>
<input type="button" v-on:click="show" v-bind:title="btnText" > //I'M SURE ERROR IS HERE
<h2 v-show="showH2">check hide</h2>
</div>
<div></div>
<script>
let sample = new Vue({
el: '.sample',
data:
{
showH2: true,
name: '12344',
numbers: []
},
methods:
{
addNumber()
{
let num = Math.floor(Math.random() * 11) - 5;
this.numbers.push(num);
}
},
computed:
{
btnText()
{
return this.showH2 ? 'Hide' : 'Show';
},
sum()
{
let sum = 0;
this.numbers.forEach((el) => sum += el);
return sum;
},
show()
{
this.showH2 = !this.showH2;
}
}
});
</script>
</body>
</html>
You are perfectly correct when you say- "There is no problem if write code straight to v:bind, but once I pass it through func it gives me error". This is because You don't call a computed and it doesn't accept any parameters. You reference a computed property just like you would a data property. Whereas a method is just a function bound to the Vue instance. It will only be evaluated when you explicitly call it. So if you shift show in methods it will work. vue doc

Computed, print items on change without duplicate

I'm trying to show items in a table every time the list of item changes. I've got a function and a computed but I'm not quiet sure if I'm doing it right.
Function(){
Every time a new value comes in: changeList.push(item);
}
Then I have this computed
Computed:
changedData: function(){
return this.changeList;
}
HTML
<tr v-for="change in changedData">
<td>change.value</td>
<tr>
It does print out every item in the list when it is changed, but the thing is I don't want it to print out the items that already printed out, only new items.
EDIT: Found the problem (push function)
for(index in question.Answers){
if(question.Answers[index].Selected === "selected" &&
question.Answers[index].Value === savedQuestion.Value){
this.myList.push({newValue: question.Answers[index].Value, oldValue:
savedQuestion.Value});
}
}
This will add all the questions with their value regardless if the list already contains the same question with the exact same values.
So to achieve this you can check in the array if the object with same values is present or not.If not than only push the values.
Below is the updated code.
const search = (newVal,oldVal) => array.find(element => (element.newValue === newVal && element.oldValue === oldVal));
for(index in question.Answers){
if(!search(question.Answers[index].Value,savedQuestion.Value))
if(question.Answers[index].Selected === "selected" &&
question.Answers[index].Value === savedQuestion.Value){
this.myList.push({newValue: question.Answers[index].Value, oldValue:
savedQuestion.Value});
}
}
In your code computed is actually not needed.Here is the basic example of adding dynamic values into the list at runtime.
https://codepen.io/anon/pen/LqKjMM?editors=1111
Template code =>
<script src="//vuejs.org/js/vue.js"></script>
<h1>Example of managing a Vue.js list</h1>
<div id="products">
<vue-products></vue-products>
</div>
<script type="text/template" id='vue-products-template'>
<div>
<form v-on:submit.prevent="addProduct">
<input type="text" v-model="productName">
<input type="submit" value="Add"></input>
</form>
<ul>
<li v-for="name in productNames">{{name}}</li>
</ul>
</div>
</script>
Script code =>
Vue.component('vue-products', {
template: '#vue-products-template',
data: function() {
return {
productName: "",
productNames: ['booze', 'tums', 'peas', 'short wave radio'],
}
},
methods: {
addProduct: function() {
this.productNames.push(this.productName)
this.productName = ""
}
}
})
$(function() {
new Vue({el: '#products', data: {}})
});

Loops in Handlebars

I have an array of tracks coming from a database that I want to display in a div.
More specifically I need to put every two of them in a bootstrap row. I can easily do it in the controller JS file by first collecting all in a fragment element and then use a loop to put them into the rows and then in the target div but I am wondering if it would be possible to do it directly while producing them in handlebars?
Here is the handlebars template:
{{#if result}}
{{#each result}}
<div class="playlist-item col-xs-4">
<a href="#/user/{{username}}/playlist/{{id}}" class="no-style">
<h3 class="result-title">{{title}}</h3>
<p class="result-description">{{description}}</p>
<img class="result-image img-circle" src="{{img}}">
</a>
<br>
<a type="button" id="{{id.videoId}}" class="btn btn-default btn-remove"
href="#/user/{{username}}/playlist/{{id}}/remove-from-playlist">Remove from
playlist</a>
</div>
{{/each}}
{{else}}
<h4>You currently have no tracks in your playlist</h4>
{{/if}}
Here is the JS:
showPlaylist() {
return Promise.all([
userController.loadPlaylist(),
templates.loadTemplate('playlist'),
])
.then(([tracks, template]) => {
let fragment = document.createDocumentFragment()
let div = document.createElement('DIV');
div.innerHTML = template(tracks);
div = [...div.children];
let len = div.length
while(div.length > 0) {
let row = document.createElement('div')
row.className = 'row'
let col = div.splice(0,2)
row.append(col[0])
if(col[1]) {
row.append(col[1])
}
len -= 2;
fragment.append(row)
}
$('#container').html(fragment)
})
}
It is possible to group your items into rows, but you would need to use a custom helper function to do it.
We will need to create a block helper that takes an array of items, breaks the array into rows of specified number of columns, and then applies the block "row" template to each row. If we were to call our block helper "eachRow", the resulting template might look like the following:
{{#eachRow result 2}}
<div class="row">
{{#each columns}}
<div class="playlist-item col-xs-4">
{{!-- TODO: Rest of item template goes here. --}}
</div>
{{/each}}
</div>
{{/eachRow}}
Notice that we still use the item template within a regular Handlebars #each block. Except now the #each is wrapped within a "row" template block. The 2 is a parameter that will be passed to our helper that is to be the number of columns in each row.
Next, we will write our helper:
Handlebars.registerHelper('eachRow', function (items, numColumns, options) {
var result = '';
for (var i = 0; i < items.length; i += numColumns) {
result += options.fn({
columns: items.slice(i, i + numColumns)
});
}
return result;
});
This helper simply iterates over our source array in increments of numColumns and for each iteration applies our "row" block template, passing the array of items (columns) that are to render in that row. The helper concatenates the rows and returns the result.
I have created a fiddle for reference.

Categories