vue + pure html component? - html

how to implement vue-cli calling pure html method?
Please check below as sample file. because I have custom pure html code and facing difficulty converting it to vuejs.
product.vue
<template>
<custombutton #childclick="childclick" />
</template>
<script>
export default {
component: { custombutton },
methods: {
childclick(value) {
console.log(value)
}
}
}
</script>
custombutton.html
<html>
<body>
<button #click="childclick" />
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<script>
var App = new Vue({
el: '#app',
data() {
return {
vueMessage: 'testing vue message',
}
},
methods: {
childclick() {
this.$emit('childclick', 'success!')
}
},
});
</script>
</html>

Assuming you want to preserve the possibility to call Vue component from both .html file and .vue files, I would recommend moving your Vue logic from custombutton.html file into a separate custombutton.js file, which will export an object representing Vue component previously defined in HTML. You can then import custombutton.js both in custombutton.html and product.vue.
Example:
custombutton.js:
export default {
name: 'CustomButton',
template: '<div>Hello world!</div>'
}
custombutton.html:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module">
import CustomButton from "./custombutton.js"
new Vue({
el: '#app',
components: { CustomButton },
template: '<CustomButton />'
});
</script>
</body>
</html>
product.vue:
<template>
<CustomButton />
</template>
<script>
import CustomButton from "./custombutton.js"
export default {
components: { CustomButton }
}
</script>
Note that:
For custombutton.html to work target browsers should implement ECMAScript modules.
For product.vue to work you should set runtimeCompiler: true in your Vue CLI configuration. (Since the template in custombutton.js is defined as a string runtime compiler needs to be included in the final bundle.)
It is important to find a suitable location for custombutton.js so it can be imported easily using relative paths from both custombutton.html and custombutton.vue files.
Defining the template as a string in custombutton.js can be quite inconvenient, as you have no markup highlighting, however I don't see any workaround for this.

This solution is a bit more cumbersome, but allows template to be written with syntax highlighting into <script> element, which is more convenient than writing template as a string literal into a template property.
custombutton.js:
export default {
name: 'CustomButton',
data: function () {
return { testVar: 125 }
}
}
custombutton.component.html:
The template is now located in #custombutton-template element. "Hello world! 125" should be printed out.
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
</head>
<body>
<script id="custombutton-template" type="x-template">
<div>Hello world! {{ testVar }}</div>
</script>
<div id="app"></div>
<script type="module">
import CustomButton from "./custombutton.js"
CustomButton.template = "#custombutton-template"
new Vue({
el: '#app',
components: { CustomButton },
template: '<CustomButton />'
});
</script>
</body>
</html>
product.vue: Thanks to Webpack config (see below) we are able to import contents of custombutton.component.html file as a string, parse it and get inner HTML of #custombutton-template element, which is the desired template.
<template>
<CustomButton />
</template>
<script>
import CustomButton from "./custombutton.js"
import CustomButtonComponent from "./custombutton.component.html"
var el = document.createElement("html")
el.innerHTML = CustomButtonComponent
var template = el.querySelector("#custombutton-template").innerHTML
CustomButton.template = template
export default {
components: { CustomButton }
}
</script>
Importing custombutton.component.html file as string is done by non-default Webpack raw-loader, therefore it is necessary to adjust Webpack config:
vue.config.js:
module.exports = {
runtimeCompiler: true,
configureWebpack: {
module: {
rules: [
{
test: /\.component\.html$/,
use: ["raw-loader"]
}
]
}
}
}
package.json:
{
"devDependencies": {
"raw-loader": "^4.0.2"
}
}

Related

How load html template in vuejs

