Read all element bindings from a Vue template - vue.js

I'm trying to pre-process a Vue 2 template and get a list of all of the element bindings. So if I have a file like this:
<html>
<body>
<div id="app">
<p>Here's a message: {{message1}}</p>
<p>Here's an input: <input type="text" v-model="message2"></p>
</div>
<script type="application/javascript" src="vue.js"></script>
<script type="application/javascript">
new Vue({
el: "#app",
data: {
message1: "foo",
message2: "bar"
}
});
</script>
</body>
</html>
Then somewhere (beforeMount?) I could query Vue and it would tell me the bindings are ['message1', 'message2']. Is that possible?

I ended up solving this by getting the text of the render function (by calling vm.$options.render.toString() ) and then parsing the bindings from that.
For instance, the rendering function for a simple list view looks like this:
function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c(
"table",
{ attrs: { border: "1", cellpadding: "5", cellspacing: "0" } },
[
_vm._m(0),
_vm._l(_vm.rows, function(row) {
return _c("tr", [
_c(
"td",
[
_c("router-link", { attrs: { to: "/detail/" + row.ID } }, [
_vm._v(_vm._s(_vm._f("truncate")(row.TITLE, 100)))
])
],
1
),
_c("td", [_vm._v(_vm._s(_vm._f("truncate")(row.DESCRIPTION, 200)))]),
_c("td", [_vm._v(_vm._s(row.TYPE))])
])
})
],
2
)
}
It looks like the bindings are always contained in an _s() element, and optionally a vm.f() instruction when using filters.

Related

Search Turkish Character Problem on VueJs Bootstrap Vue Table

