Angular doesnt set dynamic HTML embed src attribute at second time - html

I am trying to set PDF file paths dynamically to embed tag using this.renderer.setAttribute(this.pdf.nativeElement, "src", ...
At first I can set embed src PDF path and it displays on screen but second time I set for another path it doent work as expected.
Can anyone help?
Live demo link is:
https://stackblitz.com/edit/angular-kghaku

You will need to first remove the src attribute and then apply the new one. Also, need to wrap the setAttribute in a setTimeout, since it needs to execute after removeAttribute
StackBlitz Demo
setpdf1() {
this.renderer.removeAttribute(this.pdf.nativeElement, "src");
setTimeout(() => {
this.renderer.setAttribute(this.pdf.nativeElement, "src", "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")
})
}
setpdf2() {
this.renderer.removeAttribute(this.pdf.nativeElement, "src");
setTimeout(() => {
this.renderer.setAttribute(this.pdf.nativeElement, "src", "https://file-examples.com/wp-content/uploads/2017/10/file-sample_150kB.pdf")
})
}

Try binding:
<embed #pdf src="{{ selectedSRC }}">
In component:
selectedSRC: string;
setpdf1(){
this.selectedSRC = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
}
setpdf2(){
this.selectedSRC = "https://file-examples.com/wp-content/uploads/2017/10/file-sample_150kB.pdf";
}

Related

How can I set the "target" attribute of <a> tags which are already "embedded" into HTML?

I am developing a website using VueJS, and Kentico Kontent as a CMS. This CMS offers the "rich text" feature, basically allowing text content to embed links and basic formatting, which gets automatically converted into HTML when served through the API.
I have no problem displaying the HTML content using the v-html directive, but I cannot think of a way to set the attributes of the inner <a> tags to _blank, so that the embedded links open new windows when clicked.
Is there any elegant way to do this without having to parse the HTML from the Front-end?
You could create a directive:
Vue.directive('links-in-new-window', {
inserted: function(el) {
const anchors = el.querySelectorAll('a')
anchors.forEach((anchor) => anchor.target = "_blank")
}
})
And just apply that to the same element you're using the v-html on:
<div class="content" v-html="content" v-links-in-new-window></div>
In vue V3 the directive would look like this:
app.directive('links-in-new-window', {
mounted: function(el) {
const anchors = el.querySelectorAll('a')
anchors.forEach((anchor) => anchor.target = "_blank")
}
})
HTML is the same, remember to use v- => v-links-in-new-window
<div class="content" v-html="content" v-links-in-new-window></div>

How to gently change img src in Angular2+

When I dynamically change img's src attribute, old image is displayed while loading a new one.
I have a component which displays some data: text and image. On click the underlying data is changed (i.e. new data from server). Once click, text is changed immediately, but component displays old image while new one is loaded. When new image is loaded, then it is visually displayed which can take noticeable amount of time.
In real application one can have product details and changing products on button click. All data is replaced immediately but not image.
Problem exists when the component is not destroyed (reused).
I've already tried clear image src after click, but it not worked.
I have simple binding in template
img [src]="img.url" style="width: 300px; height: 300px">
<p>{{ img.text }}</p>
and image change on click
this.img = this.images[1];
You can see sample app here https://stackblitz.com/edit/angular-cojqnf
Is this possible to take more control of this image change process? It would be great to clear image on click and wait for new one with empty background for example.
I hacked around a little with your stackblitz demo, I basically wrapped your code in an ImageGhostDirective to make it reusable. The directive listens to any changes on the src attribute using a MutationObserver to change the style. Using a HostListener on the 'load' event, it reverts the styles back to normal. I start with an opacity of 0 for the first load, followed by an opacity of 0.2 between successive image changes, but this is completely arbitrary and could be replaced by a spinner or any kind of placeholder...
Here is the link to the stackblitz: https://stackblitz.com/edit/angular-image-ghost-directive
<img [src]="'https://loremflickr.com/300/300?random=' + index"
style="width: 300px; height: 300px" imgGhost>
#Directive({
selector: 'img[imgGhost]'
})
export class ImageGhostDirective implements OnDestroy {
private changes: MutationObserver;
constructor(private elementRef: ElementRef) {
this.changes = new MutationObserver((mutations: MutationRecord[]) =>
mutations.filter(m => m.attributeName === 'src').forEach(() => this.opacity = 0.2)
);
this.changes.observe(this.elementRef.nativeElement, {
attributes: true,
childList: false,
characterData: false
});
}
ngOnDestroy(): void {
this.changes.disconnect();
}
#HostBinding('style.display') display = 'block';
#HostBinding('style.opacity') opacity = 0;
#HostListener('load')
onLoad(): void {
this.opacity = 1;
}
}
It is also possible to tell Angular to automatically attach this directive to every img element by using the img:not([imgGhost]) selector in the directive decorator. That way, you don't have to manually place the directive on every image in your app.
Hope this is useful.
Finally I achieved what I want by leveraging (load) event on img and [ngStyle].
In template I added load handler and style:
<img [src]="img.url" style="width: 300px; height: 300px" (load)="loaded()"
[ngStyle]="{'display': imgVisible ? 'block' : 'none'}">
In back-end:
imgVisible = true;
and when changing data, also hide image:
this.imgVisible = false;
next, when image is loaded, show the image (be careful! when old and new images have the same URL, this event is not raised; if it is the case you need to conditionally hide image)
loaded(): void {
this.imgVisible = true;
}
Complete code for solution: https://stackblitz.com/edit/angular-ewptj7
I'm not a big fan of this kind solutions. It could be difficult to apply when you have more images.
All better solution are welcome.

