Initialize VueJs after opening new tab - vue.js

At the moment I use php to collect data needs to be printed in one pdf and open pdf in new browser tab. The problem is that it generate to much traffic as soon as I've a lot of users and a lot of docs to be printed in a same time. So I'm looking for a way to collect everything in one html, open it in new tab, fill with data from back-end and print it after.
My issue is to initialize VueJS for the new browser tab. I always have the following warning in console:
vue.js:1141 [Vue warn]: Cannot find element: #app
I guess the reason is that VueJS works with SPA. Is there any way to implement my idea?
Here is template for a new tab needs to be opened in a new tab.
<div id="app">
<printing-form v-for="printedForm in printedForms" track-by="id" :printed-form="printedForm"></printing-form>
</div>
<template id="printingForm-template">
Some HTML need to be printed in new tab.
</template>
Below my JS code to init VueJS
var printButton = $('#print-button');
printButton.on('click', function(e) {
e.preventDefault();
printButton.unbind('click');
printButton.css('color', '#008000');
var idNumbers = TopPanel.getArrayOfId();
if (idNumbers == '') {
idNumbers = TopPanel.getIdNumber();
}
var url = window.location.href + '/form';
$.ajax({
url: url,
data: 'id=[' + idNumbers + ']',
success: function(data) {
var newWindow = window.open(url); //Open URL in new tab
var printedIds = $.parseJSON('[' + idNumbers + ']');
printedIds.forEach(function(item, i, arr) {
var printedField = $('#top-row-'+item).find('.table-field-printed');
if (!printedField.hasClass('true-value')) {
printedField.addClass('fa');
printedField.addClass('fa-check');
printedField.addClass('true-value');
}
});
printButton.css('color', '#4f5762');
printButtonInit();
newWindow.onload = function() {
VuePrinting.vueInit(newWindow); //Here I initialize VueJS module
}
},
error: function(xhr, textStatus, errorThrown) {
alert($.parseJSON(xhr.responseText));
printButton.css('color', '#4f5762');
printButtonInit();
},
});
});
}
This separate module for VueJs
var VuePrinting = (function() {
return {
vueInit: function() {
Vue.component('printingForm', {
temlate: newWindow.document.getElementById('printingForm-template')
});
var vm = new Vue({
el: newWindow.document.getElementById('app'),
data: {
printedForms: [
{ obj1 },
{ obj3 },
{ obj3 }
]
}
});
}
}
})();
UPDATE: I've corrected my code after RoyJ comment, so now it works correct. Only one note... template should also be changed from selector to:
newWindow.document.getElementById('printingForm-template')

When you specify a selector for your el, Vue will look in the current document for it. The new tab will not be part of the current document.
Save a reference when you open the tab. Write the HTML into its document. Query the #app out of the document, and use the actual element instead of a selector in your Vue setup:
var vm = new Vue({
el: newWindow.document.getElementById('app')
});

Related

Show Image EXIF for User with VueJS

