Vuejs reusing same HTML elements - javascript

I am using this lazyload directive here:
https://itnext.io/lazy-loading-images-with-vue-js-directives-and-intersectionobserver-d0eb390cad9
<li v-for="entry in entries">
<router-link :to="'/d/' + entry.title">
<img :title="entry.title" :data-url="entry.imageUrl" v-lazyload />
</router-link>
</li>
Problem is that whenever the route changes to another url, the first set of images that are displayed on the screen are the same images that were on the previous page!
It seems that vuejs is reusing HTML elements from another page to the next, but the lazyload directive is not aware of this.
How can I prevent this to happen and force Vue to re-render elements ?

The problem is that the component is being reused as Vue thinks that only the data is being changed. To solve this issue you should tell Vue to re-render the component when the path changes. To do that you can give a unique key to <router-view />
something like this:
<router-view :key="$route.fullPath"/>
This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions

Related

How do I target an html element in Angular from another sibling component (skip link)?

I need to target a nested layout component's element from another sibling component. The desired target element is several layers deep within said component. I Have a Header Component that is housing a Skip Link <a></a>. Upon selecting the Skip Link I need to send focus to the aforementioned sibling component's #main-content element, which happens to be a <div></div>. How can I achieve this?
I have tried creating a custom attribute directive. I have tried targeting the element with #ViewChild("main-content"), I can't seem to get the target element to achieve focus, and thus show the border with style changes. Any Advice is greatly appreciated.
Header Component:
...
<a [href]="skipLinkPath" class="skip-link" (click)="skipLink()" tabIndex="0">Skip to Main Content</a>
...
Header Component's TS:
...
#ViewChild('#mainContent') mainContent: ElementRef;
...
skipLink() {
console.log("click event called");
console.log(this.mainContent);
this.mainContent.nativeElement.focus();
}
...
Main Component:
<div class="content-display skip-link-focus" role="main" id="main-content" #mainContent>
<router-outlet></router-outlet>
</div>
You can use a shared service to achive this
SharedService.ts
->
isFocus: Subject<boolean> = new Subject<boolean>();
Create a subject in shared service
Header Component
skipLink() {
this.sharedService.isFocus.next(true);
}
Main Component
create a field
isFocus = false;
in on init method subscribe to service
this.sharedService.isFocus.subscribe(value => {
this.isFocus = value;
});
<div class="content-display skip-link-focus" role="main" [ngClass]={_focused: isFocus} id="main-content" #mainContent>
<router-outlet></router-outlet>
</div>
in css
._focused {
//required css
}
Not entirely sure why the accepted answer is JavaScript based. This can all be achieved with no JS at all in a much simpler fashion and more robust way.
<a [href]="skipLinkPath" class="skip-link" (click)="skipLink()" tabIndex="0">Skip to Main Content</a>
Firstly the skip link shouldn't need a tabindex, if you needed to add that to make the link accessible have a quick look through your code for an issue as links are automatically added to the focus order.
The above could become:
<a [href]="#main-content" class="skip-link">Skip to Main Content</a>
Notice how I changed the href to match the id of your main content div.
<div class="content-display" role="main" id="main-content" #mainContent>
<router-outlet></router-outlet>
</div>
The above will work without needing to handle the focus state yourself (if you want the main div to show that it is focused add tabindex="0" to it, but the above example will still skip the menu with the next tab stop being the first focusable item in your HTML).
What about the URL change using an anchor?
Obviously the above adds a #main-content to your URL, I don't consider that an issue, but if you do, you could intercept the click and use the history API in the browser to stop the URL updating.
Although that may then seem like more work, you then have a functional skip link that works without JavaScript so that whatever no JavaScript fallback your site has is still accessible.
final suggestion
<div class="content-display" role="main" id="main-content" #mainContent>
The above div looks like it could be changed to a <main> element to be more semantically correct as I am assuming you are using HTML5.

Vue component parent from child recursive include

So I want to have many components nested to each other and included dynamically.
Lets assume simple case:
-container
-row
-container
-row
-widget
etc.
So how can I include container that will load row which will load previous component container in an elegant way (recursive I guess)
I want this functionality for more components than just container and row
I had the same problem myself right now:
it's usually webpack which cause this problem so you have two options:
Register your component Globally
On you child component, register the parent like this:
components: {
ComponentLoader: () => import('./ComponentLoader.vue')
}
you can read more here:
https://v2.vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
So to achieve my goal I have to build ComponentLoader and child components loaded from it.
ComponentLoader.vue
<template
v-for="(block, index) in data.children">
<component
v-if="component"
:is="component"
:key="`container-${block.id}-${index}`"
:data="block"/>
</template>
</template>
Which for instance will load Article component from its children:
ArticleComponent.vue
<template>
<SimpleArticle
:data="data"/>
<ComponentLoader
v-if="data.children"
:data="data"/>
</template>
So ArticleComponent will call ComponentLoader again if it has more children to load. This way works for me and is recursively going through the data tree.

