Run functions with checkbox interactions - javascript

I am trying to create checkboxes that will add an object to an array when checked, but will remove that object when unchecked. I am not sure if this is the correct way, but will show what I have below. Using Angular 2 by the way.
Original Code:
<div>
<ul>
<a *ngFor="let perkResult of perkList.results" (click)="onAddPerk(perkResult)">
<li>{{ perkResult.perk }}</li>
</a>
</ul>
</div>
<div *ngFor="let perk of company.perks;>
{{ perk.perk }}
<a (click)="onDeletePerk(i)"></a>
</div>
Functions:
onAddPerk(perkResult) {
// Adds a new perk to the array
this.company.perks.push({perk: (perkResult.perk)});
}
onDeletePerk(i: number) {
// Delete the perk in the selected index
this.company.perks.splice(i, 1);
}
And I want to do something like this:
<div *ngFor="let benefitResult of benefitList.results" >
<a (click)="onAddBenefit(benefitResult)">
<input type="checkbox" />
//Basically if checked then run add function, if not checked then run delete function
</a> {{ benefitResult.benefit }}
</div>
EDIT:
Got to this point, but cannot reference my other scope for the delete.
<ul *ngIf="benefitList">
<div *ngFor="let benefitResult of benefitList.results; let i = index" >
<input type="checkbox" (change)="updateBenefits($event, benefitResult, i)" >
<li>
{{ benefitResult.benefit }}
</li>
</div>
</ul>
//Original delete
<div *ngFor="let benefit of company.benefits; trackBy: customTrackBy; let i = index">
{{benefit.benefit}}
<a (click)="onDeleteBenefit(i)"></a>
</div>
//function
updateBenefits(event, benefitResult, i) {
if(event.srcElement.checked) {
this.company.benefits.push({benefit: (benefitResult.benefit)});
} else {
this.company.benefits.splice(i, 1);
}
}

You could listen to the change event on the checkboxes. Then pass the event along with the object and index to a method that will look at the status of the checkbox element.
In the component:
optionsList = {
results: [{
perk: 'free coffee',
checked: false
}, {
perk: 'car',
checked: false
}, {
perk: 'pastry',
checked: false
}, {
perk: 'free post-it notes',
checked: false
}]
};
updatePerks(event, perkResult) {
perkResult.checked = event.srcElement.checked;
}
In your HTML template:
<ul>
<li *ngFor="let listItem of optionsList.results">
<label><input type="checkbox" (change)="updatePerks($event, listItem)">
{{ listItem.perk }}</label>
</li>
</ul>
Note: I don't know your data structure, so make the necessary adjustment or post a data sample if you need more help.

Related

Why isn't the v-bind attribute working properly?

So I'm creating a simple To-Do List app using VueJS:
<template>
<div>
<br/>
<div id="centre">
<div id="myDIV" class="header">
<h2 style="margin:5px">My To Do List</h2>
<input type="text" id="myInput" v-model="text" v-on:keyup.enter="AddNote()" placeholder="Title...">
<span v-on:click="AddNote()" class="addBtn">Add</span>
</div>
<ul id="myUL">
<li v-on:click="ToggleClass(index)" v-for="(item, index) in array" v-bind:class="{ checked: isChecked[index] }">
{{item}}
<span class="close">×</span>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "Notepad",
data() {
return {
array: [],
text: "",
isChecked: []
}
},
methods: {
AddNote: function() {
if(this.text!=="") {
this.array.push(this.text);
this.isChecked.push(false);
this.text = "";
}
},
ToggleClass(index) {
console.log(index);
this.isChecked[index]=!this.isChecked[index];
console.log(this.isChecked);
}
}
}
</script>
However when I click on an item the v-bind attribute doesn't bind the class when I click on it. Instead it binds it when I type something in the text field above.
Can anyone please help?
The isChecked array is not reactive and vue cannot detect changes.
You have to trigger it, for example via $set or splice.
Read more about it here: https://v2.vuejs.org/v2/guide/list.html#Caveats
You can change your code like this:
ToggleClass(index) {
console.log(index);
this.isChecked.splice(index, 1, !this.isChecked[index])
// or this.$set(this.isChecked, index, !this.isChecked[index])
console.log(this.isChecked);
}

toggle active class on list menu items - vue js