I work with Exif.js and VueJS for my project.
but i have problem.
when i want show information of image on screen it doesn't work.
but it work on Browser Console.
How i show information with tag in html for Users?
Here is my code:
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Vue exif meta info getter',
DateImage: "DateTimeDigitized"
},
components: {
'picture-input': PictureInput
},
methods: {
onChange(image) {
console.log('onChange!')
if (image) {
EXIF.getData(this.$refs.pictureInput.file, function () {
console.log('image info', this)
console.log('exif data', this.exifdata)
console.log("date image jadid : " + this.DateImage);
})
} else {
console.log(`it's not image`)
}
},
getEI() {
var old = console.log;
var logger = document.getElementById('log');
console.log = function (message) {
if (typeof message == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
} else {
logger.innerHTML += message + '<br />';
}
}
}
}
})
</script>
you have an issue with data and reactivity. Here the concepts from the vue guide.
"Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive."
Means data should be a function for reusability and you need declare a variable for apply the exif values and be capable of show them updated in the screen
Extra, next time include your html part sometimes the errors will be there.
A extra common problem starting with vue is the use of this, well this inside exif it's not the same to this in vue. An easy way for bypass 'this' issue is save the vue this in a temporary variable (let vm = this) and use them inside the exif code.
Just like that:
<template>
<!-- exifs is an object, you can print direct a value
if you know their key (exifs.Name) or you can iterate
with v-for and show all the values -->
<p v-for="ex in exifs">
{{ex}}
</p>
</template>
<script>
const app = new Vue({
el: '#app',
data() {
return {
message: 'Vue exif meta info getter',
DateImage: "DateTimeDigitized",
exifs: null
}
},
components: {
'picture-input': PictureInput
},
methods: {
onChange(image) {
console.log('onChange!')
if (image) {
let vm = this
EXIF.getData(this.$refs.pictureInput.file, function () {
vm.exifs = this
})
} else {
console.log(`it's not image`)
}
},
getEI() {
var old = console.log;
var logger = document.getElementById('log');
console.log = function (message) {
if (typeof message == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(message) : message) + '<br />';
} else {
logger.innerHTML += message + '<br />';
}
}
}
}
})
</script>
Here the example you use from jsfiddle fixed

How to call a global component with out HTML tag in Vue?

