vue way of .each() - javascript

I have a hard coded menu.vue file that basically gets the url slug and adds a class of active if it matches the <a> href. It works just fine but I am sure there is a cleaner way of doing it so I am not copy/pasting the same code over and over. In Jquery I know I could just loop through all the <a> in an each and see if slug matches the href and addClass
menu.vue
<template>
<div>
<!-- Main site menu -->
<nav>
<ul>
<li>about</li>
<li>faq</li>
<li>signup</li>
<li>login</li>
<li>Contact</li>
</ul>
</nav>
</div>
</template>
<script>
export default {
data(){
return {
slug: ''
}
},
created(){
var urlArray = window.location.pathname.split( '/' );
this.slug = urlArray[1];
},
}
</script>

Example: https://jsfiddle.net/wostex/63t082p2/46/
<div id="app">
<p v-for="link in links" :key="link">
<a :href="'/'+link" :class="{'active': slug == link}">{{link}}</a>
</p>
</div>
new Vue({
el: '#app',
data: {
links: [
'about',
'home',
'faq'
]
}
});
In this example each link has a corresponding class, e.g. 'home' class, 'faq' class.

Related

Event and Data Handling Vue.js

I am new to Vue.js and I have trouble on answering my code activities:
So the menu button would open selections from the drop down box upon clicking, and when you click any of the selections, it would go back to the menu button
index.js
const vm = new Vue({
el: '#root',
data: () => {
{return {menuClick: true}}
},
methods: {
methodClick(e){
console.log(event.target.value)
}
},
template: `
<div #click="methodClick">
<a class='dropdown-button btn' href='#' v-if="methodClick">Open</a>
<ul class='dropdown-content'v-else="!methodClick">
<li>one</li>
<li>two</li>
<li class="divider"></li>
<li>three</li>
</ul>
</div>
`
});
index.html
<!--Don't edit this! Change to the index.js file (click on the link on the left side) and edit that file-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
<style>.dropdown-content{ display: block !important; opacity: 1 !important; }</style>
<div id="root">
</div>
<!--Don't edit this! Change to the index.js file (click on the link on the left side) and edit that file-->
When I console log it, it just says it is undefined.
You haven't made use of the attribute 'menuClick'
const vm = new Vue({
el: '#root',
data: () => {
return {menuClick: false} // changes added
},
methods: {
methodClick(e){
console.log(event.target.value);
this.menuClick = !this.menuClick; // changes added
}
},
template: `
<div #click="methodClick">
<a class='dropdown-button btn' href='#' v-if="!menuClick">Open</a> // changes added
<ul class='dropdown-content' v-else> // changes added
<li>one</li>
<li>two</li>
<li class="divider"></li>
<li>three</li>
</ul>
</div>
`
});

mouseover event not firing till clicked on Google Chrome