I want that, clicking on a menu item, the active class is triggered only for that specific item and removed for the others, until now I wrote this:
<template>
<nav class="navbar">
<div class="navbar__brand">
<router-link to="/">Stock Trader</router-link>
</div>
<div class="navbar__menu">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/portfolio">Portfolio</router-link>
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
<router-link to="/stocks">Stocks</router-link>
</li>
</ul>
</div>
<div class="navbar__menu--second">
<ul class="navbar__menu--list">
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
End Day
</li>
<li #click="isActive=!isActive" class="navbar__menu--item" :class="{active:isActive}">
Save / Load
</li>
</ul>
</div>
</nav>
</template>
<script>
export default {
data(){
return{
isActive: false
}
}
}
</script>
now of course, when I click on one item the active class is inserted/removed for all the items, what is the best solution for making that only a specific item, on clicking on it, receives the active class?
You'll want some sort of identifier for each clickable item and set that to your data property. For example
data() {
return { active: null }
}
and in your list items (for example)
<li #click="active = 'portfolio'"
class="navbar__menu--item"
:class="{active:active === 'portfolio'}">
In this example, the identifier is "portfolio" but this could be anything, as long as you use a unique value per item.
You could keep an object of links you have and handle a click on each of items. E.g.
data() {
return {
links: [
{
title : 'Portfolio',
to : '/portfolio',
isActive : false,
location : 'first',
},
{
title : 'Stocks',
to : '/stocks',
isActive : false,
location : 'first',
},
{
title : 'End Day',
to : '#',
isActive : false,
location : 'second',
},
{
title : 'Save / Load',
to : '#',
isActive : false,
location : 'second',
},
]
};
},
methods: {
handleNavClick(item) {
this.links.forEach(el, () => {
el.isActive = false;
});
item.isActive = true;
}
},
I do this in vue3.
the template is:
<li
v-for="(title, index) in titles"
class="title"
:key="index"
:class="{ active: active === index }"
#click="updateActive(index)"
>
{{ title }}
</li>
and the script is
<script lang="ts" setup>
import { ref } from "vue"
const titles = ["title1","title2","title3"]
const active = ref(-1)
function updateActive(val: number) {
active.value = val
}
</script>
If you have several ul => use title instead of index. For example :
<ul>
<div>
Applicants
</div>
<li
v-for="(title, index) in applicantMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
<ul>
<div>
Offices
</div>
<li
v-for="(title, index) in officeMenuTitles"
:key="index"
:class="{ active: active === title }"
#click="updateActive(title)"
>
{{ title }}
<div
v-if=" active === title "
class="cursor"
/>
</li>
</ul>
And on script :
...
const active = ref('')
function updateActive(title: string) {
active.value = title
}

Vue.js change model attached to a form upon clicking a list

