I have used vee-validate for form validation in vue2 application. On click location button I need to scrolltop to the error message.
<a href="javascript:void(0)" class="btn btn-success" style="border-radius: 4px" #click.prevent="addNewLocation"> location
<span class="ti-plus"></span>
</a>
Here addNewLocation() method action is:
addNewLocation () {
this.$validator.validateAll().then(isValid => {
if (isValid) {
this.$store.commit('order/MUTATE_ADD_NEW_LOCATION', {stair_built_location: this.stair_built_location});
let locationArrayIndex = this.order.locations.length - 1;
this.$router.push({ name: 'StairHeader', params: { location_index: locationArrayIndex }});
} else {
window.scrollTo(0,0);
console.log('invalid');
}
});
},
In code window.scrollTo(0,0) does not work. How can I solve this problem?
You need to use scrollBehavior within your routing add function like this
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return { selector: to.hash }
}
}
It should detect if any #something was added in URL and scroll to it.
You can use it with for example router link then pass hash in to="
<router-link :to=" { hash: #IDofDiv } "> scroll to div </router-link>
you should also be able to just add hash from code. You can find more, complex info in vuejs router docs.
Remember to add div with this ID.
Related
I have a Vue 'app' of sorts. It's just part of a larger Django application - but I'm using this to springboard my Vue learning.
I'm trying to create unique forms that would be editable.
I have been messing about with this for a while trying to figure out how to 'disable all the forms except the one being edited'.
If a new 'evidence' is added, that form should be enabled and the others uneditable.
If an existing evidence is being edited then the 'add evidence' button should not be active and only the form being edited should be able to be edited.
My Vue looks like this - I have a base container (that's the Vue app) and a component (that is the forms):
var evidenceFormComponent = Vue.component("evidence-form", {
template: "#evidenceFormTemplate",
props: ["csrf_token", "evaluation_id", "element"],
components: {},
data: function () {
console.log("data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: null,
baseUrl: null
};
},
created: function () {
console.log("created_function!");
this.baseUrl = "/api/";
this.subdomainUrl = "/api/";
this.fetchAdditionalEvidence();
this.fetchSubdomainList();
this.isDisabled = true;
},
methods: {
fetchSubdomainList: function () {
// get the evidence if any using a Jquery ajax call
console.log("this should be fetching the subdomains");
return getSubdomains;
},
fetchAdditionalEvidence: function () {
// get the evidence if any using a Jquery ajax call
console.log("this is fetching additional evidence");
return getEvidence();
},
editForm: function (element) {
console.log("editing the form!");
this.isDisabled=false;
},
cancelEdit: function () {
console.log("cancel the edit!");
}
}
// watch:
});
const vm = new Vue({
el: "#evidenceFormsContainer",
data: function () {
console.log("parent data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: false,
baseUrl: null
};
},
methods: {
addForm: function () {
console.log("adding a child form!");
this.evidence.unshift({});
},
}
});
getEvidence and getSubdomains just return generic stuff atm as I would expect from an API.
I have read that it is best to have all UI elements present in case someone has JS disabled or something odd. So I figured I would create all 4 buttons then show/hide depending on if they should be disabled or not.
<button class="btn btn-primary text-white valign-button" v-on:click.prevent="element.isDisabled=false" #click="editForm()">
<i class="far fa-edit"></i> EDIT
</button>
<button :id="'saveButton'+element.id" v-if="element.isDisabled" v-on:click.prevent="element.removedRow=true" class="btn btn-primary text-white valign-button">
<i class="far fa-save"></i> SAVE
</button>
<button class="btn bg-danger text-white valign-button" data-toggle="modal" data-target="#deleteEvidenceModal" v-on:click.prevent>
<i class="fal fa-trash-alt"></i> DELETE
</button>
<button v-if="element.isDisabled" v-on:click.prevent="element.removedRow=true" class="btn bg-secondary text-white valign-button" #click="cancelEdit()">
<i class="fas fa-minus"></i> CANCEL
</button>
The problem I am running into is figuring how to tell if I'm editing one, or if it is a new one being added and properly disabling all the other elements.
For clarity I have made a JSFiddle of this in practice.
When you click 'add evidence' in the example, you will see. The form is 'disabled' still and the other forms still have the ability to click 'edit'.
I'm a bit lost. Would a child component for the buttons be better? Then if I'm editing a form or creating a new one, I can hide all the buttons on all the other instances?
All advice welcome!
Create a global reference to an activeForm element:
data: function () {
console.log("data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: null,
baseUrl: null,
activeForm: null // this is what we're adding
};
},
When you're in a loop, you know the index of the element you're working with. Pass that to your function:
#click="editForm(index)"
Assign that index to your activeForm variable:
editForm (index) {
this.activeForm = index
}
Change your v-if comparator assignment to observe whether the current index is the same as the activeForm:
v-if="activeForm && activeForm === index
In this way, a single variable is responsible for determining the edit state.
If you want to disable all the forms when adding, I'd just make another variable called adding and set it to true/false in the same way we did above with the other functions, and modify the v-if on the edit and delete buttons:
v-if="(activeForm && activeForm === index) && !adding"
I have this angular 6 application where I have implemented a login using Firebase's Google login. I have added a button that says Login when you are logged out, or if you are logged in, the button says Logout and it also shows the email address that you are currently logged in with.
Here is a code sample:
<p *ngIf="isLoggedIn()" class="text-white" style="margin-bottom: 0; padding-left: 10px">{{ afAuth.auth.currentUser.email }}</p>
<button *ngIf="!isLoggedIn()" class="btn btn-danger" type="button" (click)="login()">Log In</button>
<button *ngIf="isLoggedIn()" class="btn btn-danger" type="button" (click)="logout()">Log Out</button>
constructor(private afAuth: AngularFireAuth) {
}
isLoggedIn() {
return this.afAuth.auth.currentUser;
}
login() {
try {
this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
} catch (e) {
console.log(e.message);
}
}
logout () {
try {
this.afAuth.auth.signOut().then(() => {
this.hideAllViews();
});
} catch (e) {
console.log(e.message);
}
}
Everything seems to work fine except that if you are currently logged in and you refresh the page it doesn't seem to display the correct state, meaning it shows the Login button instead of showing your email address and a Logout button since you're already logged in. As soon as you click on a tab or any other button or link the page updates back to displaying the email and a Logout button.
To me it seems that the HTML page is loading before it checks the isLoggedIn() method.
I have found a workaround to this by adding the following inside the constructor:
setTimeout(function () {
},
Please let me know what is the "Angular way" / best way of fixing this issue.
You can check the callback for onAuthStateChanged() and find out when your var currentUser is initialized.
/* Don't forget ot import the firebase */
import * as firebase from 'firebase/app';
<p *ngIf="isLoggedIn()" class="text-white" style="margin-bottom: 0; padding-left: 10px">{{ afAuth.auth.currentUser.email }}</p>
<button *ngIf="!isLoggedIn()" class="btn btn-danger" type="button" (click)="login()">Log In</button>
<button *ngIf="isLoggedIn()" class="btn btn-danger" type="button" (click)="logout()">Log Out</button>
constructor(private afAuth: AngularFireAuth) {
}
/*Use callback to check when the variable is initialized*/
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// If there is a user (a user is loggedIn), do something in that case
} else {
// Otherwise ...
}
});
login() {
try {
this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
} catch (e) {
console.log(e.message);
}
}
logout () {
try {
this.afAuth.auth.signOut().then(() => {
this.hideAllViews();
});
} catch (e) {
console.log(e.message);
}
}
what you did is a good work-around, but what you should actaully use is life cycle hook(s) eg: ngAfterViewInit
ngAfterViewInit() {
this.isLoggedIn();
}
Currently i am implementing a like system into my laravel project with Vue js. The scenario is after users liked the post. It will showing the total like of the post beside the like button. If using controller and passing data into view is easy to be done. But when change to Vue js i have no idea how to do it. This is my Like.vue file.
<template>
<button class="btn btn-primary" v-if="!auth_user_likes_post" #click="like()">
Support
</button>
<button class="btn btn-primary" v-else #click="unlike()">
Unsupport
</button>
export default {
mounted(){
},
props: ['id'],
computed: {
likers() {
var likers = []
this.post.likes.forEach( (like) => {
likers.push(like.user.id)
})
return likers
},
auth_user_likes_post() {
var check_index = this.likers.indexOf(
this.$store.state.auth_user.id
)
if (check_index === -1)
return false
else
return true
},
post() {
return this.$store.state.posts.find( (post) => {
return post.id === this.id
})
}
},
methods: {
like() {
this.$http.get('/like/' + this.id)
.then( (resp) => {
this.$store.commit('update_post_likes', {
id: this.id,
like: resp.body
})
})
},
unlike() {
this.$http.get('/unlike/' + this.id)
.then( (response) => {
this.$store.commit('unlike_post', {
post_id: this.id,
like_id: response.body
})
})
}
}
}
The like and unlike functions has been done and working perfectly. Now i just need to show the total number.
This is Feed.vue file. The like was adding to this page.
<div class="post-description">
<p>{{ post.content }}</p>
<div class="stats">
<like :id="post.id"></like>
<a href="#" class="stat-item">
<i class="fa fa-retweet icon"></i>12
</a>
<a href="#" class="stat-item">
<i class="fa fa-comments-o icon"></i>3
</a>
</div>
</div>
If my function cannot be achieved the expected scenario then can you recommend other ways which can do that?
I have the returned array with likers. But at beginning i never though using this could be achieved my goal. So this {{ likers.length }} actually can show the total number.
I have problems with creating routes with user's usernames. So idea is something like this: Click on path and go to that users profile. His link should be something like : http://www.something.com/usersUsername
I was reading and trying everything I found on internet about this but lot of stuff changed so I couldn't manage this.
Only thing I found usefull is that when page loads client ,,watch" paths first and then subscribes to a collection so I got ,,null" for path. Any help? My idea is to create something to waitOn for subscribe...
Packages: iron:router , accounts-ui , accounts-password
Here is code:
Start page, template:
<template name="početna">
<h1>Dobrodošli!</h1>
<h3>Registrujte se:</h3>
{{> register}}
<h3>Prijavite se:</h3>
{{> login}}
{{#if currentUser}}
<h2>Logovan si!</h2>
{{> logout}}
Profil
{{/if}}
Router JS file:
Router.configure({
layoutTemplate: 'okvir'
});
// * * * * * * //
Router.route('/', {
name: 'početna',
template: 'početna',
});
Router.route('/:username', {
waitOn: function(){
return Meteor.subscribe('userData'), Meteor.user().username
},
name: 'profil',
template: 'profil',
});
Simple HTML template file only to se if it works:
<template name="profil">
<h1>RADI</h1>
</template>
Thanks!
Here you go:
Router.route('/:username',{
name: 'profil',
waitOn: function () {
return Meteor.subscribe('userData', this.params.username)
},
action: function () {
this.render('profil', {
data: function () {
return Meteor.users.findOne({username: this.params.username});
}
});
}
});
EDIT:
With this.params.username will let anybody visit that profile, user or not. If you want to prevent that, you can use onBeforeAction()
onBeforeAction: function() {
if(Meteor.user() && this.params.username == Meteor.user().username){
this.next();
} else {
Router.go('/not-authorized') //or any other route
}
},
Luna, thanks for help! Luna's answer helped but I also needed:
1.) Helper to set value of username=username
Template["početna"].helpers({ username: function() { return Meteor.user().username } })
2.) Publish
Meteor.publish("userData", (username) => {
return Meteor.users.find({
username: username
})
});
I am relatively new to Vue, so forgive me if this is obvious (or obviously impossible).
I have a set of JSON data (fetched from a RESTful API via vue-resource):
{content: "This is content. <a href='/blog'> Link to blog </a>"}
Right now, the link triggers a page reload. If it were a vue-router v-link, that would not be an issue. However, this doesn't work (quotes are escaped in the data, of course):
{content: "This is content. <a v-link="{ path: '/blog' }"> Link to blog </a>"}
At this point, the template is already parsed, and Vue won't create a v-link anymore (it will just show up as a v-link in the rendered html).
My final result would ideally mean that I could include links in my CMS, either in HTML or Vue format, and have Vue route them correctly as v-links.
Is there something I can do to make Vue interpret the link in the JSON data?
I've answered the question on Vue Chat, and writing it here in case any other people facing similar problem
Simplified example on Codepen
HTML
<div id="app">
<div>
<a v-link= "{path:'/home'}">Go to home</a>
</div>
<router-view></router-view>
</div>
<template id="home">
<div>
<div>
Fetched Content:
</div>
<div>
{{{ fetchedContent }}}
</div>
</div>
</template>
<template id="route1">
<div>
Route1 view
</div>
</template>
<template id="route2">
<div>
Route2 view, this is different from Route1
</div>
</template>
javascript
function getContent (callback) {
var content = 'Click this: Go to route1 and Go to route2'
setTimeout(function () { callback(content) }, 1000)
}
var Home = Vue.component('home',{
template:'#home',
data: function () {
return {
fetchedContent: 'Loading...'
};
},
ready: function () {
var self = this
var router = this.$router
getContent( function (result) {
self.fetchedContent = result;
Vue.nextTick(function () {
var hyperLinks = self.$el.getElementsByTagName('a')
Array.prototype.forEach.call(hyperLinks, function (a) {
a.onclick = function (e) {
e.preventDefault()
router.go({ path: a.getAttribute("href") })
}
})
})
})
}
});
var Route1 = Vue.component('route1', {
template: '#route1'
});
var Route2 = Vue.component('route2', {
template: "#route2"
});
var router = new VueRouter({
hashbang:false,
history:true
});
router.map({
'/home':{
component:Home
},
'/route1':{
component:Route1
},
'/route2':{
component:Route2
}
});
router.start({
}, '#app');
I had a similar solution here: question using a custom dataset in my JSON code and a click listener to process it:
mounted() {
window.addEventListener('click', event => {
let target = event.target
if (target && target.href && target.dataset.url) {
event.preventDefault();
console.log(target.dataset.url);
const url = JSON.parse(target.dataset.url);
console.log(url.name)
this.$router.push(url.name);
}
});
},