Use global functions in vue directives - vue.js

I'm trying to use lodash methods (_.isEmpty) in vue directives like this:
<div class="post" v-for="post in posts"></div>
...
<div class="comments" v-if="! _.isEmpty(post.comments)">
<div class="comment" v-for="comment in post.comments"></div>
</div>
...
</div>
but getting the following error:
Uncaught TypeError: Cannot read property 'isEmpty' of undefined
It seems vue is looking for the _.isEmpty method inside the current scope. How should I call global functions in this case?

You can only access functions of the current Vue instance/component in a template:
data
props
methods
No "third-party" code can be run.
So, you would have to create a method in the Vue component to proxy to the lodash methods:
methods: {
isEmpty: function (arr) { return _.isEmpty(arr)}
}
and use this method in the template instead:
<div class="comments" v-if="! isEmpty(post.comments)">

Why not just add _ to your Vue component:
data(){
return {
_:require('lodash') //or however you include it. maybe just window._
}
}
Then it would be accessible. Not positive if _ is a valid object key, so might just call it lo or lodash if needed.
Also, assuming that comments is an array, there would be no problem using v-if='post.comments.length'. Lo-dash is great but unnecessary if you already know it's an array.

Related

how to pass a function as parameter in a vue directive

in react and jsx I would use curly braces to pass nested functions and their params, as parameters. but since there is this quote syntax in vue js, I am confused.
what I'm trying to do is something like this:
<span #click="myFirstFunction( ()=> console.log('param') )" />
myFirstFunction(code){
code();
}
actually, this is the simplified version
what I'm trying to do is to save that code in an state and run it in other situations
but this doesn't seem to work.
Actually, your syntax is correct. But in your simple example, you are trying to access a property or a data which is not defined. You may think console is a global object it should be accessed from anywhere, but no. You can only access component properties (data, methods, etc.) from the template in VUE. So you should do something like below to access the console or any other global object:
data() {
return {
console: Object,
};
},
created() {
this.console = console;
},
SandBox
You should do either
<span #click="() => console.log('param')" />
or
<span #click="myFirstFunction" />
myFirstFunction should be defined as a method.
methods: {
myFirstFunction() {
// your function
}
}

Pass string variable from Django to Vuejs method

I'm passing an parameter from Django to a Vue method in my html as such:
# Django view
context = {
'var1': var1,
}
<!-- html -->
<div>
<p>Display var1: {{var1}}</p>
<button #click.prevent="doThis({{var1}})">Do This</button>
</div>
<!-- html result below
Display var1: value_of_var1
-->
The above works perfectly however, the doThis({{var1}}) gives an error.
// Vue method
method: {
doThis(var1){
console.log(var1)
}
}
I get the following in the console:
undefined
...[Vue warn]: Property or method "value_of_var1" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
What am I doing wrong?
How can I get the above error when I'm passing in a variable to the method?
My first answer above works so I'm leaving it. It is however not optimal for the purpose I wanted. Estradiaz comment is really what I needed. I was missing the quotes '' in my code. This is required when passing strings into methods. So the solution is:
<!-- html -->
<div>
<p>Display var1: {{var1}}</p>
<button #click.prevent="doThis('{{ var1 }}') ">Do This</button>
</div>
I couldn't figure out what is happening above but I found a way around it. I let var1 display in the html then added a style of display:none. I then used javascript to fetch the contents in my vuejs file:
<!-- html -->
<div>
<p id="get_var1" style="display: none;">{{var1}}</p>
<button #click.prevent="doThis()">Do This</button>
</div>
// Vue method
method: {
doThis(){
let var1 = document.getElementById("get_var1").textContent
console.log(var1)
}
}
// Console:
// Value_of_var1
Note that the only reason this is ok is that this isn't sensitive or secret information.

How to call a JavaScript function from a Vue.js v-bind

