How to refer URL into Vue.Template link.
Template is longer and all operations are going to include to mounted/methods.
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: './views/templatebutton.html' //how to refer URL here.
})
You could read the local HTML file as a string, and then load the result into the template field. With a module loader (such as Webpack), you would use require() to import the HTML file:
// Foo.js
Vue.component('button-counter', {
template: require('./views/templatebutton.html')
})
Alternatively, if vue-loader is available to your project, you could use single file components, which allow importing the template from an external file:
<!-- Foo.vue -->
<template src="./views/templatebutton.html" />
demo
I solve this limitation using requirejs (
although it is not recommended).
You can load the text from html file by adding 'text!' before the template url and load it as text like:
var template = require('text!/assets/vuejs/controllers/venda_direta/cart.html');
and then use it as your template string:
...
template : template
...
Related
I'm looking for the right url to reference static assets, like images within Vue javascript.
For example, I'm creating a leaflet marker using a custom icon image, and I've tried several urls, but they all return a 404 (Not Found):
Main.vue:
var icon = L.icon({
iconUrl: './assets/img.png',
iconSize: [25, 25],
iconAnchor: [12, 12]
});
I've tried putting the images in the assets folder and the static folder with no luck. Do I have to tell vue to load those images somehow?
For anyone looking to refer images from template, You can refer images directly using '#'
Example:
<img src="#/assets/images/home.png"/>
In a Vue regular setup, /assets is not served.
The images become src="data:image/png;base64,iVBORw0K...YII=" strings, instead.
Using from within JavaScript: require()
To get the images from JS code, use require('../assets.myImage.png'). The path must be relative (see below).
So your code would be:
var icon = L.icon({
iconUrl: require('./assets/img.png'), // was iconUrl: './assets/img.png',
// iconUrl: require('#/assets/img.png'), // use # as alternative, depending on the path
// ...
});
Use relative path
For example, say you have the following folder structure:
- src
+- assets
- myImage.png
+- components
- MyComponent.vue
If you want to reference the image in MyComponent.vue, the path sould be ../assets/myImage.png
Here's a DEMO CODESANDBOX showing it in action.
A better solution would be
Adding some good practices and safity to #acdcjunior's answer, to use # instead of ./
In JavaScript
require("#/assets/images/user-img-placeholder.png")
In JSX Template
<img src="#/assets/images/user-img-placeholder.png"/>
using # points to the src directory.
using ~ points to the project root, which makes it easier to access the node_modules and other root level resources
In order for Webpack to return the correct asset paths, you need to use require('./relative/path/to/file.jpg'), which will get processed by file-loader and returns the resolved URL.
computed: {
iconUrl () {
return require('./assets/img.png')
// The path could be '../assets/img.png', etc., which depends on where your vue file is
}
}
See VueJS templates - Handling Static Assets
Right after oppening script tag just add import someImage from '../assets/someImage.png'
and use it for an icon url iconUrl: someImage
this finally worked for me, image passed as prop:
<img :src="require(`../../assets/${image}.svg`)">
What system are you using? Webpack? Vue-loader?
I'll only brainstorming here...
Because .png is not a JavaScript file, you will need to configure Webpack to use file-loader or url-loader to handle them. The project scaffolded with vue-cli has also configured this for you.
You can take a look at webpack.conf.js in order to see if it's well configured like
...
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
...
/assets is for files that are handles by webpack during bundling - for that, they have to be referenced somewhere in your javascript code.
Other assets can be put in /static, the content of this folder will be copied to /dist later as-is.
I recommend you to try to change:
iconUrl: './assets/img.png'
to
iconUrl: './dist/img.png'
You can read the official documentation here: https://vue-loader.vuejs.org/en/configurations/asset-url.html
Hope it helps to you!
It works for me by using require syntax like this:
$('.eventSlick').slick({
dots: true,
slidesToShow: 3,
slidesToScroll: 1,
autoplay: false,
autoplaySpeed: 2000,
arrows: true,
draggable: false,
prevArrow: '<button type="button" data-role="none" class="slick-prev"><img src="' + require("#/assets/img/icon/Arrow_Left.svg")+'"></button>',
Having a default structure of folders generated by Vue CLI such as src/assets you can place your image there and refer this from HTML as follows <img src="../src/assets/img/logo.png"> as well (works automatically without any changes on deployment too).
I'm using typescript with vue, but this is how I went about it
<template><div><img :src="MyImage" /></div></template>
<script lang="ts">
import { Vue } from 'vue-property-decorator';
export default class MyPage extends Vue {
MyImage = "../assets/images/myImage.png";
}
</script>
You could define the assets path depending on your environment
const dev = process.env.NODE_ENV != 'production';
const url = 'https://your-site.com';
const assets = dev ? '' : url;
<template>
<img :src="`${assets}/logo.png`"/>
<p>path: {{assets}}</p>
</template>
<script>
export default {
data: () => ({
assets
})
}
</script>
Ideally this would be inside an utils js file, or as an extended app defineProperty, like:
const app = createApp(component);
app.config.globalProperties.$assets = assets;
app.mount(element);
and will be available as:
<template>
<img :src="`${$assets}/logo.png`"/>
<p>path: {{$assets}}</p>
</template>
<script>
export default {
mounted() {
console.log(this.$assets);
}
}
</script>
load them in created, mounted or where you need them
async created() {
try {
this.icon = (await import('#assets/images/img.png')).default;
} catch (e) {
// explicitly ignored
}
and then
<img :src=icon />
Inside code you can directly require image using.
const src = require("../../assets/images/xyz.png");
Or
In order to dynamically load image need this.
const image = new window.Image();
image.src = require("../../assets/images/xyz.png");
image.onload = () => {
// do something if needed
};
I have 2 problems:
If I use the variable in src:
I will get this link in the web and not exactly:
http://localhost:3000/~/assets/product/image/bg.png
Similar to the first problem.
I want to use dynamic css file in head() method:
export default {
head () {
const style = this.$cookie.get('app_style')
return {
link: [
{ rel: 'stylesheet', href: ~/assets/${style}.css }
]
}
}
}
To load dynamic images in the asset folder you can use require to tell webpack which image to load like so. In your template you can do:
<img :src="require(`../assets/img/${folderType}/bg1.png`)" />
Note: assuming folderType is defined in your script section
You can extract the require statement into a computed property(would make your template cleaner). So like so:
export default {
// ...
data() {
return {
folderType: 'folderTypeGoesHere'
}
},
computed: {
imagePath () {
return require(`../assets/img/${this.folderType}/bg1.png`) // the module request
}
}
}
Then in your template just use:
<img :src="imagePath" />
For your second problem, you could resolve to not using the tilde ~ alias and just use a relative path as I did in the image path example above.
It depends on your Nuxt version and also the bundler.
In Nuxt2 address starts with ~\assets\ however in Nuxt3 it starts with \assets\ .
Consider some png images in assets directory and the name of one of them is in img variable which can be changed dynamically.
If you are using Nuxt2 and Webpack, you need to use require like the following.
<img :src="require(`~/assets/${img}.png`)">
If you are using Nuxt3 and Vite
<img :src="`/assets/${img}.png`"/>
I have a form in my Vue component which uploads the api file. Now I want to render the contents of the file like this:
I have imported swagger client library: https://github.com/swagger-api/swagger-ui.
Now, here
is an example of how you do it in a static page. But I need to do it inside a Vue component (or Quasar, specifically), so I do it like that:
Register swagger-ui inside my register components file:
<link rel="stylesheet" type="text/css" href="swagger-ui.css">
Now it is available as:
this.swaggerUI({})
anywhere in my components. Inside my component I have a div in a template to render the api file:
<template>
<q-form>here lies q-file element, submit button and other stuff</q-form>
<div id="swagger-ui"></div>
</template>
In the mentioned question he had something like:
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "https://yourserver.com/path/to/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
]
})
window.ui = ui
}
</script>
Here's the difference: first of all, no window.onload, I must render it on submit button. Then, I deal with an uploaded file stored in my model, so no URL here. Now, I don't get how to make it work with locally stored file, when I try with the remote url, it gives me:
vue.esm.js?a026:628 [Vue warn]: Error in v-on handler: "Invariant Violation: _registerComponent(...): Target container is not a DOM element."
I was getting a similar error (Target container is not a DOM element) trying to use a static swagger spec. Instead of using window.onload, I found that Vue has the mounted() function, so this Vue 3 file worked for me:
<template>
<div class="swagger" id="swagger"></div>
</template>
<script>
import SwaggerUI from 'swagger-ui';
import 'swagger-ui/dist/swagger-ui.css';
export default {
name: "Swagger",
mounted() {
const spec = require('../path/to/my/spec.json');
SwaggerUI({
spec: spec,
dom_id: '#swagger'
})
}
}
</script>
This one appeared to be a simple yet very unobvious typo: in windows.onload function:
dom_id: '#swagger-ui',
must instead be
dom_id: 'swagger-ui',
without hash sign, that's it!
I try to test custom widget from js reference and I get error in debugger:
Error: QWeb2: Template 'some.template' not found
qweb.xml was properly set in manifest, because when I extend ListController and use another template, it works correctly.
Here is template definition, which I use in qweb.xml:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<div t-name="some.template">
<span class="val"><t t-esc="widget.count"/></span>
<button>Increment</button>
</div>
</template>
I tried to change <template> -> <templates>, totally removed tag "template" but still get the same error message.
JS:
odoo.define('working.test', function (require) {
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'some.template',
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = value;
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
// Create the instance
var counter = new Counter(this, 4);
// Render and insert into DOM
counter.appendTo(".o_nocontent_help");
})
Manifest:
# -*- coding: utf-8 -*-
{
'name': "testwidget",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
'qweb': ['static/qweb.xml'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'views/views.xml',
'views/web_asset.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}
Any idea how I need to modify this template to make the widget working correctly and in which table in db odoo stores these templates?
I was running into this same issue and needed to put my QWeb code into static/src/xml/base.xml in order for Odoo to recognize it.
You can check to see if Odoo is loading the QWeb by going to this URL on your Odoo instance:
<odoo_instance>/web/webclient/qweb?mods=<my_module_name>
Such as:
localhost:8069/web/webclient/qweb?mods=test
For comparison, you can see a successful output by using mods=web to load the QWeb assets for the web module.
You can try changing
'qweb': ['static/qweb.xml'],
to
'qweb': ['static/*.xml'],
It happens with me sometimes, by specifying static xml file name, it does not render that template. But by just loading all .xml files by using *, templates are loaded.
To solve this issue I used as workaround Widget.xmlDependencies:
xmlDependencies: ['/test/static/qweb.xml']
but the main reason I think was cache in PyCharm which I didn't invalidate.
After having done some code reading, IMO, I realized the official documentation might not have pointed out clearly how to use templates in frontend.
To summarize my understanding:
The 'qweb' field in manifest is mainly designed for webclient (i.e. the backoffice), not the website. When entering webclient, a request to /web/webclient/qweb is made to retrieve all the templates of installed modules.
In order to use templates in website (i.e. frontend), synchronous and asynchronous ways both exist.
Synchronous way: Use qweb.add_template. When parameter is template content itself or a DOM node, template is loaded in a synchronous way. (While param is a URL, then it fires up an ajax request to server to fetch content.)
qweb.add_template is mentioned in https://www.odoo.com/documentation/13.0/reference/qweb.html
Asynchronous way:
Use ajax.loadXML which you can use anywhere you want to start loading template from a URL.
Use xmlDependencies which you specify in widget definition. And if you dig into the code in widget.js, you can see ajax.loadXML is being used in willStart.
There are discussions regarding qweb.add_template vs ajax.loadXML
See https://github.com/OCA/pylint-odoo/issues/186 and https://github.com/odoo/odoo/issues/20821
FYI.
I guess you may need to make sure that the js definition refers to the module name correctly
odoo.define('MODULE TECHNICAL NAME SHOULD BE HERE.test', function (require) {});
you should also register your js function with something like:
core.action_registry.add("module_name.name", Widget_Extend);
for more info https://www.odoo.com/documentation/11.0/reference/javascript_reference.html#registries
In Odoo 14 make sure
dashboard.js
odoo.define('library_managment.dashboard', function(require) {
"use strict";
// alert("hello odoo...............")
console.log("Hello My Module........!!")
var widgetRegistry = require('web.widget_registry');
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'library_managment.template',
xmlDependencies: ['/library_managment/static/src/xml/template.xml'],
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = 4*9+5;
console.log("parent is", parent)
console.log("counter is..", this.count)
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
widgetRegistry.add('library_counter', Counter);
return Counter;
});
template.xml
add this
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<div t-name="library_managment.template">
<span class="val">
<t t-esc="widget.count"/>
</span>
<button class="bg-danger">Increment</button>
</div>
</odoo>
then add js file in assets.xml inside youe views
<odoo>
<template id="assets_backend" name="Library assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/library_managment/static/src/js/dashboard.js"></script>
</xpath>
</template>
</odoo>
then add in manifest like this:
'js': ['/static/src/js/dashboard.js'],
'qweb': ['/static/src/xml/template.xml']
then inside form view add this line
<widget="library_counter"/>
I had the same problem but with "hr_org_chart" template idk why everything works fine in another computer but in mine it returned this problem, I solved it by installing this module hr-org-chart
I was trying to use to embed the svg picture but it does not show anything. I looked at some other threads and it was suggested to add type="image/svg+xml", however, it did not solve the issue. When I am trying to look at the DOM for some reason it seems to create an endless loop. I attached the picture
This is the compononent
<template>
<div class="logo">
<object type="image/svg+xml" data="logo.svg">
</object>
</div>
</template>
This is the app.vue
template>
<div id="app">
<Demo></Demo>
</div>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'app',
components: {
Demo
}
}
</script>
```[![Snapshot][1]][1]
[1]: https://i.stack.imgur.com/Q6ipO.png
This happen because vue-loader doesn’t recognize paths in just any attribute. By default just recognize these ones: https://vue-loader.vuejs.org/options.html#transformasseturls
So, there are 3 possible solutions
Note: If you are not using eslint as linter you could remove eslint comments
1: Bind the route to your image
First add the next variable to your data in the component
data() {
return {
// eslint-disable-next-line global-require
mySvg: require('../assets/logo.svg'),
};
},
Next modify your template
<object type="image/svg+xml" :data="mySvg">
2: Add vue-loader rule
If you don't want to have to bind every svg image, you could add a rule to vue-loader in order to say how to handle data attribute in a object
Go to your webpack config file, if you created the project using vue-cli 3.x you have to create a vue.config.js file in the root (same level that package.json)
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap((options) => {
// eslint-disable-next-line no-param-reassign
options.transformAssetUrls = {
object: 'data',
};
return options;
});
},
};
if you want to check that the config was added, execute vue inspect > webpack.config and expect see something like this (inside webpack.config):
{
loader: 'vue-loader',
options: {
...
transformAssetUrls: {
object: 'data'
}
}
}
More info: https://cli.vuejs.org/guide/webpack.html#working-with-webpack
3: Replace default loader and use svg as vue components
Other option is use vue-svg-loader. This loader inlines the SVGs which enables you to modify them using css. Also optimize your files with SVGO
See more: https://vue-svg-loader.js.org/#vue-cli
It is worth checking that you don't have a CSS rule hiding object tags. Otherwise it seems correct. You probably need to check the path and make sure you can reach your image. I assume your filename is a dummy, but try to use an absolute path. And make sure you can hit the path and see the image in your browser.