I have a problem. I used bootstrap vue table. And I have a search box. I have a yield as "Istanbul". It doesn't see it when I press i in lower case. It accepts a capital letter I. I tried toLocaleLowerCase() but didn't run.
I type "istanbul" in the search box, but it does not find it in the table. It finds it when you write it as "İstanbul".
This is my template and dataset:
<template>
<div>
<b-table striped hover :fields="fields" :items="cities"></b-table>
</div>
</template>
<script>
export default {
data() {
return {
cities : [
{key:1,city:'İstanbul'},
{key:2,city:'İzmir'},
{key:3,city:'Adana'},
],
cityCopyArray : [
{key:1,city:'İstanbul'},
{key:2,city:'İzmir'},
{key:3,city:'Adana'},
],
fields:["city"]
}
}
</script>
This is my input:
<input
:placeholder="'City Name"
:id="'cityNamr'"
v-model="citySearchSearch"></input>
This is my watch:
citySearchSearch: {
handler(val) {
this.cities = this.cityCopyArray.filter((city) => {
return this.converter(city.name).includes(this.converter(val))
})t
},
},
And I used this code as converter :
converter(text){
var trMap = {
'çÇ':'c',
'ğĞ':'g',
'şŞ':'s',
'üÜ':'u',
'ıİ':'i',
'öÖ':'o',
};
for(var key in trMap) {
text = text.replace(new RegExp('['+key+']','g'), trMap[key]);
}
return text.replace(/[^-a-zA-Z0-9\s]+/ig, '')
.replace(/\s/gi, "-")
.replace(/[-]+/gi, "-")
.toLowerCase();
},
You can compare Turkish characters using toLocaleUpperCase('tr-TR') like:
const firstWord = 'istanbul';
const secondWord = 'İstanbul';
// If firstWord contains secondWord, firstWordContainsSecondWord will be true otherwise false.
const firstWordContainsSecondWord = firstWord.toLocaleUpperCase('tr-TR').indexOf(secondWord.toLocaleUpperCase('tr-TR')) !== -1;
Simple example:
new Vue({
el: '#app',
data: {
firstWord: 'istanbul',
secondWord: 'İstanbul',
result: null,
},
watch: {
firstWord() {
this.contains();
},
secondWord() {
this.contains();
}
},
mounted() {
this.contains();
},
methods: {
contains() {
// If firstWord contains secondWord, result will be true otherwise false.
this.result = this.firstWord.toLocaleUpperCase('tr-TR').indexOf(this.secondWord.toLocaleUpperCase('tr-TR')) !== -1;
}
}
});
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input placeholder="firstWord" v-model="firstWord">
<input placeholder="secondWord" v-model="secondWord">
<br/><br/>
<div>
Result =>
<br/> {{ firstWord }} contains {{ secondWord }} : {{ result }}
</div>
</div>

vuejs target all anchor tag onclick

I want to call a method when any anchor tag clicked. It is not possible to define #click or v-on on all element.
<a href="#">link1<a>
<a href="#">link2<a>
<a href="#">link3<a>
<a href="#">link4<a>
<a href="#">link5<a>
on jquery I can do this
$('a').click(function(){
//........
});
How can I achieve it in vuejs
Note: I have 10+ components in my app & a tag is spread every where in components.
You could attach the click handler on the parent/container element, and detect the target/child element being clicked via the event.target:
new Vue({
el: '#app',
methods: {
clicked(e) {
const container = e.currentTarget; // FYI
const anchor = e.target;
console.log(`${anchor.innerHTML} was clicked!`);
}
}
})
Vue.config.devtools = false;
Vue.config.productionTip = false;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="container" #click="clicked">
Link #1
Link #2
Link #3
<!-- more anchor elements here -->
</div>
</div>
This doesn't seem like a great idea, BUT you can "watch" changes. In this case to the collection of links and then attach an event handler...
Note: I didn't test this, but it should get you in the right direction
var vm = new Vue({
el: '#example',
data: {
linkList
},
// update the items in linkList when the DOM is ready
mounted(){
this.linkList = document.getElementsByTagName('a');
},
// watch for changes to linkList and do something
watch: {
linkList: function(){
for( var i=0; i < linkList.length; i ++ ){
LinkList[i].onclick = linkAction;
}
}
},
methods: {
linkAction: function (event) {
alert(event.target.tagName)
}
},
})
Edit: After reading the comments regarding the goal, you should let vue-router handle transitions between sections. I assume you don't want to edit each component so look into Dynamic Transitions inside the router.
In this case, you would "watch" for changes to the $route data and do your action there:
watch: {
'$route' (to, from) {
// to & from are data in the route object telling you which components are being shown
const toPath = to.path;
function(){
// do some action
}
}
}
This would function regardless of the tag used to trigger the route...
Well, you can achieve it by defining the links under data and loop through the v-for directive
First Way
new Vue({
el: "#app",
data: {
links: [
{ link: "first Link" },
{ link: "second Link" },
{ link: "Third Link" },
{ link: "Fourth Link" }
]
},
methods: {
onAnchor: function(e){
console.log("Clicked on", e.target.innerHTML);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<p v-for="link in links">
{{link.link}}
</p>
</div>
Second way
Create a wrapper component for a and use the same throughout the application.
Vue.component('An', {
template: '#anchor',
props: ['link']
})
new Vue({
el: "#app",
data: function() {
return {
links: [
{ link: "first Link", text:'first Link' },
{ link: "second Link", text:'second Link' },
{ link: "Third Link", text:'third Link' },
{ link: "Fourth Link", text:'Fourth Link' }
]
}
},
methods: {
changeText(event) {
console.log(event)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Anchor</h2>
<p v-for="link in links">
<An :link="link" v-on:on-click="changeText($event)"></An>
</p>
</div>
<script type="text/x-template" id="anchor">
<p>
{{link.link}}
</p>
</script>
Hope this helps!

Vuejs component doesn't update when changing variable

I'm learning Vue.js and have been able to write a simple list/detail application. Selecting the first item renders the detail component with the correct data, however when I select a different item the detail component doesn't reload with the right information.
For example:
<template>
<div>
<detail :obj="currentObject"></detail>
</div>
</template>
<script>
export default: {
data: function(){
return {
currentObject: null,
objs = [
{name:'obj 1'},
{name:'obj 2'}
]
};
}
}
</script>
When I do this.currentObject = objs[0] the component detail updates with the correct content. However the next time I call this.currentObject = objs[1], the component detail no longer updates.
Not sure what's the context your are switching the data on your currentObject, but the below is a concept detail component and when you switch the objs it updated the prop :obj and seems working fine.
Looking at your code, you should declare objs using : not =.
data: function() {
return {
currentObject: null,
objs: [
{name:'obj 1'},
{name:'obj 2'}
]
};
}
Here is the concept detail component, run the snippet to check it working.
Vue.component('detail', {
props: ['obj'],
template: '<div>{{ obj }}</div>'
})
var app = new Vue({
el: '#app',
data() {
return {
bToggle: false,
currentObject: null,
objs: [
{name:'obj 1'},
{name:'obj 2'}
]
}
},
created(){
this.switchData();
},
methods: {
switchData() {
if(!this.bToggle){
this.currentObject = this.objs[0];
}else{
this.currentObject = this.objs[1];
}
this.bToggle = !this.bToggle;
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<button #click="switchData()"> switch </button>
<detail :obj="currentObject"></detail>
</div>

Vue v-for list does not update after data changed

The idea is simple:
The Vue instance loads groups from an API.
This groups are shown using a v-for syntax.
The groupdata is formatted properly, and added to app.groups using .push function, which should update the v-for list.
However, the v-for list will not update.
When I change v-for="group in groups" (loaded externally) to v-for="group in people" (already in the app), it does give output.
HTML
<div id="app" class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Realtime Socket Chat</div>
<div class="panel-body" v-if="dataReady" v-for="group in groups"> #{{ group.name }}
<a v-bind:href="group.link" class="pull-right">
<div class="btn btn-xs btn-primary">
Join
</div>
</a>
</div>
</div>
</div>
</div>
Vue
var app = new Vue({
el: "#app",
data: {
groups: [],
people: [{name: 'hi'}, {name: 'lol'}]
},
mounted: function() {
this.load()
},
methods: {
load: function() {
axios.get('http://example.com/groups')
.then(function (response) {
console.log(response.data.groups);
response.data.groups.forEach(function(group) {
app.groups.push(group);
});
// app.groups.forEach(function (group) {
// group.link = '/g/' + group.id + '/join';
// });
// console.log(response.data.groups);
console.log(this.groups); //outputs [{...}, {...}] so this.groups is good
})
.catch(function (error) {
this.errors.push(error);
console.log(error);
})
}
}
});
API
{
"groups": [
{
"id": 1,
"name": "Photography Chat",
"created_at": "2017-11-26 08:50:16",
"updated_at": "2017-11-26 08:50:16"
},
{
"id": 2,
"name": "Media Chat",
"created_at": "2017-11-26 08:50:16",
"updated_at": "2017-11-26 08:50:16"
}
]
}
It seems like app is undefined when your load function is executed. So, using ES6 arrow function syntax, your load function should look like this:
load: function() {
axios.get('http://example.com/groups')
.then(response => {
let groups = response.data.groups || []
groups.forEach(group => {
this.groups.push(group)
})
console.log(this.groups)
})
.catch(error => {
this.errors.push(error)
console.log(error)
})
}
A fact I left out in the question (because I assumed it would not matter) was that I am working within the Laravel framework, Laravel automatically imports Vuejs within main.js. Which does not play nice when Vue is loaded in additionally. I removed main.js and it works.

How to handle getting new data even?

I have got Vue-JS app. After use click variable rasters_previews_list get new data. I would like to generate list with them. I can't understand which directive I should use to handle this even.
Here is my code:
var userContent = Vue.extend({
template: `
<div class="LayersMenuSectionContent" v-if="rasters_previews_list.length">
<ul v-for="img in rasters_previews_list">
<li>{{img.id}}</li> // generate list here
<ul>
</div>
`,
data: function () {
return {
rasters_previews_list: [] // when come new data I should generate li with then
}
},
ready: function()
{
},
methods:
{
}
});
Should I use v-on or v-if?
When rasters_previews_list is changed, list is autorendered in v-for.
new Vue({
el: '#app',
template: `
<div class="LayersMenuSectionContent" v-show="rasters_previews_list.length">
<ul v-for="img in rasters_previews_list">
<li>{{img.id}}</li>
<ul>
</div>
<button #click="add">Add</button>
`,
data: function () {
return {
rasters_previews_list: [] // when come new data I should generate li with then
}
},
ready: function(){ },
methods: {
add(){
this.rasters_previews_list.push({id: 'hello'},{id: 'world'});
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app"></div>
Extra: You must use v-show instead of v-if for this case