In Vue.js, how do I scroll to an imported component? - javascript

I know about the scrollBehavior function that Vue has, but I am unsure of how to use it with my code.
I have an Index page, where sections are made of imported vue components.
For example
<template>
<div class="Container">
<About />
<Services />
<Contact />
</div>
</template>
My Navbar has links to all these components.
<template>
<nav>
<img class="nav__logo" :src="navLogo" height="40px" width="auto">
<ul class="nav__row" ref="nav">
<div class="nav__row__dropdown-toggle-btn" #click="toggleNav">
<img :src="navMenuIcon" height="40px" width="auto">
</div>
<li class="nav__list-item" v-for="(link, index) in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false"
:class=" { active: hover } ">
<router-link :to="link.path">
{{ link.text }}
</router-link>
</li>
</ul>
</nav>
</template>
which it gets from my App.vue script
<script>
import Navbar from '#/components/Navbar'
import Footer from '#/components/Footer'
export default {
components: {
Navbar,
Footer
},
data: () => ({
navLinks: [
{
text: 'Home',
path: '/'
},
{
text: 'About',
path: '/about'
},
{
text: 'Contact',
path: '/contact'
},
{
text: 'Services',
path: '/services'
}
]
})
}
</script>
but if I click on "About" it will just take me to a seperate page for the "About" component.
When I click on "About" I want the page to scroll down to the imported About component that is nested on my Index page.
How can I accomplish this? Is it something I need to change in the path?

The idea of router that you trying to implement is actually treating About components as a page, and when clicking at it, it will go to that page (and change URL)
So, in order to implement what you want, I actually suggest using pure javascript
First, give the About component and id ( or ref if you prefer Vue ref)
<template>
<div class="Container">
<About id="about"/>
<Services />
<Contact />
</div>
</template>
Then bind a method to click event of nav
<li class="nav__list-item" v-for="(link, index) in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false"
:class=" { active: hover } ">
#click=nav(link)
</li>
data: () => ({
navLinks: [
{
text: 'About',
id: 'about'
}
]
}),
methods:{
nav(link) {
const position = document.getElementById(link.id).offsetTop;
// smooth scroll
window.scrollTo({ top: position, behavior: "smooth" });
}
}
If you prefer Vue ref, can change all id to ref. And use this.$refs[link.id].$el.offsetTop instead;

i found this video so useful,
my goal was able to scroll between component at the same page.
but still you can apply anywhere, i think
https://laracasts.com/series/practical-vue-components/episodes/1

Related

Styles and fonts are loaded after the page has been mounted

Every time I use Nuxt.js in my projects I encounter the same mistake with styles. Some of them are enabled after the page is mounted. I tried to import styles from a file from a static folder, tried to use styles in the file (tag style), with the scoped attribute, without it, but each time some styles are included after the page has already been mounted. There are no problems in pure Vue js.
Look here, this is the state I was talking about. It lasts about half a second
And after that the page becomes normal
Sometimes this state persists until the user scrolls the page, sometimes it does not disappear at all
To clarify, I prefer the scoped attribute in my projects, without importing files with styles.
Edit
Ok. I don't know will it helps, but there is the code snippets
This is a test page
<template>
<div>
<app-promo
:data="{
titles: ['адреса пиццерий', 'roni napoletana'],
bgImg: 'page-addresses/promo/promo_bg.png'
}"
>
<app-search></app-search>
</app-promo>
</div>
</template>
This is a search component
<template>
<div v-click-outside="closeResults" class="search">
<div class="search__bar">
<input placeholder="Начните вводить название улицы, чтобы найти ближайшую пиццерию" v-model="searchRequest" #keypress.enter="search" type="text" class="search__input">
<button #click="search" class="search__submit"><img src="#/static/img/page-addresses/promo/zoom.svg" alt="Search"></button>
</div>
<div :class="['search__results', {'search__results_active': showResults}]">
<nuxt-link
:to="item.link"
class="search__result"
v-for="item in searchedItems"
:key="item.title"
>
{{ item.title }}
</nuxt-link>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchedItems: [
{
title: 'Истикбол улица',
link: '/'
},
{
title: 'Шевченко улица',
link: '/'
},
{
title: 'Тараккиёт улица',
link: '/'
}
],
searchRequest: '',
showResults: false
}
},
methods: {
search() {
this.showResults = true
},
closeResults() {
this.showResults = false
}
},
}
</script>
This is a promo component
<template>
<section class="promo">
<div class="promo__bg"><img src="#/static/img/page-main/promo/promo_bg.png" alt=""></div>
<div class="promo__inner">
<h1 class="promo__title">
<template v-for="title in data.titles">
{{ title }}
</template>
</h1>
<h2 class="promo__subtitle" v-if="data.subtitle">{{ data.subtitle }}</h2>
<slot></slot>
</div>
<div class="promo__label" v-if="data.labelImg">
<img src="#/static/img/page-main/promo/label_img.svg" alt="Img">
</div>
</section>
</template>
<script>
export default {
props: {
data: {
default: {
titles: '',
subtitle: '',
labelImg: '',
bgImg: ''
}
}
}
}
</script>
My nuxtconfig is standard. The styles are too.