I want a global dialog component which is called only by JavaScript. And never had custom content within it. So I don't want to put any HTML tag like <my-dialog ref="myDialog"></my-dialog> in my code. Just call this.$ref.myDialog.show().
I have a HTML tag version. How to instance the component only in JavaScript?
I think you neeed to create a JS window with a component inside
here is an example:
var componentName = "my-dialog";
var model = {d:1};
var d = document.createElement("div"); // JavaScript Window
document.body.appendChild(d);
d.id = 'win' + componentName;
var app = new Vue({
render(h, data) {
return h(componentName, { on: { 'close': this.close }, props: { model: this.model } });
},
el: d, data: { wait: false, error: "", after: 0, model },
mounted() {
},
methods: {
close() {
this.$destroy();
$(d).remove(); // remove the window by jQuery
}
}
})
First you need to assign Vue to window.vue.
window.vue = new Vue({ // options })
then call it using js. vue.$ref.myDialog.show()

Cant mount children component to ref

I have a problem with VuePaginator , that I can mount it to my Vue app $refs properties. I am doing everyting according to docs, here is my component in the html:
<v-paginator :resource.sync="comments" ref="vpaginator" resource_url="{{route('api.item.comments', $item->pk_i_id)}}"></v-paginator>
The pagination works correctly, but I can't trigger fetchData() from the vuejs code, because paginator is not getting mounted to vm.$refs.vpaginator.
Here is the code that I use:
var app = new Vue({
el: '#comments',
data : {
comments: [],
newComment: {
text: ""
}
},
components: {
VPaginator: VuePaginator
},
methods: {
addComment: function(comment){
var vm = this;
this.$http.post($('meta[name="item-url"]').attr('content'), comment)
.then(function(response){
toastr.success(response.data.result);
comment.text = "";
vm.$.vpaginator.fetchData();
}).catch(function (error) {
if(error.data){
toastr.error(error.data.text[0]);
}
})
},
logRefs: function(){
console.log(this.$refs.vpaginator);
}
}
});
I have created logRefs() function to check the $ref property and it is always undefined.
Since you are using the Version 1 of VueJS, usage is a bit different - check this demo http://jsbin.com/rupogesumo/edit?html,js,output
<v-paginator :resource.sync="comments" v-ref:vpaginator resource_url="{{route('api.item.comments', $item->pk_i_id)}}"></v-paginator>
Docs Reference: https://v1.vuejs.org/api/#v-ref

Handle methods differently in Vue depending on mobile or not

I'm having trouble setting up my Vue components to handle their methods differently if the user is on mobile. For instance a navigation drop down, if a user clicks on a link, I want to prevent them from going to that location, but instead drop down the drop down. Whereas on desktop, I want them to go to it if they click on it and only drop down on hover. I'll need this for so many other aspects of my project.
I have a main Vue instance:
var Main = new Vue({
el: 'body',
data: {
mobile: true
},
ready: function() {
if( document.clientWidth >= 992 )
{
this.mobile = false;
}
}
});
export default Main;
Then for my components, I'm doing something like this:
import Main from './../Main';
var NavLink = Vue.component('navlink', {
template: '#nav-link-template',
replace: true,
data: function() {
return {
}
},
props: ['text', 'url'],
ready: function() {
},
methods: {
handleClick: function(e) {
e.preventDefault();
console.log(Main.mobile);
if( Main.mobile )
{
if( this.$children.length )
{
// Has dropdown
this.$children[0].dropDown();
}
else
{
// No dropdown so redirect user
window.location = this.url;
}
}
else
{
// Not mobile so let user go
window.location = this.url;
}
}
}
});
Not only does Main.mobile return the default value no matter what resolution because their ready methods seem to run BEFORE the Main ready method.. but this also feels like the wrong setup.
Thanks for any insight.
First, according to you code, you dont need Main commonjs module to be a vuejs instance. Make it as a simple js object
Main = {
mobule: document.clientWidth >= 992
}
export default Main;
Or you may want to handle client window size dynamically
var Main = new Vue({
created: function() {
// dunno why v-on="resize: func" not working
global.window.addEventListener('resize', function () {
//calc width and heigh somehow
self.$broadcast('resize', width, heigh);
});
}
});
export default Main;

Adapter response isn't displayed in a Dojo ListItem

I am using a Worklight adapter to get the RSS Feed from a web site; the adapter gets me the data in XML format, but the problem is I can't display the data in a Dojo LisItem.
These are the JS functions to call the Adapter:
function loadFeedsSuccess(result) {
console.log("Data sucesfully downloaded, HTTP " + result.status);
if(result.invocationResult.Items.length > 0) {
console.log("Server has returned " + result.invocationResult.Items.length + " item(s)"); displayRSSFeed(result.invocationResult.Items);
}
}
function loadFeedsFailure(result) {
console.log("Error while loading RSS feed: " + result.errorMessage);
}
function displayRSSFeed(rawData) {
var store = new dojo.store.Memory({data:rawData, idProperty: "guid"});
require(["dijit/registry"], function(registry){ var newsList = registry.byId("newsList"); dojo.empty("newsList");
store.query(function(news){
var newsItem = dojox.mobile.ListItem({label:news.title}); newsList.addChild(newsItem); });
});
}
function getNewsInit() {
var invocationData = {
adapter: "FeedReader",
procedure: "getStoriesFiltered"
};
var options = {
onSuccess: loadFeedsSuccess,
onFailure: loadFeedsFailure
};
WL.Client.invokeProcedure(invocationData, options);
}
The Browser doesn't diplay the data an shows the following Error:
[/NewsApp/apps/services/api/News/common/query] exception. ReferenceError: dojo is not defined worklight.js:4673
Uncaught ReferenceError: dojo is not defined
Any one have any idea how to fix my Problem?
if you're using Dojo and setting the async configuration property to true, then the dojo namespace is no longer available. This means that you can't use dojo.store.Memory or dojox.mobile.ListItem anymore.
To solve that issue you either have to disable the async function or use AMD to load your modules:
function displayRSSFeed(rawData) {
require([ "dijit/registry", "dojo/store/Memory", "dojox/mobile/ListItem", "dojo/dom-construct" ], function(registry, Memory, ListItem, domConstruct) {
var store = new Memory({data:rawData, idProperty: "guid"});
var newsList = registry.byId("newsList");
domConstruct.empty("newsList");
store.query(function(news){
var newsItem = new ListItem({label:news.title});
newsList.addChild(newsItem);
});
});
}
If it then throws the error:
ReferenceError: require is not defined
Then it means you're load loading the Dojo core at all, make sure you're loading dojo.js.