Vue.js anchor to div within the same component

I'm developing a Vue.js application and I'm having trouble to link an anchor to a certain div within a component.
I have the following anchor:
Porto, Portugal
and the following div:
<div id="porto" class="fl-porto">
I'm using vue-router in hash mode.
The problem is, whenever I click the "porto-button" it will redirect me to the "home" page ( ' / ' )
I'm using Vue.js 1.X and I tried using history mode (URL without the hashbang) but it gives me a cannot GET '/page' error upon refreshing a page.
Am I doing something wrong? What can I do about this?
Because you are using router in hash mode, you will not be able to scroll that easily because scrolling to /#something will actually redirect you to 'something' page.
You will have to emulate scrolling behaviour on your own, try doing something like that:
//P.S. the code is written for Vue 2.
//You will have to adjust it to Vue 1.
//Your view:
<a class="porto-button" #click="scrollMeTo('porto')">Porto, Portugal</a>
...
<div ref="porto" class="fl-porto">
//Your code:
methods: {
scrollMeTo(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
How it works:
Set the references through ref attribute to the element you would like to scroll to;
Write a function that will programmatically set window.scrollY to the top of the referenced element.
Job is done :)
Update 1:
jsfiddle https://jsfiddle.net/5k4ptmqg/4/
Update 2:
Seems that in Vue 1 ref="name" looked like el:name (docs), here is an updated example:
https://jsfiddle.net/5y3pkoyz/2/
Another method is to use "scrollIntoView()"
So, euvl's code still stands, except you would change the method slightly:
new Vue({
el: '#app',
methods: {
goto(refName) {
var element = this.$els[refName];
element.scrollIntoView();
}
}
})
If you wanted to get fancy and make the scroll smooth, you can even add the following:
element.scrollIntoView({ behavior: 'smooth' });
Note that this will need a polyfill for older browsers.
What worked for me
<router-link to="#leaders">Leaders</router-link>
or dynamic
<router-link :to="`#${subMenuItem.linkTarget}`" class="page-submenu-list__link">
{{subMenuItem.linkTitle}}
</router-link>
in router
routes:[],
scrollBehavior (to, from, savedPosition) {
//https://router.vuejs.org/guide/advanced/scroll-behavior.html
if (to.hash) {
return { selector: to.hash }
} else if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 }
}
}
An alternative solution is to use the v-scroll-to directive (webpage, github).
I find this solution to be clean, simple, flexible and effective. To use:
Install it:
npm install --save vue-scrollto
Have Vue 'use' it:
var VueScrollTo = require('vue-scrollto');
Vue.use(VueScrollTo)
Apply it as a directive in your Vue component's template:
Scroll to #element
<div id="element">
Hi. I'm #element.
</div>
Or apply it programmatically in your Vue component's methods:
this.$scrollTo('#element', 500, { easing: 'ease-in-out' })
Or apply it programmatically in your Vuex actions:
import { scrollTo } from 'vue-scrollto'
scrollTo('#element', 500, { easing: 'ease-in-out' })
Another solution, if you're already using Vuetify, you may prefer to use Vuetify's built-in programmatic scrolling method, $vuetify.goTo():
<v-btn #click="$vuetify.goTo('#element', {duration: 500, easing: 'easeInOutCubic'})">
Scroll to #element
</v-btn>
<div id="element">
Hi. I'm #element.
</div>
If you set a ref="something" on an element, you could also use this oneliner with #click:
<a #click="$refs.something.$el.scrollIntoView()">
Go to something
</a>

