Here is my replies.vue file. Here what i am doing is when this replies component is created, i call a fetch() method that loads some data from the server:
//Replies.vue
<template>
<div>
<div v-for="(reply, index) in replies_with_pagination.data">
<reply :attributes="reply" :key="reply.id" :current_user_id="auth_user_id" #iamdeleted="updateCollection(index)"></reply>
</div>
<pagination :current_paginated_dataSet="replies_with_pagination"></pagination>
</div>
</template>
<script>
import Reply from './Reply.vue';
export default{
components: { Reply },
props: ['all_replies','auth_user_id','thread_id'],
data() {
return {
all_reply_items: this.all_replies,
replies_with_pagination: '',
}
},
methods: {
fetch: function(){
let vm = this;
axios.get(route('replies.paginated',{ 'thread' : this.thread_id
}))
.then(function(serverResponse){
vm.replies_with_pagination = serverResponse.data;
});
},
},
created() {
this.fetch();
}
And then i am passing that to the pagination component via the current_paginated_dataSet props. Here is the code for pagination.vue
//Pagination.vue
<template>
<div>
<nav aria-label="...">
<ul class="pagination">
<li class="page-item disabled" v-show="current.prev_page_url">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active">
<a class="page-link" href="#">2 <span class="sr-only">(current)</span></a>
</li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item" v-show="current.next_page_url">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
</div>
</template>
<script>
export default{
props: ['current_paginated_dataSet'],
data() {
return {
current: this.current_paginated_dataSet,
}
},
}
</script>
As you can see i am using this property to initialize a data called current in the pagination component. This current_paginated_dataSet property was initialized at the replies component to the value of FALSE, which, upon fetching the data from server was then reset to some object returned by the server.
My problem is the previous and next buttons on the pagination component are not visible. It seems the current data property on the pagination component is not getting the updated value from the replies component which updates after loading data from server. It is always set to the intial value of FALSE that it received from parent root component.
What am i doing wrong here?
EDIT
it works if instead of referring to the current data property i directly refer to the current_paginated_dataSet props in the previous and next buttons, like so:
// this works:
<li class="page-item disabled" v-show="current_paginated_dataSet.prev_page_url">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
But i want to achieve this by referring to the data property current. How can i do that?
Related
I am building a single page application using vue.js with materialize and I have a navbar with tabs to navigate to different pages on my website. Materialize's tabs has an active property that displays which tab is currently selected. Every thing this far works perfectly.
This is materialize's tabs reference
The Issue
If you refresh the web page the tabs active property 'resets' to its default position. So I am trying to figure out how to persist the state of the navbar's active property and then reassign it to router-link.
My Code
<ul id="tabs" class="tabs tabs-transparent" :class="{ 'navbar--color': !changeNavColor }">
<li class="tab" id="home">
<router-link
id="home"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="/"
>Home</router-link>
</li>
<li class="tab" id="services">
<router-link
id="services"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Services"
>Service</router-link>
</li>
<li class="tab" id="Preapproved">
<router-link
id="Preapproved"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="PreApproved"
>Get Pre-Approved</router-link>
</li>
<li class="tab">
<router-link
id="cars"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Cars"
>inventory</router-link>
</li>
<li class="tab">
<router-link
id="testimonials"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Testimonials"
>Testimonals</router-link>
</li>
</ul>
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var activeTab = document.getElementById(this.currentTab);
activeTab.classList.add("active");
console.log("saved");
}
$(document).ready(function() {
$("a").click(function(event) {
this.currentTab = event.target.id;
console.log(this.currentTab);
});
});
$(document).ready(function() {
$(".tabs").tabs();
});
window.addEventListener("scroll", this.onScroll);
},
beforeDestroy() {
window.removeEventListener("scroll", this.onScroll);
},
watch: {
currentTab(newTab) {
console.log("watch");
localStorage.currentTab = this.currentTab;
}
}
any help is appreciated
So I think you can use hash mechanism by using window.location.hash. You can put the data-toggle attribute in every tag anchor. And just check for location.hash !== ''
But the more Vue way would be through client side storage. Check the link its from vue docs. Although it is for the input field but you will get the idea of how to persist info by keeping a reference in client storage and on reload getting your desired state from there onward. Hope it helps.
Remove the indicator that is nested inside the first li, and the navigation works fine.
<ul id="tabs" class="tabs tabs-transparent">
<li id="tab" class="tab">
Home
<li class="indicator"></li> <!-- THIS RANDOM NESTED INDICATOR IS THE ISSUE -->
</li>
<li id="tab" class="tab">
Service
</li>
<li id="tab" class="tab">
Get Pre-Approved
</li>
<li id="tab" class="tab">
inventory
</li>
<li class="indicator" style="right: 488px; left: 0px;"></li> <!-- CORRECT INDICATOR, AUTOMATICALLY GENERATED BY MATERIALIZE -->
</ul>
This is how I ended up implementing this. First all of your router-links or <a> have to have unique id's
mounted() {
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var element = document.querySelector(this.currentTab);
element.classList.add("active");
}
$(document).ready(function() {
$(".tabs").tabs();
$("a").click(function(event) {
this.currentTab = "#" + event.target.id;
localStorage.currentTab = this.currentTab;
});
});
},
watch: {
currentTab(newTab) {
localStorage.currentTab = this.currentTab;
}
}
I'm trying to create a simple reactive navigation based on if a user is authenticated or not. A login method on a login view sets a token in localstorage. If set, I want to display the logout button. I've tried computed, standard methods and props to no avail.
When I login, the navigation does not update. HOWEVER, if I refresh the page (reinitialize the app), it does show the logout button.
What exactly am I doing incorrectly?
I have been trying for hours to grasp the concept of Vue JS and am on the brink of quitting. What would have taken me minutes to do server side has taken me hours client side. WHERE IS THE REACTIVITY?
Nav.vue
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="#">App</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">
<router-link to="/">Home</router-link>
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<router-link to="/about" class="nav-link">About</router-link>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item" v-if="hasAuth()"><a #click="logout()" class="nav-link">Log Out</a></li>
<template v-else>
<li class="nav-item">
<router-link to="/register" class="nav-link">Register</router-link>
</li>
<li class="nav-item">
<router-link to="/login" class="nav-link">Login</router-link>
</li>
</template>
</ul>
</div>
</nav>
</template>
<script>
export default {
name: 'Nav',
data: () => {
return {
auth: false
}
},
methods: {
logout: function () {
localStorage.removeItem('user-token');
this.$router.push({ path: 'login' });
},
hasAuth: function () {
this.auth = (localStorage.getItem('user-token')) ? true : false;
return this.auth
}
},
};
</script>
App.vue
<template>
<div id="app">
<Nav></Nav>
<router-view/>
</div>
</template>
<script>
import Nav from '#/components/Nav.vue';
export default {
components: {
Nav,
},
}
</script>
While Vue.js is reactive, localStorage is not. Vue cannot possibly know if the localStorage is modified or not. There is no local change event available with local storage.
To solve this problem, use Vuex combined with Local Storage for persistent data. The point where you save the token to local storage, at that time also save a copy inside Vuex store.
Another component like Nav should read data from Vuex store which is reactive. When you refresh the page, initialize Vuex store with the data from localStorage.
This way you get a perfect reactive system.
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 4 years ago.
I'm trying to use jQuery to add a click function to a list item. I had this method working using JS and onClick(), however I am trying to bring the code up to date with using jQuery. So my list has the following structure:
<ul id="paginationList" class="pagination">
<li id="1" class="page-item disabled">
<a class="page-link" href="#">Previous</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li id="2" class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
The jQuery function is on the document.read function:
$(function () {
$('ul.pagination li.page-item').click(function () {
var $li = $(this);
$.ajax({
type: 'GET',
url: '/Projects/ProjectTable/CountProjects',
data: { searchString: $('#searchBox').val() },
success: function(result) {
$('#table-container').load('/Projects/ProjectTable/TestPartialView',
{
searchString: $('#searchBox').val(),
currentPage: page,
projectCount: result
}, function() {
$li.addClass('active');
});
}
});
});
getTable();
});
getTable() is being executed, however when I click on any <li> the method is not called. I have tried removing ul.pagination from the .click call but again that has had no impact.
What do I need to change so that the <li> have a .click method on each of them?
I have checked the console and there are no errors being reported at all.
There are a few problems with your code.
it seems you never remove the "disabled" class, even when you add the "active" one (but maybe that is what you want), and you are not using event delegation so maybe your even was binding before the <li> elements were created.
Try this:
$('.pagination').on("click", ".page-item", function(){
console.log(this);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="paginationList" class="pagination">
<li id="1" class="page-item disabled">
<a class="page-link">Previous</a>
</li>
<li class="page-item">
<a class="page-link">1</a>
</li>
<li id="2" class="page-item">
<a class="page-link">Next</a>
</li>
</ul>
Here is the same code, written in a more understandable way (IMHO)
$(()=>{
$('.pagination').on("click", ".page-item", onPaginationItemClick);
function onPaginationItemClick(){
var listItem = this,
// it's nicer to assign jQuery ajax calls to a variable, so it could be aborted if needed
CountProjects_REQ = $.ajax({
type : 'GET',
url : '/Projects/ProjectTable/CountProjects',
data : { searchString: $('#searchBox').val() }
});
// using the "done()" method on the jQuery XHR object. do not use "success", it is the old and aweful way
CountProjects_REQ.done(RES => {
$('#table-container').load('/Projects/ProjectTable/TestPartialView', {
searchString : $('#searchBox').val(),
currentPage : page,
projectCount : RES
}, ()=>{
listItem.classList.add('active');
});
})
};
// I have no idea what this part is for...I'll just leave it here
getTable();
});
$('ul.pagination li.page-item') works for the click event:
$('ul.pagination li.page-item').click(function () {
console.log('clicked');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="paginationList" class="pagination">
<li id="1" class="page-item disabled">
<a class="page-link" href="#">Previous</a>
</li>
<li class="page-item">
<a class="page-link" href="#">1</a>
</li>
<li id="2" class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
If you are adding the li dynamically then you can better use,
$(document).on('click', 'ul.pagination li.page-item', function () {
console.log('clicked');
});
My code is like this :
<template>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
#for(i=0;i<total;i++)
<li>{{i}}</li>
#endfor
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</template>
<script>
export default{
props:['total', 'data'],
created: function(){
console.log(this.$store.state.product.next_page);
},
computed:{
next:function(){
return this.$store.state.product.next_page
}
}
}
</script>
I try the code. But it does not work.
There exist error :
[Vue warn]: Property or method "i" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
For example, variable total = 5, then it will display like this :
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
How can I do it?
From Range v-for
<li v-for="i in total">
{{i}}
</li>
Just make sure total is an integer and not a string. For example, instead of
<component-name total="5"...
use
<component-name :total="5" ...
See https://v2.vuejs.org/v2/guide/components.html#Literal-vs-Dynamic
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')
}
})