I use vue.js and vue-router.js.
I added both of files to a html page
This is my component for load template...
const Dashboard = {template: "<strong>"}
But I want load html page dynamically and with HTTP URL.
const Dashboard = {template: "How load html page with url(http)"}
Can anyone guide me?
you could define a component similar to this
Vue.component('dynamic-component', {
props: {
contentURL: String,
},
template: `<div v-html='inner_html'></div>`,
computed: {
inner_html() {
return // http call to get html value utilizing this.contentURL
},
},
});
then from the router
{
path: '/path/option-a',
components: {
DynamicComponent
},
props: {
contentURL: `https://some-resource/option-a`
}
},
{
path: '/path/option-b',
components: {
DynamicComponent
},
props: {
contentURL: `https://some-resource/option-b`
}
}
For creating layers, you can create vue component and use slot
Layout.vue
<template>
Header
AnyComponents
<slot />
Footer
</template>
and in your page component you should import your layout and wrap
MainPage.vue
<template>
<MainLayout>
Here is your conntent of page or router
<router-view />
</MainLayout>
</template>
<script>
import MainLayout from 'layout.vue';
export default {
components: {
MainLayout
}
}
</script>
And now for render your page you should use your page in router
example of router.js
import Vue from 'vue';
import Router from 'vue-router'
import MainPage from 'MainPage.vue';
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
component: MainPage,
}]
})
If you want to insert html to your page, use v-html
<div class="content" v-html="dashboard.template">
</div>

Not load a script in index.html before angular2 modules are all loaded

I'm trying to implement an angular2 app that has a dependency on a 3rd party js library, that is getting included in index.html script tag. Trying to implement the sales force external identity solution as outlined here.
this script is looking for some meta tags in the page in order to function properly, whether to pop-up as a model or inline form for example.
I'm using the angular platform browser's Meta functionality to set the meta tags in the page, implemented in the constructor of app.component.ts.
This seems to have created a race condition, as in certain situations (typically, when the script exists in the browser cache) it throws alerts that the required meta tags aren't available.
How can I stop the loading of the script in index.html (I've set it with the defer and async keywords btw) to not load until AFTER the app.component has set the meta tags?
app.component.ts
import {Component, OnInit} from '#angular/core';
import {TodoService} from './providers/todo.service';
import {Meta} from '#angular/platform-browser';
import {environment} from '../environments/environment';
import { isNull } from 'util';
declare var SFIDWidget: any;
declare var SFIDWidget_loginHandler: any;
declare var SFIDWidget_logoutHandler: any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [TodoService]
})
export class AppComponent implements OnInit {
constructor(public meta: Meta) {
//meta tag stuff
//check for meta tags and add if not exists
if (isNull(this.meta.getTag('name="salesforce-logout-handler"'))) {
this.meta.addTags(
[
{
name: "salesforce-client-id",
content: `${environment.clientId}`
},
{
name: "salesforce-community",
content: `${environment.communityURL}`
},
{
name: "salesforce-redirect-uri",
content: `${environment.redirectURL}`
},
{
name: "salesforce-mode",
content: "modal"
},
{
name: "salesforce-forgot-password-enabled",
content: "true"
},
{
name: "self-register-enabled",
content: "true"
},
{
name: "salesforce-save-access-token",
content: "true"
},
{
name: "salesforce-target",
content: "#salesforce-login"
},
{
name: "salesforce-login-handler",
content: "onLogin"
},
{
name: "salesforce-logout-handler",
content: "onLogout"
}
], true);
} else {
console.log('meta tags exist!');
}
}
ngOnInit() {
}
onLogin(identity) {
console.log('fired from the app component');
}
onLogout() {
SFIDWidget.init();
}
}
index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Herokudos</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script
src="https://sdo-134f326f986sdo-134f326f986.force.com/servlet/servlet.loginwidgetcontroller?type=javascript_widget&min=false&cacheMaxAge=0"
async defer></script>
<link
href="https://sdo-134f326f986sdo-134f326f986.force.com/servlet/servlet.loginwidgetcontroller?type=css"
rel="stylesheet" type="text/css"/>
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
Remove the <script> tag that you hardcoded into index.html. If you want to load scripts that depend on Angular being ready, consider loading them dynamically in the component's ngAfterViewInit() lifecycle hook. This will guarantee that the script will load only after your AppComponent has been rendered.
AppComponent
/*executes once the component template is built*/
ngAfterViewInit(){
//Add a script tag to the DOM
let script = document.createElement('script');
document.body.appendChild(script);
//listen for the load event on the script...
Observable.fromEvent(script,'load').first().subscribe(
//...and call onScriptLoad once the script is loaded
()=>this.onScriptLoad()
);
//set script source: will trigger the download
script.src = "https://sdo...";
}
/*executes once the script has been loaded*/
onScriptLoad(){}

