Vue.js: How to read $index inside computed function? - javascript

I have list of links:
<ul id="menu">
<li v-for="item in items">
<a href v-bind:href=link>{{item.message}}</a>
</li>
</ul>
var example1 = new Vue({
el: '#menu',
data: {
items: [
{ message: 'Link1' },
{ message: 'Link2' }
]
},
computed: {
link: function() {
return f($index) // how do I access current array index ??
}
}
})
I can achieve the desired result by using mustache markup in a href attribute but it must be possible to access this $index variable from within a computed function?

You can't.
This is what methods are for:
<a href v-bind:href="link($index)">{{item.message}}</a>
methods: {
link: function(index) {
return f(index)
}
}

Related

Vue.js v-for element from $refs broken in watch function

With Vue.js 2.5.22 and FireFox 65.0. What am I missing?
https://jsfiddle.net/r083hqgv/2/
A v-for element identified by a :ref="x" attribute doesn't work as expected in a watch function. I've also tried using :id="x" & getElementById(), and calling setTimeout(..., 200) within $nextTick().
Code from the above fiddle:
<div id="app" style="position:relative">
<h2>last element top: {{offset+''}}</h2>
<button #click="add()">Add & get top</button>
<ol>
<li v-for="a in list" :key="'r.'+a">
<a #click.stop.prevent="get($event.target)" href="#"
:ref="'r.'+a">get top {{'r.'+a}}</a>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
offset: 0,
last: 'unset',
list: [],
},
methods: {
add: function() {
this.last = 'r.'+ this.list.push(this.list.length+1);
this.list = this.list.slice();
},
get: function(iEl) {
this.offset = iEl.offsetTop;
iEl.style = 'font-style:italic';
}
},
watch: {
list: function() {
this.$nextTick(function() {
var aEl = this.$refs[this.last];
if (aEl) this.get(aEl);
});
}
}
})
As referenced by the documentation ($refs), this.$refs["..."] returns an array when v-for is used. Therefore, change
if (aEl) this.get(aEl);
to
if (aEl) this.get(aEl[0]);
Everything will work (I already tested it on your jsfiddle).

Pass click action using array and v-for (Vue.js)