How to create Anchor Tag in react-router?

I am using react-boilerplate (3.4.0) with react-router internally for the routing.
I have tried to create a Link with : < a href="#anchor-tag" >< /a >
When I click on it I expect to scroll to the div with id=anchor-tag
It just scroll to the top of the page, even if I use a Link component instead of a < A > tag. Does we have to care about use < A > or < Link > ?
How should we create anchor tag in react-router ?
This might be a while late but what you can do is add this to your component:
import ReactDOM from 'react-dom';
... some more codez...
componentDidUpdate() {
const anchor = this.props.location.hash.replace('#', '');
if (anchor) {
const domElement = ReactDOM.findDOMNode(this.refs[hash]);
if (domElement) {
domElement.scrollIntoView();
}
}
}
and then on your actual element that you want to navigate to, you need to add the ref attribute, like this:
<h1 ref="my-anchor">Hello World</h1>
The link element is going to look just like a regular link:
<a href="/about#my-anchor>Go To Anchor</a>
Or with react-router:
<Link key="my-anchor" to="/about#my-anchor">Go To Anchor</Link>
This works with react-router 2.6.1 - not sure about later versions at this point.
Hope this helps!
Improved answer above:
componentDidMount() {
const anchor = this.props.location.hash;
if (anchor) {
const domElement = document.querySelector(anchor);
if (domElement) {
domElement.scrollIntoView();
}
}
}
I highly recommend to use the package from Rafael Pedicini called react-router-hash-link.
Live example right there. It's working well and the code is clean.

How to add body element in facebook's reactjs my-app?

I want to add a background image on Facebook's react.js my-app. I can do this by setting a background image to a div element. But my div only covers half of the page. Which is why my image is not fulling occupying the page.
Instead, I want to do this on a body element. But how to add a body element and set a background image to it?
I tried this, but its not working in App.js file of my-app
<body background="http://i.imgur.com/DaNQJ6I.jpg"></body>
[EDITED]
Css-tricks.com provides a quite cool solution of the full page images:
https://css-tricks.com/perfect-full-page-background-image/
I used this example for my React implementation with the useEffect (https://reactjs.org/docs/hooks-effect.html).
Note: background-size replaced by backgroundSize when the style is setted.
import React, { useEffect } from "react";
import img from "./test.jpg";
export default function App() {
useEffect(() => {
document.body.style.background = `url('${img}') no-repeat center center fixed`;
document.body.style.backgroundSize = "cover";
document.body.style.webkitBackgroundSize = "cover";
}, []);
return <div className="App"></div>;
}
Try using style:
<body style={ bodyStyle }></body>
And bodyStyle should look like:
var bodyStyle = { backgroundImage: 'url(http://i.imgur.com/DaNQJ6I.jpg)' };
For some reason, I cannot add a background image on app.js or app.css files of react framework. However, I tried to do it on index.html. This worked perfectly to me.
So, please add background images on index.html If you are unable to do it in app.js or app.css.
And also, future readers, If you have found a way to do it, feel free to answer the question, thanks!!
This works for me. The actual work is done in componentDidMount(). So technically, you could add this on about any component. Like, you could put it on a navbar or even in the layout.
For my projects, I created a component. Add it to your layout (or where ever) and it should work.
Good luck!
export default class BodyStyle extends Component {
componentDidMount() {
const { style } = this.props;
var body = document.getElementsByTagName( "body" )[ 0 ];
for ( var key in style ) {
body.style[ key ] = style[ key ];
}
}
render() {
return <span ref="BodyStyle"/>;
}
};