I'm trying to run a function when the cursor is over a list item like so:
<div id="vue-app">
<ul>
<li v-for="item in items" #mouseover="removeItem(item)">{{item}}</li>
</ul>
</div>
new Vue({
el: '#vue-app',
data: {
items: ['meat', 'fruits', 'vegetables'],
},
methods: {
removeItem(value) {
...
}
},
});
however the mouseover event only fires when I click on the list item. What am I not doing correct here?
MouseOver
MouseClicked
Check this working code
new Vue({
el:'#vue-app',
data:{
items:['meat','fruits','vegetables'],
},
methods:{
removeItem(value){
console.log(value);
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.7/vue.js"></script>
<div id="vue-app">
<ul>
<li v-for="item in items" #mouseover="removeItem(item)">{{item}}</li>
</ul>
</div>

How to render a list of static content with Vue named slot?

I have trouble figuring out how to get the following to work:
My parent template
<comp>
link 1
link 2
</comp>
and my component comp template looks like the following:
<ul class="comp">
<li class="comp-item"><slot name="links"></slot></li>
</ul>
currently all my anchors goes to that single li tag (which is expected)
but I would like to be able to produce multiple li for every named slot I inserted like the following:
<ul class="comp">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
Is there any way to achieve what I need without using scoped slot? Because my content is pure HTML so I feel it is unnecessary to put static content inside prop in order to render them.
From what I have seen, most vue UI framework requires you to use another custom component for the list item, which I feel is over killed for the problem. Is there any other way to do this?
This is easily accomplished with a render function.
Vue.component("comp", {
render(h){
let links = this.$slots.links.map(l => h('li', {class: "comp-item"}, [l]))
return h('ul', {class: 'comp'}, links)
}
})
Here is a working example.
console.clear()
Vue.component("comp", {
render(h){
let links = this.$slots.links.map(l => h('li', {class: "comp-item"}, [l]))
return h('ul', {class: 'comp'}, links)
}
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp>
link 1
link 2
</comp>
</div>
Or with the help of a small utility component for rendering vNodes you could do it like this with a template.
Vue.component("vnode", {
functional: true,
render(h, context){
return context.props.node
}
})
Vue.component("comp", {
template: `
<ul class="comp">
<li class="comp-item" v-for="link in $slots.links"><vnode :node="link" /></li>
</ul>
`
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp>
link 1
link 2
</comp>
</div>
You can make use of scoped slots instead of slots
Your comp component receives a prop links which is an array of links(since static initialized as a custom option). Iterate over the links and pass link as data to the slot just like passing props to a component
Vue.component("comp", {
template: `
<ul class="comp">
<li class="comp-item" v-for="link in links">
<slot v-bind="link"></slot>
</li>
</ul>
`,
props: ["links"]
})
new Vue({
el: "#app",
// custom static option , accessed using vm.$options.links
links: [
{text: "link1"},
{text: "link2"},
{text: "lin3"}
]
})
In the parent where the comp component is used make use of a <template> element with a special attribute slot-scope , indicating that it is a template for a scoped slot.
The value of slot-scope will be used as the name of a temporary variable that holds the props object passed from the child:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<comp :links="$options.links">
<template slot-scope="link">
{{link.text}}
</template>
</comp>
</div>
Here is the working fiddle
If you don't like to put your data in array, and render list with v-for
You can put all of them in the component, no slot:
<ul class="comp">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
or with slot:
<comp>
<ul class="comp" slot="links">
<li class="comp-item">link 1</li>
<li class="comp-item">link 2</li>
</ul>
</comp>

My load more button is not showing despite everything being correct according to me

HTML side code
<template name="profPageAuthorSubmit">
<div>
<ul style="list-style-type:none" class="pclProf">
<li><img id="profilePagePic" src="{{profilePagePicture}}"><span class="username">{{authorSubmit}}</span></li>
<li class="accesories">{{a}} Poems</li>
<li class="accesories">{{b}} Short stories</li>
<li class="accesories">{{c}} Followers</li>
<li class="accesories">{{d}} Followings</li>
</ul>
</div>
<div>
<ul style="list-style-type:none" class="pcl">
{{#each Contents}}
<li class="p">
<p class="bucket">
<li><span class="contentType">{{type}}</span></li>
<li><span class="titleBody"><strong>{{{title}}}</strong></span></li>
<li><img class="authorPic" src="{{authorPhoto}}"><span class="authorName">{{author}}</span></li>
<li><span class="createdAtTime">Written at {{createdAt}}</span></li>
<li class="contentMain">{{{content}}}</li>
{{>contentEssentials}}
{{#if editing}}
{{>contentEdit}}
{{/if}}
{{>commentsTemp}}
</p>
</li>
{{/each}}
**{{#if nextPath}}
Load more
{{#unless ready}}
{{> spinner}}
{{/unless}}
{{/if}}**
</ul>
</div>
</template>
Client side code
profileController= RouteController.extend({
template: 'profPageAuthorSubmit',
increment: 10,
contentsLimit: function() {
return parseInt(this.params.contentsLimit) || this.increment;
},
findOptions: function() {
return {sort: {createdAt: -1}, limit: this.contentsLimit()};
},
subscriptions: function() {
this.ContentsSub = Meteor.subscribe('contents', this.findOptions());
return Meteor.subscribe('allUserData');
},
Contents: function() {
var locationArray= location.href.split('/');
var UserId= locationArray[4];
return contentsList.find({createdBy: UserId, anonymous: false}, this.findOptions());
},
data: function() {
var hasMore = this.Contents().count() === this.contentsLimit();
var nextPath = this.route.path({contentsLimit: this.contentsLimit() + this.increment});
console.log("ready");
return {
Contents: this.Contents(),
ready: this.ContentsSub.ready,
nextPath: hasMore ? nextPath : null
};
}
});
Router.map(function(){
Router.route('profPageAuthorSubmit',{
path: '/user/:createdBy/:contentsLimit?',
controller: profileController
});
});
Why is load-more button not showing. I have checked it again and again but still it is not showing. The same thing has worked in other templates.
Basically it is checking whether the contents number is greater than the number displayed, if it is then the load more button should show.
Thanking all
As far as i know, every child of a "ul" tag must be a "li" tag. So the "a" isnt recognized. Try to put the content inside a "li" tag.

Meteor: Change the class of link on click

Template:
<template name="header">
<div class="blog-masthead">
<div class="container">
<nav class="blog-nav">
<a class="blog-nav-item {{active}}" href="{{pathFor 'homePage'}}">Home</a>
{{#if currentUser}}
<a class="blog-nav-item {{active}}" href="#">{{Meteor.userId}}</a>
{{else}}
<a class="blog-nav-item {{active}}" href="{{pathFor 'loginPage'}}">Login</a>
<a class="blog-nav-item {{active}}" href="{{pathFor 'signUpPage'}}">Sign Up</a>
{{/if}}
<a class="blog-nav-item {{active}}" href="#">About</a>
</nav>
</div>
</div>
</template>
header.js
Template.header.events({
'click a': function(e) {
e.preventDefault();
this.active?"active":"";
}
});
I just want to make set click link's class as active. Clicked link class should look like class="blog-nav-item active".
The example below relies on iron-router and presents a reactive approach for adding the active class
First should modify the template to be:
<a class="blog-nav-item {{active 'homePage'}}" href="{{pathFor 'homePage'}}">Home</a>
And then the appropriate helper for it should be:
Template.name.helpers({
active: function(routeName) {
var curRoute = Router.current().route;
return curRoute.name === routeName ? 'active' : '';
}
});
Edit v1.0+
As of iron-router 1.0, A route's name is now accessible at
route.getName() (previously it was route.name).
In particular, you'll need to write return
curRoute.getName() === routeName ? 'active' : '';
Thank you #jrbedard for the notification
I guess this would be the "Meteor way" you wanted:
<template name="menu">
<ul>
<li class="{{#if isCurrentPage 'pageA'}}active{{/if}}">
Page A
</li>
<li class="{{#if isCurrentPage 'pageB'}}active{{/if}}">
Page B
</li>
<li class="{{#if isCurrentPage 'pageC'}}active{{/if}}">
Page C
</li>
</ul>
</template>
Router.onBeforeAction(function(){
Session.set('currentRouteName', Router.current().route.name)
})
Template.menu.helpers({
isCurrentPage: function(pageName){
return Session.equals('currentRouteName', pageName)
}
})
Router.current().route.name do evaluate to the current page name in the latest version of iron router, but I don't know if that's part of the public API one should use.
EDIT
Router.current() is apparently reactive, so all JavaScript code you need is the following:
Template.menu.helpers({
isCurrentPage: function(pageName){
return Router.current().route.name == pageName
}
})
You can solve it with JavaScript only:
Template.header.events({
'click .blog-nav-item': function(event, template){
// Remove the class 'active' from potentially current active link.
var activeLink = template.find('.blog-nav-item')
if(activeLink){
activeLink.classList.remove('active')
}
// Add the class 'active' to the clicked link.
event.currentTarget.classList.add('active')
}
})

Categories