I want to call a plain JavaScript function from inside a Vue.js v-bind attribute. I can do it by channeling it through a Vue.js data variable, but calling a plain JavaScript function directly from v-bind produces an error.
http://jsfiddle.net/edwardtanguay/2st0fruh
How can I call a JavaScript function directly without having to map to a variable or method in Vue.js?
HTML
<div id="app">
<div>
The URL is: {{url}}
</div>
<div>
<a target="_blank" v-bind:href="url">goto URL</a>
</div>
<div>
<a target="_blank" v-bind:href="url2">goto URL2</a>
</div>
<div>
<a target="_blank" v-bind:href="getUrl()">goto URL2 using javascript function</a>
</div>
</div>
JavaScript
function getUrl() {
return 'http://www.amazon.com';
}
var vm = new Vue({
el: '#app',
data: {
url: 'http://www.google.com',
url2: getUrl()
}
});
In order for Vue to be able to bind itself, it needs to be part of the Vue instance. Whilst your function might be accessible under normal circumstances, it's not part of the vue instance and in turn can't be accessed.
As you have already stated, using a data prop moves it into scope of the vue instance. You could also use a computed prop for this if the value is likely to mutate.
As per this post from the vue author(Evan You) regarding using global functions:
Because implicitly falling back to globals will be a maintainability nightmare. When you look at a template you will have no idea whether a variable belongs to the component, or some globals defined by someone you have no idea where.
It's worth noting that if you feel this is something you want to do a lot, you could have a global mixin and specify some methods on there to get the Urls.
There is a way to do this but I don't recommend it:
See this fiddle
Basically, you add your own function to the Vue.prototype (this is how things like Vue Router and Vuex work):
Vue.prototype.$myFunc = function getUrl() {
return 'http://www.amazon.com';
}
Make sure you declare it before you create your var vm = new Vue() call.
Then you can access it via $myFunc() from the template or this.$myFunc() from the vue instance itself.

When v-for array created by computed option changs, the DOM doesn't change accordingly

Recently, I've encountered a problem that caused by the computed option of vuejs.
Firstly, I use v-for to loop for an array (soloColImgs) which is created by the computed option.
my HTML
<div class="show-box" v-for="item in soloColImgs" track-by="$index">
<img v-bind:src="item.imgUrl"/>
<a v-bind:href="item.itemUrl" target="_blank"></a>
</div>
my JS
//...
computed: {
soloColImgs :function(){
//....
},
methods: {
change:function(){
this.soloColImgs.pop();
}
}
Secondly, I change the array (soloColImgs) by using pop() or splice() etc...When I look into the console, the array can change accordingly, however, the DOM does't change at all. It would be greatful if anyone can help me out of this.
The point of a computed property is that its determined solely by the function that defines it. You cannot change it directly, you must change it by acting on the dependencies. The dependencies are the properties that are used to calculate the returned value.

closure within v-for, attribute interpolation

I have this basic setup
<div v-for="n in 4">
<some-component #on-some-event="onSomeEvent(n)"></some-component>
</div>
the on-some-event is dispatched within some-component. but I need to know which of these components sent the message. with the setup above, only n is passed into the method. and the data that the event sends is nowhere.
I'd like to interpolate the function so that the method looks like this
onSomeEvent(n){
return (obj)=>{
console.log(`component ${n} sent ${obj}`);
};
}
but wrapping onSomeEvent with {{}} throws a warning: attribute interpolation is not allowed in Vue.js directives and special attributes.
I could just pass the n index into the component but that seems less elegant because I may not have the ability to modify some-component
I am somewhat new to Vue, so perhaps I am missing some core functionality for this type of thing?
<div v-for="n in 4">
<some-component #on-some-event="onSomeEvent | pass n"></some-component>
</div>
....
filters: {
pass(handler, n) {
return function() {
handler()(n, ...arguments)
}
}
},
methods: {
onSomeEvent() {
console.log(...arguments)
}
}
https://jsfiddle.net/2s6hqcy5/2/
You didn't miss anything, the message is correct, in Vue, you won't be able to use interpolation like that.
http://vuejs.org/guide/syntax.html#Interpolations
However, you may want to change how you manage events and pass data between components. In your example, you can just bind the data like this:
<div v-for="n in 4">
<some-component :n="n"></some-component>
</div>
In your component, declare the prop:
Vue.component('some-component', {
props: ['n'],
...
Now, inside each component, you have the n available like any other property (http://vuejs.org/guide/components.html#Props).
Then when dispatching your event, you can call it like this, with no need for a param:
onSomeEvent()
On the event itself, you can access the n:
console.log('Component' + this.n + ...)
https://jsfiddle.net/crabbly/mjnjy1jt/