Render Component in iframe using vuejs without src attribute

<iframe id="frame" width="100%" height="100%">
</ifrme>
I want to render component in this iframe. Is there any option of creating html element or rendering component in iframe?
new Vue({
el:'#frame',
store:store,
router:router,
render: component
})
The easiest way for me is to use srcdoc attribute. It loads raw html overriding src attribute.
<template>
<iframe :srcdoc="html"></iframe>
</template>
Update: More detail example: Consider a textarea for a user html input and want to display in an iframe.
<template>
<div id="app">
<textarea v-model="html"></textarea>
<iframe :srcdoc="html"></iframe>
</div>
</template>
<script>
export default {
name: "App",
data(){
return {
html:"<h1>Hello I am H1 block</h1>"
}
}
};
</script>
You can refer below link That helped me a lot.
Here is the link and the code snippets.
Vue.component('i-frame', {
render(h) {
return h('iframe', {
on: { load: this.renderChildren }
})
},
beforeUpdate() {
//freezing to prevent unnessessary Reactifiation of vNodes
this.iApp.children = Object.freeze(this.$slots.default)
},
methods: {
renderChildren() {
const children = this.$slots.default
const body = this.$el.contentDocument.body
const el = document.createElement('DIV') // we will mount or nested app to this element
body.appendChild(el)
const iApp = new Vue({
name: 'iApp',
//freezing to prevent unnessessary Reactifiation of vNodes
data: { children: Object.freeze(children) },
render(h) {
return h('div', this.children)
},
})
iApp.$mount(el) // mount into iframe
this.iApp = iApp // cache instance for later updates
}
}
})
Vue.component('test-child', {
template: `<div>
<h3>{{ title }}</h3>
<p>
<slot/>
</p>
</div>`,
props: ['title'],
methods: {
log: _.debounce(function() {
console.log('resize!')
}, 200)
},
mounted() {
this.$nextTick(() => {
const doc = this.$el.ownerDocument
const win = doc.defaultView
win.addEventListener('resize', this.log)
})
},
beforeDestroy() {
const doc = this.$el.ownerDocument
const win = doc.defaultView
win.removeEventListener('resize', this.log)
}
})
new Vue({
el: '#app',
data: {
dynamicPart: 'InputContent',
show: false,
}
})
https://jsfiddle.net/Linusborg/ohznser9/
I've tried and haven't found a way to mount vue directly on #iframe.
Yet, you can add div to #iframe and mount to that:
// create iframe element
var iframe = document.createElement('iframe')
iframe.id = 'iframe'
// place iframe inside page html
document.documentElement.insertBefore(iframe, document.querySelector('html').firstChild)
// append div to iframe
var container = document.createElement('div')
container.id = 'container'
iframe.contentWindow.document.body.appendChild(container)
// render vue component inside iframe on #container
new Vue({
el: container,
render: h => h(component)
})
Result:
<html>
<iframe id="iframe">
#document
<html>
<head>
</head>
<body><!-- <-- here was <div id="container"></div> -->
<div class="message" style="color: orange;">Hello from Vue component!</div>
</body>
</html>
</iframe>
<head>
</head>
<body>
</body>
</html>
P.s. I've used this code in chrome extension content scripts (javascript injected into pages). If you're going to use
it elsewhere make sure not to break Same-origin policy.
new Vue({
el:'#frame',
store:store,
router:router,
render: component
})
just try to give a name to your route view.
Hope it works

Vue 2 Failed to mount component

