how to use this.$refs in vue 3 composition api? - vuejs3

I use vue3-simple-html2pdf.
<vue3-simple-html2pdf ref="vue3SimpleHtml2pdf" :options="pdfOptions" :filename="exportFilename">
<div class="main-content">
<h1>PDF</h1>
</div>
</vue3-simple-html2pdf>
<button #click="download">Download pdf</button>
documentation says to do this when i want to download pdf
this.$refs.vue3SimpleHtml2pdf.download()
I use composition api and try to do like this
const vue3SimpleHtml2pdf = ref(null)
const download = () => {
vue3SimpleHtml2pdf.download()
}
but it doesn't work
how can I fix it?

Your code is not complete to understand how you have built your app. Vue App section is not shown.
UPDATE
Here is looks like you are trying to use the download() function from 'null'.
const vue3SimpleHtml2pdf = ref(null)
const download = () => {
vue3SimpleHtml2pdf.download()
}
This does not make any sense to me.
It looks like you are mixing ref() and $refs up. The first is the Special Attribute ref with access through the Vue App property $refs and the second is the ref() reactivity function. Please check the docs on how to use them.
The vue3-simple-html2pdf plugin has a great explanation page and a working sandbox app.
If you are not sure how to use the plugin with the Composition API, then I would suggest you to try to make your code work with the Options API using the sample from the Sandbox.
UPDATE 2
Please read the Docs on using the Composition API, starting from
Creating a Vue Application and especially Components Basics.
The Vue Tutorial Step 11. Components is also very helpful.

I forgot that I should refer to the
vue3SimpleHtml2pdf.value.download()
not to the
vue3SimpleHtml2pdf.download()
another solution is to call the function on click in the template
<button #click="this.$refs.vue3SimpleHtml2pdf.download()">Download pdf</button>

Related

Loading local file inside an iframe in Electron

I've tried different approaches but all are problematic.
So first of all I was using webview, but as per electron documentation, this tag is undergoing major architectural changes and it's recommended to use iframe or other alternatives. Furthermore, the webview tag gives me a warning while used alongside VueJS that the component is not registered. I understand this component doesn't exist within HTML standards and is something specific to electron, so I am not sure how to tell Vue to ignore or recognize it in the use case of an electron app.
Coming to the iframe problem, approach one of loading the file directly via src, gives me the obvious error Not allowed to load local resource:. Turning off webSecurity though allows the file to load but I read it's not recommended to turn it off. I am not sure if there are specific use case where it's safe to turn it off or shouldn't be at all.
I decided to try via file protocol as I already have it in place. The protocol code:
protocol.registerFileProtocol('downloads', (request, callback) => {
const url = request.url.substring('downloads:///'.length)
const location = path.normalize(paths.downloads(url))
callback({ path: location })
})
Though when I load the file this way, the renderer process crash without errors. Is there something in addition to the above which would help loading local files via iframe?
Edit 1
My use case is the following: I have a typical entry point to an index.html which contains code for a VueJS app.
if (app.isPackaged) {
window.loadFile(join(__dirname, '../renderer/index.html'))
} else {
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
window.loadURL(url)
window.webContents.openDevTools()
}
Inside that VueJS app, I require to list html files from a directory. I am able to achieve so via webview but I have tried to move away from it for the reason mentioned above. I tried using iframe but encountered issues as well. If there's a setting that doesn't turn off all security and allows me to load the file via iframe, that would be ideal.
This is kind of the reverse of this question where they're using an iframe, running into the "not allowed to load local resource" and being told to use a <webview> instead.
The <webview> docs list BrowserView as another alternative which is what I would recommend here. That should be much easier to work with than an iframe.
const { app, BrowserView, BrowserWindow } = require('electron')
app.whenReady().then(() => {
const win = new BrowserWindow()
const view = new BrowserView()
win.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadFile('<yourFile>')
})
Even though it is not recommended to use webview tag, I decided to go forward as it's the only thing that works for me. The only issue then was this error where Vue does not recognize the tag. To work around that error/warning, I had to update my vite.js config:
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === 'webview'
}
}
}),
// ...

How to remove __NEXT_DATA__ from dom in NextJs?

Because of NEXT_DATA element dom size is getting very high and it is affecting to the performance. Can anyone plz help me to remove NEXT_DATA from dom?
I am using full server-side rendering with dynamic routes in next Js.
TLDR: If you would(/could) remove the __NEXT_DATA__ script tag, React wouldn't be able to hydrate. You either hardcode the data in the page or try to reduce your pageProps payload, returned from getServerSideProps.
I came upon this issue also recently, where I asked myself, why would there be a need for the content to be included in the HTML 2 times.
As the content itself - when your NextJS renders the appropriate HTML and sends it to the client
As JSON in <script> tag - This is because of the need for rehydration on the client side.
"Solutions"
Returning only necessary data from data-fetching method - I can recommend reading up on this article Reducing HTML payload with NextJS, in which they talk about formatting / aggravating the necessary data and returning only the needed fields.
Not using the data-fetching method and hardcoding static content - The idea behind using static data fetching, if not using revalidate option, is that the content shouldn't be changing (maybe ever). So in that case, why couldn't we hardcode the data in the page itself. Althought downside of this of course is, having to write it all out manually / download the required content to some JSON / object and then use it in the page like so.
Reading
Here's a github link with related discussion in next in which you might be interested.
Blog post about reducing the size of __NEXT_DATA__ - by Liran Cohen.
To remove the rehydration data regex: <script id="__NEXT_DATA__((.|n)*)script> from all pages of the static website run the following command after the build has finished:
find out -name '*.html' | xargs perl -0777 -pi -e 's/<script id="__NEXT_DATA__.*?script>//sg;'
If you come across this issue, make sure you don't have any conditional rendering. This is how I solved this issue myself !
You can do this to not show __NEXT_DATA__:
export const config = {
unstable_runtimeJS: false,
};
But all JavaScript functionality will not work in frontend.
Sometimes you might not require some of the __NEXT_DATA__ props during client side rendering.
To drop those props, you can mutate the NextScript.getInlineScriptSource function in the _document.tsx file. Here is an example:
// _document.tsx
const nextInlineScriptSource = NextScript.getInlineScriptSource;
NextScript.getInlineScriptSource = (props) => {
if (props?.__NEXT_DATA__?.foo) {
props.__NEXT_DATA__.foo = 'bar';
}
return nextInlineScriptSource(props);
};