how to render the correct component in routes NextJs

I have a problem when I change routes. Between pages it renders me fine. But when I need a dynamic route it does not render me.
In routes I have the folder uw and inside [table].js
When I put the manual routes uw/first and I want to go to uw/second, the first one does render well and then the second one sticks to the style of the first one and it does not bring me the correct data for that view
const menuItems = [
{
href: '/dashboard',
title: 'Inicio',
icon: 'fluent:home-16-filled',
},
{
href: '/uw/primera' ,
title: 'Cartera',
icon: 'bxs:user',
},
{
href: '/uw/segunda',
title: 'Cartera',
icon: 'bxs:user',
},
];
and then return the routes
<div>
<aside className="main-aside">
<div className="main-aside-image">
<Image
src="/logo-castor.png"
alt=""
width={198}
height={32}/>
</div>
<nav>
<ul className="main-aside-items">
<div className="main-aside-categories">
{menuItems.map(({ href, title, icon }) => (
<li className="main-aside-item-categories" key={title}>
<Link href={href}>
<a
className={`flex p-2 bg-fuchsia-200 rounded hover:bg-fuchsia-400 cursor-pointer ${
router.asPath === href && 'bg-fuchsia-600 text-white'
}`}
>
{title}
</a>
</Link>
</li>
))}
</div>
</ul>
</nav>
</aside>
</div>
You're using the title as the key inside the map, since the title is the same between uw/primera and uw/segunda it won't update the component.
Changing the key to the href could solve that.
<li className="main-aside-item-categories" key={title}>
to
<li className="main-aside-item-categories" key={href}>

communication between components in vuejs. (search field)