How to render/create a Vue component after an specific event (e.g. #click #change)

I'am new to Vue JS and face now a problem, which seems should be easy but I can't find a way to do it.
This is what I want to do and the reason:
I want to first load some data from the server/local, edit the data, and show the data to the user. But which data to load is decided by the user (through buttons, e. g. click event)
Because the way to show the data is a little bit too complicated (I mean the HTML codes, the UI), so I decided to create a global component to handle it, so my Vue instance won't be messed up.
I have alreday did a part of the component, but the problem is, every time I refresh my page, it comes the warning, said the some parameters are undefined.
Of course they are not defined at the beginning, because the user haven't choose which data to load.
Can someone with experience of that tell me. How I can tell the Vue to render a component after something like a click event?
I will try to make a simplified code example below, so you can understand that better:
- part of the main.js file (simplified)
// the global Vue component
var infoBox = Vue.component('info-box', {
props:['infos'],
template:`
<div>
<li>{{ infos.name }}</li>
<li>{{ infos.id }}</li>
<li>{{ infos.title }}</li>
<li>{{ infos.whatever }}</li> // continues..also simplified, actually I need to edit the infos first
</div>
`
}
// the Vue instance
var app = new Vue ({
el: '#app',
data: {
data: '',
},
methods: {
loadData: function() {
// load the needed data from the server
this.data = $.getJSON(/*....*/) // just for example that I loaded some data and push it to the instance
}
}
})
- Part of the HTML file
<div id="app">
<button #click="loadData"></button> // also simplified, the real project has different kinds of buttons, the user can decide which data should be loaded
<info-box :infos="data"></info-box> // get the data from the Vue instance, which does not exist until the button was clicked the the data was loaded
</div>
More details about my problem:
The workflow is actually OK, because I have alredy defined a empty "data" in the Vue instance. The real Problem is inside the component, when I want to edit and handle some data, because before the click, the data is empty, so I got a lot of the "XXX is not defined" warning because some functions I use don't allow undefined props.
The perfect way I want: Is there any way that I can say to Vue: OK, now the button is clicked, let's see, the data is loaded, now pls render/create the component.
If not, than the only way I can do is to load all the data first when I create the Vue instance. But obviously it's too much data and it will cause problems.
I have tried to understand the Vue official document "Async component", which seems to be the best solution for me:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
But I can't go any further because I can't understand this at all and can't use it in my project (and I think this code is the key to tell the Vue, that he can render/create the component now):
require(['./my-async-component'], resolve)
Also, the setTimeOut is not what I wanted, because if the user don't click the button, we can wait until the world ends, the data is always empty.
There are dozens of ways you could fix this.
You can simply set a v-if="data" on your component which only loads the entire component when your data is loaded. Normally I would put another element below this with v-else and have a loading spinner component or something alike.
If you want to have the layout of component you can do the same within the template of your info-box component.
<div>
<div class="wrapper" v-if="infos">
<li>{{ infos.name }}</li>
<li>{{ infos.id }}</li>
<li>{{ infos.title }}</li>
<li>{{ infos.whatever }}</li>
</div>
<div class="spinner-loading">
Put your loading animation here
</div>
</div>
An async component will not help you because the async component might actually load before your AJAX request has actually finished. You misunderstand how async components work.
You can also created computed properties for each of your info properties and put in placeholders if they are not set so you can manage them individually. Plenty of options here but you need to make sure you have data or at least a placeholder before showing your template.

react-router link to element by id within another component

I'm using React Router and I'm trying to link to a sub-component in another route by id. Basically what would usually be done using the <a href="www.url.com/profile/#profile-header-id">.
I'm not sure if there's a built in way for react router to do this, but if not perhaps I can manually trigger the link at a later point when I know the element has been rendered.
The issue isn't linking to another route which of course is done with the Link from react router. The issue is linking to an element which is found in the rendered HTML of the linked component.
Less Abstract Code Example:
So let's say my router is
<Route path"/A" component={A}>
<Route path"/B" component={B}>
component A has the following render:
render(){
<div>
// A looooot of text and other HTML elements
<div id="relevant-to-B">
// relevant stuff for component B
</div>
</div>
}
Now in component B, I want a Link that not only takes me to the Route "/A", but also scrolls to the element of id #relevant-to-B, thereby skipping all the irrelevant stuff.

React component ignores dynamic element while re-rendering

I have developed a react component with three div elements like below.
render: function(){
return (
<div id="div_1">
<div id="div_2"></div>
<div>
Click the below button
Click here
</div>
</div>
);
})
In runtime, using jquery am inserting few elements into "div_2" div like below.
componentDidMount: function(){
//Invoking global function, which is outside react
window.loadView();
}
And my load view method looks somthing like below,
function loadView(){
$('#div_2').html('//my elements')
}
Now to my surprise, when I change the status of my react component, the view is getting re-rendered but somehow the contents within "div_2" remains undisturbed. Can someone say why this behaviour?
React has its own virtual copy of the DOM, hidden somewhere. React uses this to do its magic in only updating DOM when something changed from state A to state B.
In your example, React is unaware of the changes you made with jQuery to <div 2>. So, as far a React knows, <div 2> is unchanged, so React does not update it.
I would strongly advise against mixing React and jQuery for updates to components. If you want to keep your code manageable, give React the exclusive monopoly to update the DOM.
In your case, I would advise to let React only manage the inner part, like so:
render: function(){
return (
<div>
Click the below button
Click here
</div>
);
})
And in your HTML:
<div id="div_1">
<div id="div_2"></div>
<div id="react-only domain"></div> // mount your ReactDOM here
</div>
You should use componentDidUpdate in your case instead of componentDidMount.

Categories

Resources