I have an array of objects. These objects are loaded into a list in vue.js.
Aside from this list, I have a form that displays data from one of these objects. I want to, when clicking one of the list's elements, it will bind this specific object to the form and show its data.
How can do this in Vue.js?
My list code is:
<div id="app-7">
<ul id="food-list" v-cloak>
<food-item v-for="item in foodList" v-bind:food="item" v-bind:key="item.id" inline-template>
<li class="food">
<div class="food-header">
<img :src="'img/' + food.slug +'.png'">
<div class="food-title">
<p>{{food.name}} |
<b>{{food.slug}}</b>
</p>
<p>quantity: {{food.quantity}}</p>
</div>
<div class="food-load"> // load into form upon clicking this
</div>
</div>
</li>
</food-item>
</ul>
</div>
Since I do not have the code for the form, this is my best guess without clarification.
You can add a click handler to the item you want to be clicked. It will pass the value of the food item into the method.
<div class="food-load" #click="setFoodItem(item)">
</div>
And when that method is called, it can assign the clicked item to a data property. I'm not sure where your form is, and if it is in a different component. If it is in a child component, you would have to pass it in as a prop, or emit an event to pass it to a parent component.
data() {
return {
//create a reactive field to store the current object for the form.
foodItemForm: null
};
},
methods: {
//method for setting the current item for the form.
setFoodItem(item) {
this.foodItemForm = item;
}
}
Missing quite a bit of info in your sample code, your script is very important to see to make sense of what you would like to accomplish and where things might be going wrong.
Here's a quick list of the issue I came across with your code:
v-for refers to an individual food item as 'item', inside the loop you're trying to access properties as 'food'
You don't wrap your code in a component unless you're importing the component
When binding a value to 'v-bind:src' (or shorthand ':src') only pass the url, you should be specifying this in your script not inline.
You're better off using a button and the 'v-on:click' (or shorthand '#click') to load your selected food item into your form
You should also include your Javascript
Regardless, here's how I would handle this (took the liberty in filling in some blanks):
<template>
<div id="app">
<ul id="food-list">
<!--<food-item v-for="item in foodList" v-bind:food="item" v-bind:key="item.id" inline-template>-->
<li v-for="item in foodList" class="food">
<div class="food-header">
<img :src="item.slug" v-bind:alt="item.slug" width="250px" height="auto">
<div class="food-title">
<p>{{item.name}} | <b>{{item.slug}}</b></p>
<p>quantity: {{item.quantity}}</p>
</div>
<button class="food-load" #click="loadFoodItem(item.id)">Load Food Item</button>
</div>
</li>
<!--</food-item>-->
</ul>
<form v-if="activeFoodId != null" id="foodItemForm" action="#">
<h3>Food Form</h3>
<label for="food-id">Id:</label>
<input id="food-id" type="number" v-bind:value="foodList[activeFoodId].id"><br/>
<label for="food-slug">Slug:</label>
<input id="food-slug" type="text" v-bind:value="foodList[activeFoodId].slug"><br/>
<label for="food-name">Name:</label>
<input id="food-name" type="text" v-bind:value="foodList[activeFoodId].name"><br/>
<label for="food-quantity">Quantity:</label>
<input id="food-quantity" type="number" v-bind:value="foodList[activeFoodId].quantity">
</form>
</div>
</template>
<script>
export default {
name: 'app',
data: function () {
return {
activeFoodId: null,
foodList: [
{
id: 1,
slug: 'http://3.bp.blogspot.com/-QiJCtE3yeOA/TWHfElpIbkI/AAAAAAAAADE/Xv6osICLe6E/s320/tomato.jpeg',
name: 'tomatoes',
quantity: 4
}, {
id: 2,
slug: 'https://img.purch.com/rc/300x200/aHR0cDovL3d3dy5saXZlc2NpZW5jZS5jb20vaW1hZ2VzL2kvMDAwLzA2NS8xNDkvb3JpZ2luYWwvYmFuYW5hcy5qcGc=',
name: 'bananas',
quantity: 12
}, {
id: 3,
slug: 'https://media.gettyimages.com/photos/red-apples-picture-id186823339?b=1&k=6&m=186823339&s=612x612&w=0&h=HwKqE1MrsWrofYe7FvaevMnSB89FKbMjT-G1E_1HpEw=',
name: 'apples',
quantity: 7
}
]
}
},
methods: {
loadFoodItem: function (foodItemId) {
console.log(foodItemId)
this.activeFoodId = foodItemId
}
}
}
</script>
<style>
/# Irrelevant #/
</style>
Hope it helps!

How can we achieve product filter page using angularjs with multi selection of checkboxes?