well i'm new to vue js and developing an application with a search function.
This is my component where the search results will be rendered.
<script>
import RecipeItem from "../recipe/RecipeItem";
import { baseApiUrl } from "#/global";
import axios from "axios";
import PageTitle from "../template/PageTitle";
export default {
name: "Search",
components: { PageTitle, RecipeItem },
data() {
return {
recipes: [],
recipe: {},
search: '',
}
},
methods: {
getRecipes() {
const url = `${baseApiUrl}/search?search=${this.search}`;
axios(url).then((res) => {
this.recipes = res.data;
});
}
},
watch: {
search() {
const route = {
name: 'searchRecipes'
}
if(this.search !== '') {
route.query = {
search: this.search
}
}
},
'$route.query.search': {
immediate: true,
handler(value) {
this.search = value
}
}
},
};
</script>
<template>
<div class="recipes-by-category">
<form class="search">
<router-link :to="{ path: '/search', query: { search: search }}">
<input v-model="search" #keyup.enter="getRecipes()" placeholder="Search recipe" />
<button type="submit">
<font-icon class="icon" :icon="['fas', 'search']"></font-icon>
</button>
</router-link>
</form>
<div class="result-search">
<ul>
<li v-for="(recipe, i) in recipes" :key="i">
<RecipeItem :recipe="recipe" />
</li>
</ul>
</div>
</div>
</template>
ok so far it does what it should do, searches and prints the result on the screen.
But as you can see I created the search field inside it and I want to take it out of the search result component and insert it in my header component which makes more sense for it to be there.
but I'm not able to render the result in my search result component with my search field in the header component.
but I'm not able to render the result in my search result component with my search field in the header component.
Header component
<template>
<header class="header">
<form class="search">
<input v-model="search" #keyup.enter="getRecipes()" placeholder="Search recipe" />
<router-link :to="{ path: '/search', query: { search: this.search }}">
<button type="submit">
<font-icon class="icon" :icon="['fas', 'search']"></font-icon>
</button>
</router-link>
</form>
<div class="menu">
<ul class="menu-links">
<div class="item-home">
<li><router-link to="/">Home</router-link></li>
</div>
<div class="item-recipe">
<li>
<router-link to="/"
>Recipes
<font-icon class="icon" :icon="['fa', 'chevron-down']"></font-icon>
</router-link>
<Dropdown class="mega-menu" title="Recipes" />
</li>
</div>
<div class="item-login">
<li>
<router-link to="/auth" v-if="hideUserDropdown">Login</router-link>
<Userdropdown class="user" v-if="!hideUserDropdown" />
</li>
</div>
</ul>
</div>
</header>
</template>
Result component
<template>
<div class="recipes-by-category">
<div class="result-search">
<ul>
<li v-for="(recipe, i) in recipes" :key="i">
<RecipeItem :recipe="recipe" />
</li>
</ul>
</div>
</div>
</template>
Keep the state variables in whatever parent component is common to both the header component and the results component. For example, if you have a Layout component something like this:
<!-- this is the layout component -->
<template>
<HeaderWithSearch v-on:newResults="someFuncToUpdateState" />
<ResultsComponent v-bind:results="resultsState" />
</template>
<!-- state and function to update state are in a script here... -->
When the search bar returns results you need to pass that data "up" to the parent component with an $emit call, then the parent component can then pass that state back down to the results component using normal props.
Check out this documentation: https://v2.vuejs.org/v2/guide/components-custom-events.html
Be sure to pay special attention to the .sync part of the documentation and determine if that's something you need to implement as well.
Unless you want to use a more complicated state management library like vuex (which shouldn't be necessary in this case) you can just keep state in a common parent and use $emit to pass up and props to pass down.

vue js nested routing setup for navigation bar

I'm trying to make my project to have 2 kind of nagigation bar, I have read some documentation and searching for tutorial on youtube, i did not found anything related to my problem, i want to have 2 kind of nav-bar for each route without refreshing it on every page. I put my nav-bar on app file in the current project, but what i want to do is making another nested routed that displaying another nav-bar and have it own routes, can anyone help me with this problem? simple code example would be greatly appreciated.
You could use the $route.meta for controlling which navbar to display. This is an easy solution, but you always have to take care of the nav (or set a default, like in the snippet below):
const Foo = {
template: `
<div>This is Foo</div>
`
}
const Bar = {
template: `
<div>This is Bar</div>
`
}
const routes = [{
path: "/",
redirect: "/foo",
},
{
path: "/foo",
component: Foo,
meta: {
nav: "nav1",
},
},
{
path: "/bar",
component: Bar,
meta: {
nav: "nav2",
},
},
]
const router = new VueRouter({
routes,
})
new Vue({
el: "#app",
components: {
Foo,
Bar,
},
router,
computed: {
computedRoute() {
return this.$route.meta.nav
},
},
})
.link {
padding: 0 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<div v-if="computedRoute === 'nav1'">
<router-link to="/foo" class="link">
FOO 1
</router-link>
<router-link to="/bar" class="link">
BAR 1
</router-link>
</div>
<div v-else>
<router-link to="/foo" class="link">
FOO 2
</router-link>
<router-link to="/bar" class="link">
BAR 2
</router-link>
</div>
<br>
<hr>
<router-view />
</div>

Get arguments from another component [React JS]

I'm creating a web-app using React. I'm facing a problem with my navbar and the main content:
In AppWrapper I have this:
var contents = [
<Offers />,
<Create />,
<List />
];
and where each item will be the content displaying.
In getInitialState I initialize it like this:
getInitialState: function() {
return {
currentTab: 0
};
},
Later, in render function I have this:
return (
<div>
<Navbar />
<div className='mainPanel'>
{contents[this.state.currentTab]}
</div>
</div>
);
You can see that I call the navbar component before the content.
In Navbar component I have a menu where, from there, I want to change the currentTab of AppWrapper.
How can I do that? Thank you for advance!
After trying to handle it, I found how to do that.
This is the answer:
<Navbar selectTab={this.selectTab} />
selectTab: function(tab) {
this.setState({ currentTab: tab });
}
I pass to Navbar component the function "selectTab of the parent (AppWrapper).
Then in Navbar component:
<li>
<a onClick={this.selectTab.bind(this, 0)}><i className="fa fa-dashboard fa-fw" /> Dashboard</a>
</li>
<li>
<a onClick={this.selectTab.bind(this, 1)}><i className="fa fa-edit fa-fw" /> Create</a>
</li>
Then in selectTab of Navbar I change the props to current tab.
selectTab: function(tab) {
this.props.selectTab(tab);
},
Hope someone helps this answer!

Categories