I am really struggling with the following scenario:
Some index page:
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div id="app">
<ul>
<li>some existing option</li>
<example-component :foo="foo" :bar="bar"/>
</ul>
<a #click.prevent="showDetail(1, 'abc')" href="#" >Click ME!</a>
</div>
<script src="app.js"></script>
</body>
</html>
Some single file component:
<template>
<li><a v-show="checkBool" data-toggle="tab" class="some-class" href="#container-div" data-tab-url="{{ this.foo }}">{{ this.bar }}</a></li>
</template>
<script>
export default {
props: ['foo', 'bar'],
computed: {
checkBool: function() {
return (this.foo != undefined && this.bar != undefined )
}
}
}
</script>
And the app.js looks something like this:
import Vue from 'vue'
Vue.component('example-component', require('ExampleComponent.vue'));
const app = new Vue({
el: '#app',
props: [
'foo',
'bar'
],
data: {
foo: '',
bar: ''
},
methods: {
showDetail: function (foo, bar) {
this.foo = foo,
this.bar = bar
}
}
});
I am using babel with webpack under a laravel instalation.
The scenario is like this:
When I click the Click ME! link, foo and bar are updated and passed to the component (This part is working)
The computed property, named checkBool for this example becomes true, so I will then see the new list item (This part is working)
New list item, has a link, with the text correctly set to bar (This part is also working)
At this point I know that the values setting and communication between component and parent is working properly, however data-tab-url="{{ this.foo }}" part is driving me crazy.
Instead of parsing the moustache syntax as expected and return data-tab-url="1", I get a parsed/escaped value of everything between quotes.
Something like data-tab-url="%7B%7B+this.foo+%7D%7D".
Now, how do I prevent the encode from happening?
From what I've read, there used to be a way in vuejs 1.*. Using three brackets: {{{ this.foo }}} but it's now deprecated in favor of v-html which I cannot use for my example.
Bind the attribute like this :data-tab-url="foo".
This will give the affected element a data-tab-url attribute with a value equal to the foo property of your component.
thanksd's answer is right but;
for further understanding:
You can't use mustache syntax for attribute binding. Use mustache {{}} only content of a dom element, ie.
<div>{{someValue}}</div> (THIS IS WRONG)
To bind any attribute, including template props any other attribute, such as "src" or "data-tab-url" like in question, you can use "v-bind:attr" or ":attr" shorthand, ie.
<div v-bind:src="someDataVariable"></div>
or
<div v-bind:some-prop="someMethod()"></div>
You can use any member(data, method, computed etc.) of your component or Vue app, without "this".
To render any component instance property (prop, data, computed ...) inside html you've to :
Bind it to an attribute using v-bind: or the shorthand : like :foo="someFoo"
Use it inside mustache syntax {{someFoo}}
Use it as directive value v-show="isShown" or v-model="username", directives are always prefixed by v-
For the events, they are written like v-on:eventName or #eventName which could run an inline statement #click="count++" or a method #click="increment" knowing that increment is a function defined inside the methods options
Related
So after following a beginner Vue tutorial to setup a Todo app, I decided to try to adapt some parts of it for a website I'm trying to make. What I'm stuck on is that despite everything saying my for-loop is supposed to work, it doesn't.
The project itself was created using the vue-cli, and most of the code copy-pasted from the tutorial. (which is working fine with its own for-loop)
It seems like the data might be not passed onto the template maybe?
I have tried:
having the info inside the props and data sections
passing whole object and only parameters to the template
tried with hard-coded values inside array which is iterated on
(After setting up a new vue-cli project:)
App.vue:
<template>
<div id="app">
<create-section v-on:create-section="addSection" />
<section v-for="section in sections" v-bind:key="section.title" :info="section"></section>
</div>
</template>
<script>
import CreateSection from "./components/CreateSection";
import Section from "./components/Section";
export default {
name: "App",
components: {
CreateSection,
Section
},
data() {
return {
sections: []
};
},
methods: {
addSection(section) {
this.sections.push({
title: section.title,
description: section.description
});
console.log(
"Added to sections! : " + section.title + " | " + section.description
);
console.log("Sections length: " + this.sections.length);
}
}
};
</script>
Section.vue
<template>
<div class="ui centered card">
<div class="content">
<div class="header">{{ info.title }}</div>
<div>{{ info.description }}</div>
</div>
</div>
</template>
<script type = "text/javascript" >
export default {
props: {info: Object},
data() {
return {};
}
};
</script>
Expected result:
Display Section template on the website (after creating it with addSection that another script calls. Not included for brevity)
Actual result:
Nothing is displayed, only a empty tag is added
I believe the problem is that you've called it Section. As <section> is a standard HTML element you can't use it as a component name.
There is a warning built into the library but it seems to be case sensitive, which isn't entirely helpful. Try changing your components section to this:
components: {
CreateSection,
section: Section
},
You should then see the warning.
The fix would just be to call it something else.
This is mentioned in the first entry in the style guide:
https://v2.vuejs.org/v2/style-guide/#Multi-word-component-names-essential
section is an existing HTML5 element, you should name your section component something different.
If you really want to name the component Section, register it as 'v-section'
The problem is that when you do the loop in the <section v-for="section in sections" v-bind:key="section.title" :info="section"></section> the Array sections is not ready, there is nothing there.. so when you add new things to this array you need to trigger (computed prop) to send again the data to the section component.
Aside from the issue with using an existing HTML5 command as a name for your Vue component (you should change that to another name by the way), you should also look into how you declared the props within Section.vue. The code below shows the correct way to do it:
<script type = "text/javascript" >
export default {
props: ['info'],
data() {
return {};
}
};
</script>
The props take in the name of the property being declared from the parent component and should be a string.
Hope this helps.
I am using laravel + vue. I want to do title of page in navbar, so when you are in index, in navbar is text index. When you are in settings, navbar says settings etc.
I think props is good for it, but when I use that, it works not good. Look here:
blade.php of index:
#extends('layout.app')
#section('content')
<index :msg="msg"></index>
#endsection
index.vue:
props: [
'msg'
],
Now navbar:
<template>
<nav class="navbar">
<a></a>
<p>{{msg}}</p>
</nav>
</template>
<script>
export default {
props: [
],
data() {
return {
}
},
}
</script>
and layout:
<body>
<div id="app">
<navbar></navbar>
#yield('content')
</div>
</body>
How I can change that {{msg}} paragraph in navbar when we are on different pages? My code doesn't work.
[Vue warn]: Property or method "msg" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
If you want to use a prop then you need to define it in the props object of your component. In your NavBar you are referencing to msg, but the props object in NavBar is empty. Define it and pass the prop in your layout.blade.php.
Or you could also define a computed property where you take a look at the current route and return a string fitting your business.
https://v2.vuejs.org/v2/guide/computed.html
If you want to share data between multiple components, then use a store (VUEX) as proposed :)
I'm not very new to Vue.js which is probably why I feel like I've been running mad all morning :). While creating a component, which I usually do, quite frequently, in this case, I had to initialize Google Maps within the mounted function, which seems like the right place to do that. In the mounted function, I would access the id property of a nested input field and attach an event listener to it. Pretty simple right?
Well, I figured that when I try to use the component multiple times on my page, I'm somehow accessing the same (seemingly shared) this variable within the mounted function.
Not sure why exactly this happens and/or if it's a feature but to make it even weirder, the props yield correct values within the template. (and within the methods as well)
Component Definition
<template>
<div class="LocationInput">
<input
type="text"
:id="id"
/>
</div>
</template>
<script>
export default {
name: 'LocationInput',
props: ['id'],
mounted() {
console.log('Component object representation ==> ', this)
console.log('ID ==> ', this.id)
}
}
</script>
Using my component...
<template>
<div class="MyTravelApp">
<LocationInput id="id1"/>
<LocationInput id="id2"/>
</div>
</template>
<script>
import LocationInput from './components/LocationInput';
export default {
components: { LocationInput }
}
</script>
What I get at the end of the day is the correct id values in the template but in my console, the exact same object and id are logged as you can see below. Notice how the _uid property is the same thing for both.
To make matters even worse, after modifying the this variable in the mounted function, while inspecting, I observed that the second component has that property modified as well. So they are essentially sharing the same object, which is extremely weird.
I would like to know if anyone has had similar issues and how to deal with it.
No self-closing tags for components.
Vue templates need to be valid HTML. There are no "self closing tags"
in HTML5, it's an XHTML syntax which is now outdated and you should
never use it.
(Later note:)
FYI self-closing tags works in 2.0 as long as you don't use in-dom
templates.
You may also be having an issue with camelCase vs. kebab-case. The snippet below behaves as expected.
Vue.component('locationInput', {
template: '#location-input-template',
props: ['id'],
mounted() {
console.log('Component object representation ==> ', this._uid)
console.log('ID ==> ', this.id)
}
});
new Vue({
el: '#my-travel-app'
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<template id="location-input-template">
<div class="LocationInput">
<input type="text" :id="id">
</div>
</template>
<div id="my-travel-app">
<location-input id="id1"></location-input>
<location-input id="id2"></location-input>
</div>
I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>
Update
Apparently when using <template> the reading of innerHTML will return all attributes in lower case. Angular2 will not understand ngfor or ngif as of this beta 9 version and throws error. <script> is treated as a text fragment rather than DOM, which means attributes stay as they are.
Here:
https://groups.google.com/forum/#!topic/angular/yz-XdYV2vYw
Originial
Taking the following html and angular2 beta 9 component:
HTML CODE
<my-page>Loading...</my-page>
<script type="text/html" id="my-component-template1">
<select [(ngModel)]="SelectedType">
<option *ngFor="#someType of MyTypes" [selected]="SelectedType == someType" [value]="someType">{{someType}}</option>
</select>
</script>
<template id="my-component-template2">
<select [(ngModel)]="SelectedType">
<option *ngFor="#someType of MyTypes" [selected]="SelectedType == someType" [value]="someType">{{someType}}</option>
</select>
</template>
JS CODE
var myComponent =
ng.core.Component({
selector: 'my-page',
//complains if i use #my-component-template2
template: document.querySelector('#my-component-template1').innerHTML
})
.Class({
constructor: function () {
var self = this;
self.MyTypes = ['first', 'second'];
self.SelectedType = self.MyTypes[0];
}
});
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(myComponent);
});
If i use my-component-template1 it works fine, but if i choose my-component-template2 it complains that ngModel and ngForOf are not a known native properties.
I tested a div as a template and apparently that won't work either with the same errors. So question is, why is it breaking if the template is part of the DOM? Furthermore, I really don't want to use the script text/html hack. Assuming this is why <template> was added in html5 specification. Why is this happening and how can i fix it?
The <template> tag is only used by Angular 2 structural directives (like the built-in ngIf, ngFor, ngSwitch, etc) - its use is somehow similar with the html5 specification since it defines content which is stored for subsequent use.
The * in front of a structural directive is just syntactic sugar which allows us to skip the <template> tag and focus directly on the HTML element that we are including, excluding, or repeating - you can read more about it here.
An example from Angular 2 docs which showcases this:
<!-- Examples (A) and (B) are the same -->
<!-- (A) *ngIf paragraph -->
<p *ngIf="condition">
Our heroes are true!
</p>
<!-- (B) [ngIf] with template -->
<template [ngIf]="condition">
<p>
Our heroes are true!
</p>
</template>
At the moment, I'm not sure if there's a way of defining inline Angular 2 HTML templates like there's in AngularJS 1. Your hack, as you put it, seems to do its job.
Angular handles <template> tags itself and doesn't simply add them to the DOM. If you inject TemplateRef into the constructor of your component you should get a reference to the template.
class MyComponent {
constructor(private tmplRef:TemplateRef) {
}
}