I have created a tabular page in which i need to filter the table with filters using a left side facets boxes of different category but with multi-selection options and i need to use Angularjs for this requirement.
I need to check/uncheck using the clear filter selection .
Any helping library can help to achieve the same or we need to do some logic around the checkboxes to achieve this.
My checkbox code looks like this:
<div class="col-sm-2" style="padding-top: 10px; padding-bottom: 20px;">
<div class="facetBx sltBx" ng-show="tabFilters.length > 0">
<p class="facetBxTitle"><i class="fa fa-filter"></i> Filter Selection
<a class="clrSlt" ng-click="clearAllFilters();">Clear</a>
</p>
<div class="facetBxChld" id="uRslctn">
<ul>
<li ng-repeat="item in tabFilters">
<div class="crop">
<strong title="{{item}}">{{item}}</strong>
</div>
<i class="fa fa-remove rmvThs" style="font-size: 14px;color:#000;float: right;" ng-click="checkItem(item, item,false);"></i>
</li>
</ul>
</div>
</div>
<div class="facetBx" ng-repeat="item in filters">
<p class="facetBxTitle bomtype">{{item.label}}</p>
<div class="facetBxChld" id="bomFacet">
<ul class="multiselect" style="max-height: 140px;overflow-y: auto;">
<li ng-repeat="(k,v) in item.values">
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(item.name, k, isSelected)'>
<span> {{k}} ({{v}})</span>
</li>
<li ng-show="value.length == 0">
No Data Available.
</li>
</ul>
</div>
</div>
</div>
Below website are the reference of the code which i am trying to build:
www.jabong.com
The UI(HTML) is done but i am facing the trouble in the maintaining the checking and un-checking of the checkboxes which are not clearing off.
I believe i need to code something in the ng-model of checkbox to achieve it but i am not able to be successfull so need help on the same.
Sample Plunkur for the same:
enter link description here
Thanks in advance.
Basically you need to keep track of ng-model of checkboxes with some property in your $scope. I did this by modifying your $scope.filters and adding selected property inside it, like below.
var filters = [{
label: 'Brand',
name: 'brand',
values: {
Blackberrys: 503,
Arrow: 175,
ParkAvenue: 358
}
}, {
label: 'Color',
name: 'color',
values: {
Black: 100,
Green: 200,
Red: 300
}
}]
function loadFilters() {
$scope.filters = filters.map(function(filter) {
var filter = angular.copy(filter);
for (var key in filter.values) {
filter.values[key] = {
selected: false,
count: filter.values[key]
}
}
return filter;
})
}
loadFilters();
Then you can call loadFilters() any time you want to clear all filters. Please see POC attached below.
https://plnkr.co/edit/SADPoUpftnJkg1rMuSB9?p=preview
You should try 'ng-change' on checkboxes to trigger a method which changes the values according to checked and unchecked checkboxes.
For Ex.
<input type="checkbox" ng-model='isSelected' ng-click='checkItem(key, k, isSelected)' ng-change="yourMethodHere()">
in JS:
$scope.yourMethodHere = function() {
if (isSelected) {
// filter the values here
} else {
// non filtered values
}
}
By doing this you do not need to even maintain the values of exiting checked/unchecked checkbox.

How to change an angular2 html dynamically?

Im trying to find a way to change some html lines of a component dynamically.
<li *ngFor="p in persons" [attr.id]="p.id">
<span> {{ p.name }} </span>
<a (click)="getAge(p.id)">Get Age<a/>
</li>
If the user clicks on the Get Age link i would like to replace the content inside of the corresponding li tag to something like:
<span> {{ p.OtherProperty }} </span>
<a (click)="OtherMethod(p)">Call OtherMethod<a/>
I found that ComponentFactoryResolver to create dynamic components, but i found it too overwhelming for just 2 lines of html. And i tried to change it by hand using jquery but it does not work to create the event bindings:
getAge(id) {
//some work
//remove the corresponding <li> content
$('#' + id).append('<a (click)="getAnotherValue(p.name)">GetAnotherValue<a/>');
$('#' + id).append('<span> {{ p.age}} </span>'); //this obviously doesnt work. But thats the ideia.
}
So how can i replace some html tags with angular attributes dynamically?
You could access the person's Object property dynamically like this:
object[property]; // Where property is a string
// If property equals 'name', the above sentence would equal this:
object.name; // or `object['name'];`
So, following your example, you could do this:
export class App {
persons = [
{
id: 1,
name: 'John',
age: 25
},
{
id: 2,
name: 'Mike',
age: 30
}
];
selectedProperty = 'name';
constructor() {
}
getProperty(prop) {
this.selectedProperty = prop;
}
}
And in your template you could:
<div>
<li *ngFor="let p of persons" [attr.id]="p.id">
<span> {{ p[selectedProperty] }} </span>
<br>
</li>
<button (click)="getProperty('age')">Get Age</button>
<button (click)="getProperty('name')">Get Name</button>
</div>
If I understood well, this should do the trick. You can't use ngIf because if you have 60 properties or persons then will be somewhat caothic.
Check the Plunker
Use ngIf to activate the code as shown:
<li *ngFor="p in persons" [attr.id]="p.id">
<div *ngIf=!p?.touched>
<span> {{ p.name }} </span>
<a (click)="getAge(p)">Get Age<a/>
</div>
<div *ngIf=p?.touched>
<span> {{ p.age}} </span>
<a (click)="getAnotherValue(p.name)">GetAnotherValue<a/>
</div>
</li>
isGetAgeClicked=false;
getAge(person) {
//some work
//remove the corresponding <li> content
person.touched=true
}

Categories

Resources