NextJS, _app, SSG, getInitialProps vs getStaticProps and how am I supposed to stick to DRY?

I know that the topic isn't new and I found (and read) a few discussions. What I couldn't find is an answer to my still remaining question.
How do others work around the problem of not being able to use getStaticProps in _app.js ? getInitialProps is not an option for me cause I want to use SSG. Is there a way to force SSG even when using getInitialProps? Or do I really have to fetch all my data from my Headless CMS on every page? Since I want to build a Header (including Navigation) and a Footer, for example. Currently the only option I see is to repeat a lot of code.
Any hints much appreciated and thanks for reading!
Next.js recommends using getStaticProps or getServerSideProps instead of getInitialProps to get the best performance from Next.js.
If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.
The reason why Next.js recommends this is that getInitialProps disables Automatic Static Optimization and uses Server-side Rendering.
Using getInitialProps in your _app.js will disable automatic static optimization for all pages in your application. In addition, it will force all your pages to be generated on every request, as they use server-side rendering, which will lead to a bad Time to First Byte (TTFB).
I've made a cheat sheet about Next.js page rendering with live demo and code examples on Github to help understand quickly how it works. This will help you choose the right page rendering strategy with Next.js depending on your use case.
Is there a way to force SSG even when using getInitialProps
No. Although Nextjs team is working to add support for getStaticProps for _app.js, currently it disables static optimizations.
As nextjs recommends, you should use getStaticProps when "The data comes from a headless CMS."
So you'll have to have to do the same logic in every page that needs the request. Note that's possible to extract this logic into a function and reutilize it everywhere
getData.js
export async function getData({ req, res }) {
const data = await getData();
return { props: { data: data} };
}
pages/example.js
export const getStaticProps = getData
As another option, you can fetch data once in getInitialProps.
Is easy to setup (but remember that you'll lose static optimization.)
_app.js
App.getInitialProps = ({ req, res }) => {
const data = await getData();
return { props: { data: data} };
}

Ember js, show spinner when something is loading in background

Is there a way with EmberJS to show a loading template somewhere when something in my page is loading something?
For example, I have a page with many many songs artists, and each of them have many many songs. I load the page of the artists, and good, but in background I'm loading info about songs of everyone. I simply need a loading spinner somewhere that says to me the site is loading something (for example, there is in "Network" tab of Chrome Developer Tools something pending...)
how to do that in such an elegant Emberjs way?
You can observe the isPending property of Ember.PROMISEPROXYMIXIN like so:
{{#if artist.songs.isPending}}
loading...
{{/if}}
As far as I know (and based on a quick perusal of the API docs) Ember doesn't provide a built-in way to achieve this. Unless I'm wrong, this means you'll need to implement request state tracking yourself.
You could do this in Ember's adapter layer (e.g. add code in app/adapters/application.js), but the simplest solution might be to just work with jQuery directly. There are a few APIs you can use:
jQuery.active, which indicates the number of outstanding requests
ajaxStart and ajaxStop
I'd recommend creating an Ember.Service to track this state-- then you can inject it in any Controller or Component that needs to render a template based on this info. Your Service could look like:
import Ember from 'ember';
export default Ember.Service.extend({
init() {
this._super(...arguments);
const invalidateRequestInProgress = () => {
this.notifyPropertyChange('dummyProperty');
}
$(document).ajaxStart(invalidateRequestInProgress);
$(document).ajaxStop(invalidateRequestInProgress);
},
requestInProgress: Ember.computed('dummyProperty', function() {
return Ember.$.active !== 0;
}),
});
I haven't tested this code, so I'm not sure if it'll work in practice :)

Using third party javascript package with Meteor

I'm working with Meteor at the moment, and I'm trying to make it look more 'real timey' by adding transitions to numbers as they change. The best third party package I can see for that is http://github.hubspot.com/odometer/.
I'm having trouble getting the package to work in Meteor to update comment numbers on an item.
I've tried putting the javascript into client/compatibility as per the meteor docs: http://docs.meteor.com/#structuringyourapp, but no joy.
The other issue might be that the package uses CSS transitions, which would mean that a re-rendering of the template around the number that is updating would prevent the transition from occurring. To try and fix this problem, I used {{#isolate}} around the number, but that didn't work either.
Has anyone got any other ideas on what else in meteor might be getting in the way?
I think you should try {{#constant}} instead of {{#isolate}}. Also note, that the "constant" part of your template will no longer be reactive, so you'll have to update it manually. Supposing that you have a template
<template name="myTemplate">
{{#constant}}
<span class="odometer"></span>
{{/constant}}
</template>
you will need to do something like this:
Template.myTemplate.rendered = function () {
var node = this.find('.odometer');
Deps.autorun(function () {
node.innerHtml = MyCollection.find({}).count();
});
}

Resources