I am trying to pass through a v-for the text and the #click action of each li. For the text I know how to do it...but for the click action?enter code here
Each item of the array menuOptions (which is in the 'data' part of my Vue component) is structured like this :
{name: "firstOption",action: "console.log('first option called')"}
The first parameter is the name of the option, the
<ul>
<li v-for="option in menuOptions" #click="option.action">{{option.name}}</li>
</ul>
Do you have some ideas? (I guess that's maybe a pure JS question, but maybe there are possibilities to do it Vue too?)
Pass a function expression to the action property, instead of a "stringified" function.
{
name: 'firstOption',
action: function() {console.log('first option called'}
}
var app = new Vue({
el: '#app',
data: {
menuOptions: [{
action: function() {
console.log('foo')
},
name: 'foo'
}, {
action: function() {
console.log('bar')
},
name: 'bar'
}],
}
})
<script src="https://unpkg.com/vue"></script>
<div id='app'>
<ul>
<li v-for="option in menuOptions" #click="option.action">{{option.name}}</li>
</ul>
</div>

Vue Js binding 2 values to a tag href

I'm beginner on Vue js. I'm facing an issue where I have to update two values while rendering.
<ul class="category-list">
<li v-for="category in categories">
<a v-bind:href="location/category.slug/all" v-text="category.name"></a>
</li>
</ul>
My javascript file
new Vue({
el : '#example',
data : {
location: 'new-york',
categories: [],
},
mounted() {
axios.get('parent-categories').then((response) => {
this.categories = response.data;
});
},
});
Ajax Response
[{"name":"Default Category","slug":"default-category"},{"name":"Main Category","slug":"main-category"}]
Here I wanted to build url structure like location/category-slug/all ie,
http://myapp.com/new-york/default-category/all
How to achieve this?
Found out how to handle this situation after a long struggle. All I have to do is to create a method that generates URL for me.
JS
new Vue({
el : '#example',
data : {
location: 'new-york',
categories: [],
},
methods: {
slug: function (category) {
return this.location+'/'+category.slug+'/all';
}
},
mounted() {
axios.get('parent-categories').then((response) => {
this.categories = response.data;
});
},
});
Html
<div id="example">
<h3>Services</h3>
<ul class="category-list">
<li v-for="category in categories">
<a :href="slug(category)" v-text="category.name"></a>
</li>
</ul>
</div>

Toggle class on click in VueJS

I am learning Vuejs and I am constantly finding simple things like removing a class to be a pain. Please tell me how I can allow the .active class to be added or removed based on clicks between the 3 links.
In the example below the adding is working fine but it adds .active to all the links, and does not remove when clicked on again.
<div id="app">
<h2>App</h2>
<ul>
<li>Link text</li>
<li>Link text</li>
<li>Link text</li>
</ul>
</div>
JS
var app = new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
activeLink() {
// the line below did not work
// document.getElementsByClassName("active").isActive = false,
this.isActive = true
}
}
})
JSfiddle is here, https://jsfiddle.net/s9r4q0gc/
You need to catch the event handler in the method and using that you can refer to the callee i.e. anchor object in this case.
See the fiddle : https://jsfiddle.net/s9r4q0gc/2/
activeLink(event) {
if(event.target.className == "noclass")
{
event.target.className = "link active";
}
else
{
event.target.className = "noclass";
}
}
UPDATED:
May be try this fiddle and see if it is hitting the bulls eye : https://jsfiddle.net/s9r4q0gc/4/
var app = new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
activeLink(event) {
var checkboxes = document.getElementsByClassName ("noclass");
for (var i=0; i<checkboxes.length; i++) {
checkboxes[i].className = "link active";
//checkboxes[i].className = "link";
}
event.target.className = "noclass";
}
}
})
What you can do is use a data property to hold the currently active link. Then with that you'll be able to have any given link test if it is the active link and if the .active class should be applied.
Then it's just a matter of setting that property's new value when a link is clicked. If the same link that's currently active is clicked, then the property is cleared, thus removing the .active class from all links. Otherwise, the class is added to the link that was clicked.
This is a CodePen demonstrating what I mean in action, but your markup could look something like this:
<li><a href="#" class="link" :class="{ active: activeId == 'link-1' }"
#click.prevent="activeLink('link-1')">Link text</a></li>
<li><a href="#" class="link" :class="{ active: activeId == 'link-2' }"
#click.prevent="activeLink('link-2')">Link text</a></li>
<li><a href="#" class="link" :class="{ active: activeId == 'link-3' }"
#click.prevent="activeLink('link-3')">Link text</a></li>
and your JS could look something like this:
data: {
activeId: null
},
methods: {
activeLink(linkIdent) {
this.activeId = this.activeId === linkIdent ? null : linkIdent
}
}
It's obviously not as clean a solution as it could be, but I am sticking to your requirement that the solution fit the markup you provided.
Here is an alternative solution that may modify your code more than you want. I thought this might be useful since you are just learning and may be interested in an alternative. I started by declaring your links as an array of objects in vue so that we can assign an active attribute to each of them. Then we can just toggle the active value inline or use the toggleActive function (not currently in use.. just there for illustration if you prefer to use functions over inline logic).
Html:
<div id="app">
<h2>App</h2>
<ul>
<li v-for="l in links">
{{l.text}}
</li>
</ul>
</div>
Javascript:
var app = new Vue({
el: '#app',
data: {
links: [{
text: "Link text",
active: false
},{
text: "Second text",
active: false
},{
text: "Third text",
active: false
}]
},
methods: {
//To use this function make #click in html to:
//#click="toggleActive(l)"
toggleActive(x) {
x.active = !x.active;
}
}
})
https://jsfiddle.net/yrbt90v9/
Use V-for and an array of items so you have no need to statically type the links. This allows for the dynamic functionality you are looking for.
var app = new Vue({
el: '#app',
data: {
links: [
{text: "Link Text", active: false},
{text: "Link Text", active: false},
{text: "Link Text", active: false}
]
},
methods: {
activate(link) {
link.active = !link.active
}
}
})
.link{
text-decoration: none;
color: #555;
}
.active{
text-decoration: underline;
color: #42b983;
}
<div id="app">
<h2>App</h2>
<ul>
<li v-for="link in links">Link text</li>
</ul>
</div>

AngularJS Select from Json (similiar to MySQL)