I'm using Laravel 5.2 and Vue 2.0.6. If I use local components, it works fine. But when I try to use global component from another .vue file, it shows following error:
[Vue warn]: Failed to mount component: template or render function not defined. (found in component <test> at path\to\site\resources\assets\blog\components\test.vue)
Test.vue
<template>
<div>
<h1 class="hello">Hello</h1>
</div>
</template>
<style>
.hello {
color:blue;
}
</style>
<script>
export default {}
</script>
App.js
var Vue = require('vue');
var resource = require('vue-resource');
window.Vue = Vue;
Vue.use(resource);
Vue.component('test', require('../components/test.vue'));
new Vue({
el: '#app'
});
Gulpfile.js
var gulp = require('gulp');
var elixir = require('laravel-elixir');
require('laravel-elixir-vue-2');
require('laravel-elixir-webpack-official');
var blogResourcePath = './resources/assets/blog';
var blogPublicPath = 'public/blog-assets';
elixir(function(mix) {
mix.webpack('app.js', blogPublicPath + '/js', blogResourcePath + '/js')
});
Webpack.config.js
'use strict';
const path = require('path');
module.exports = {
module: {
loaders: [
{
test: /\.js$/,
include: path.join(__dirname, 'resources/assets'),
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.vue$/,
loader: 'vue',
},
{
test: /\.css$/,
loader: 'style!css'
},
]
},
resolve: {
alias: {
vue: 'vue/dist/vue.js',
}
}
};
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="app">
<test></test>
</div>
<script type="text/javascript" src="{{ elixir('blog-assets/js/app.js')}} ">
</html>
There is no compilation error. A similar question tis here but that doesn't solve my issues.
I guess that this is because with Vue2.0, the default version is the one without template parser. You need to import Vue as follows:
import Vue from 'vue/dist/vue.js'
Please check LinusBorg answer here:
https://forum-archive.vuejs.org/topic/4399/vue-2-0-vue-warn-failed-to-mount-component-template-or-render-function-not-defined-found-in-root-instance/6

using paper-datatable-card in a custom-tag

//index.html
<html>
<head>
<link rel="import" href="test-table.html">
</head>
<body>
<template is="dom-bind" id="index">
<test-table data="{{data}}" ></test-table>
</template>
</body>
</html>
Polymer({
is: "test-table",
properties : {
data : {type : Array},
}
/*I dont know where should I put this stuff
"queryForIds:"
"getByIds :"
"set:"
"length:0"
*/
});
<dom-module id="test-table">
<template>
<paper-datatable-card id="datatableCard" header="Users" page-size="10" data-source="{{data}}" id-property="_id" selected-ids="{{selectedIds}}">
<paper-datatable id="datatable" data='{{data}}' selectable multi-selection selected-items="{{selectedItems}}">
<paper-datatable-column header="Id" property="_id" sortable>
<template>
<span>{{value}}</span>
</template>
</paper-datatable-column>
</paper-datatable>
</paper-datatable-card>
</template>
</dom-module>
as part of single page application I am using “paper-datatable-card” in my own custom-tag. I able to display the records but I’m not getting where I have to put the code for pagination. And I don’t want to put all records into dataSource at a time.
Any help is appreciated,
Thank you,
Venkat.
From within your Polymer component, you can set data in the ready method:
ready: function() {
this.data = {
queryForIds: function(sort, page, pageSize){
// implement me
},
getByIds: function(ids){
// implement me
},
set: function(item, property, value){
// implement me
},
length:0
};
}
Your comment question:
So I am unable to put the code from lines 117 to 133 in my custom elements as it doesnt support dom-bind
Answer (in Polymer 2.0) you can do it in your component constructor all the methods and all the data variables:
class YourComponent extends Polymer.Element {
contructor() {
super();
//in case if you use paper-datatable-card
this.data = {
get: function (sort, page, pageSize) {
return new Promise((resolve, reject) => {
const exampleData = [
{ 'name': 'someName1' },
{ 'name': 'someName2' }
];
resolve(exampleData);
});
},
set: function(item, property, value){...},
length: 1
};
//in case if you use paper-datatable only then without get or set properties
this.data = [ { name: 'someName' } ];
}
}