I have a json:
{"sectionTitle":"Account Information","sectionItems":[{"itemTitle":"Balance","url":"/account/balance","selected":true},{"itemTitle":"Account Statement","url":"/account/statementsearch","selected":false},{"itemTitle":"Deposit","url":"/account/deposit","selected":false},{"itemTitle":"Withdrawal","url":"/account/withdraw","selected":false},{"itemTitle":"Edit Profile","url":"/account/editprofile","selected":false},{"itemTitle":"Change Password","url":"/account/changepassword","selected":false}]}
Now I just want to check if there is an item (child) inside sectionTitle where selected is true.
Something like this in SQL
SELECT * FROM sectionItems WHERE selected=true
Can I do something similar in angular js, so I can check if the the parents has children?
I hope you understood my question.
This is my html
<nav class="sidebar-nav">
<ul class="nav metismenu" id="side-menu-help">
<li ng-repeat="menuItem in accountCtrl.menuStructure">
<a class="{{ (menuItem.sectionItems.length > 0) ? 'metisHasChildren' : '' }}" href="/en/help-area/poker-help/poker-rules/">
<span ng-if="menuItem.sectionItems.length > 0" class="fa arrow fa fa-angle-double-down"></span>
{{ ::menuItem.sectionTitle }}
{{ ::menuItem }}
</a>
<ul class="nav nav-second-level collapse in">
<li ng-repeat="subMenuItem in menuItem.sectionItems" ng-click="accountCtrl.changePage(subMenuItem.url)">
<a ng-class="(subMenuItem.selected) ? 'page-active' : ''">{{ ::subMenuItem.itemTitle }}</a>
</li>
</ul>
</li>
</ul>
</nav>
You do not need to anything fancy to get this working. Simply convert the json to an object and access the property you want using dot notation. So for example:
var json = JSON.parse(json);
var selectedItems = [];
angular.forEach(json.sectionItems, function(sectionItem) {
if (sectionItem.selected) {
selectedItems.push(sectionItem);
}
});
Would convert the json string to an object and then loop over each sectionItem child, check for selected is true and create an array of matching items.
You can use a forEach loop. This example will return an array of all sectionItems where selected equals true, but you can return whatever you'd like.
$scope.items =[];
angular.forEach(sectionItems, function(item){
if (item.selected === true){
$scope.items.push(item);
}
})
UPDATE
Here's a working plunk
To make this work inline with ng-repeat you will use it in a filter, like this:
app.filter('menuFilter', function() {
return function(menuItems) {
var filtered = [];
angular.forEach(menuItems, function(menuItem) {
angular.forEach(menuItem.sectionItems, function(item) {
if (item.selected === true) {
filtered.push(menuItem);
}
});
});
return filtered;
}
});
And change your markup, like this:
ng-repeat="menuItem in accountCtrl.menuStructure | menuFilter "
First, you don't need to do any custom filter or anything like that, just use the standard filter, as below:
<li ng-repeat="menuItem in accountCtrl.menuStructure | filter: { sectionItems: { selected: true } }"> {{ menuItem.sectionTitle }}
Working demo:
angular.module('app', [])
.controller('accountController', function() {
var vm = this;
vm.menuStructure = [
{
"sectionTitle":"Account Information",
"sectionItems":[
{
"itemTitle":"Balance",
"url":"/account/balance",
"selected":true
},
{
"itemTitle":"Account Statement",
"url":"/account/statementsearch",
"selected":false
},
{
"itemTitle":"Deposit",
"url":"/account/deposit",
"selected":false
},
{
"itemTitle":"Withdrawal",
"url":"/account/withdraw",
"selected":false
},
{
"itemTitle":"Edit Profile",
"url":"/account/editprofile",
"selected":false
},
{
"itemTitle":"Change Password",
"url":"/account/changepassword",
"selected":false
}
]
},
{
"sectionTitle":"Account Information 2",
"sectionItems":[
{
"itemTitle":"Balance",
"url":"/account/balance",
"selected":false
},
{
"itemTitle":"Account Statement",
"url":"/account/statementsearch",
"selected":false
},
{
"itemTitle":"Deposit",
"url":"/account/deposit",
"selected":false
},
{
"itemTitle":"Withdrawal",
"url":"/account/withdraw",
"selected":false
},
{
"itemTitle":"Edit Profile",
"url":"/account/editprofile",
"selected":false
},
{
"itemTitle":"Change Password",
"url":"/account/changepassword",
"selected":false
}
]
}
];
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="accountController as accountCtrl">
<ul>
<li ng-repeat="menuItem in accountCtrl.menuStructure | filter: { sectionItems: { selected: true } }"> {{ menuItem.sectionTitle }}
<ul class="nav nav-second-level collapse in">
<li ng-repeat="subMenuItem in menuItem.sectionItems" ng-click="accountCtrl.changePage(subMenuItem.url)">
<a ng-class="{ 'page-active': subMenuItem.selected }" ng-bind="::subMenuItem.itemTitle"></a>
</li>
</ul>
</li>
</ul>
</body>
</html>
Tips:
Instead of using ngClass with ternary operator, you can simply use this way:
ng-class="{ 'page-active': subMenuItem.selected }"
Even if works in this way that you're using, I'd recommend you to take a look on the special-repeats, it fits really well in your situation.
I hope it helps!!
var JSONStr=[{"sectionTitle":"Account Information","sectionItems":[{"itemTitle":"Balance","url":"/account/balance","selected":true},{"itemTitle":"Account Statement","url":"/account/statementsearch","selected":false},{"itemTitle":"Deposit","url":"/account/deposit","selected":false},{"itemTitle":"Withdrawal","url":"/account/withdraw","selected":false},{"itemTitle":"Edit Profile","url":"/account/editprofile","selected":false},{"itemTitle":"Change Password","url":"/account/changepassword","selected":false}]}];
var result = JSONStr.where({ selected: true });

Categories

Resources