Pass string variable from Django to Vuejs method - vue.js

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.

Related

Getting element (button) data attribute with vue3

I have the following html div. The {{ }} represent liquid syntax and it renders server side.
<div class="two columns">
<button
data-value='{{ value }}'
class="button-selection"
:class="selectionButtonClass($event)"
#click="selectionButton($event)"
>
<span class="text">{{ value }}</span>
</button>
</div>
In a vue 3 instance I have the following method in the same page.
selectionButtonClass(el) {
console.log('checking val');
console.log(el);
}
My goal is to set a conditional class in the selectionButton method but I can't get the element to get the data attribute. The above appears in the console log as undefined. However the #click does show the event obviously it's recognize the onclick but not the class method check.
$event is only available to event handlers. #click="selectionButton($event)" defines an inline event handler, while :class="selectionButtonClass($event)" is not an event handler.
To get the element, you need to add a ref attribute to the <button>:
<button
ref="selectionButton"
data-value='{{ value }}'
class="button-selection"
:class="selectionButtonClass($event)"
#click="selectionButton($event)"
>
And access it by this.$refs.selectionButton, assuming you are using the options API. However, the ref is available only after the component is mounted. Thus you need to handle the case where the ref is null.
More on template refs: https://vuejs.org/guide/essentials/template-refs.html
Since you are using server side rendering, I think it would be better to render the value as a parameter of the selectionButton function on the server side.

Is there any way to have a comment inside a tag [duplicate]

Sometimes it is needed to comment out some element attribute without having to remember it in order to restore it quickly after some tests.
Commenting out whole element is achievable with HTML commenting syntax
<div>
<!-- <h2>Hello</h2> -->
<span>hi</span>
</div>
However this won't work with a single attribute (causes rendering error)
<my-comp id="my_comp_1"
v-model="value"
<!-- :disabled="!isValid" -->
#click="handleClick">
</my-comp>
The best approach I could see and used before was to make a tag backup by copying whole element and settings v-if="false" for it (or comment out whole copied element) and continue to experiment with original one
I don't think you can put an HTML comment inside a component tag, for much the same reason you can't put comments inside an HTML element opening tag. It's not valid markup in either situation. I think the closest you could come would be to place the comment in the quotes:
:disabled="// !isValid"
Which would have the same effect as:
:disabled=""
Depending on whether your component can handle that value being missing, that might fit your needs.
Prefix the attribute value with data- or Wrap with data attribute.
<my-comp id="my_comp_1"
v-model="value"
data-:disabled="!isValid"
data-_click="handleClick"> # `#` could not be used
</my-comp>
or
<my-comp id="my_comp_1"
v-model="value"
data='
:disabled="!isValid"
#click="handleClick">
'>
</my-comp>
I'll with the attribute set to something like data-FIXME.
I got these solutions to work. I thought of solution 1.
Starting code:
<div
v-for="foo in foos"
:key="foo.id"
#click="foo.on = !foo.on /* JavaScript comment. */"
>
<label>
{{ foo.name }} {{foo.on}}
</label>
</div>
The Vue directive HTML attribute that needs to be disabled: #click="foo.on = !foo.on"
How the final div tag will run:
<div
v-for="foo in foos"
:key="foo.id"
>
Solutions 1 and 2 keep the disabled attribute inside its tag. I didn't find a good way to make a "raw string". To keep the attribute in the tag, the outer and inner quotes must be different.
sol. 1: I made a new v-bind attribute (:lang) to put the disabled attribute in.
:lang='en /* #click="foo.on = !foo.on" */'
Sol. 2: Pick a Vue directive. Put the attribute in.
v-for="foo in foos /* #click='foo.on = !foo.on' */"
Solutions 3 and 4 put the attribute outside the tag.
Sol. 3:
<div v-if="false">
#click="foo.on = !foo.on"
</div>
Sol. 4: <!-- #click="foo.on = !foo.on" -->
One way to remove/hide component attributes is to create a custom directive for it.
Let's say you create a directive called v-hide and put it in your component as:
<my-comp v-model="value" #click="handleClick" v-hide :disable='true'></my-comp>
And the output would be:
<my-comp v-model="value" #click="handleClick"></my-comp>
Here is a working example:
Vue.component ('my-component', {
template: `<p> A custom template </p>`
})
Vue.directive('hide', {
inserted: function (el) {
console.log('el before hide', el)
while(el.attributes.length > 0)
el.removeAttribute(el.attributes[0].name);
console.log('el after hide', el)
}
})
new Vue({
el: '#app',
data () {
return {
someValue: 'Hello'
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.js"></script>
<div id="app">
<my-component v-model='someValue' v-hide :disable='true'></my-component>
</div>

Access v-for current item from v-bind directive function call

I need to loop over some tasks objects with a v-for directive.
<div v-for="(currentTask, taskName) in step.tasks">
<span>{{ currentTask.title }}</span>
<button :class="getTaskButtonProp(currentTask, 'class')" :disabled="getTaskButtonProp(currentTask, 'disabled')">{{ getTaskButtonProp(currentTask, 'caption') }}</button>
</div>
The vue instance method involved:
// …
,methods: {
getTaskButtonProp : function (task, key) {
let out = tasksStatusDescriptor[task.status][key];
// out variable manipulation …
return out;
}
}
The data involved:
Vue complains and says ReferenceError: currentTask is not defined., as if v-bind directive parsing did not grant access to the current loop scope.
Did I miss something here ? Is there some sort of special syntax here ? Or did anyone already spot a workaround ? Thank you.
EDIT
This code is perfectly fine. A missing attribute's ending double quote boundary, up in the dom tree, led to a set of errors that have now disappeared.

Processing local components with vue

I have the following code:
<my-messages>
<message>Hello</message>
<message>World</message>
</my-messages>
For now, I did my <my-messages> component be renderized as:
<div class="Messages">
<!-- slot here -->
</div>
And I like to do the same for <message>, but the problem is that I receiving the error Unknown custom element: <message>. Except if I change my code to:
<my-messages inline-template>
<message>Hello</message>
</my-messages>
It seems a bit hack, once that I should declare the [inline-template] to all <my-messages> components, instead of it be treated directly from this component as a default rule (eg. an option as inlineTemplate: true should do the work, if it exists).
The expected render should be like:
<div class="Messages">
<div class="message">Hello</div>
<div class="message">World</div>
</div>
My component currently:
export default {
components: {
// Should process <message> sub-component.
message: require('./Messages.Message.vue'),
}
}
Edit: on reality, the inline-template seems mixing both <div>s from template, and not nesting it.
inline-template is not a hack. I think The problem is you're not registering message components at the same place where you're using my-messages component.
So the parent component that has my-messages as a child can't understand message, you need to register it in the parent too, when you use inline-template the scope changes and and whatever is inside will be treated as inner content. You can find it in the docs
EDIT
There isn't a way to have <message> to be only usable as a child of <my-messages>, you could however throw an exception if it's misused
mounted() {
if (!this.$parent.$el.classList.contains('my-message')) {
this.$destroy();
throw new Error('You must wrap the message in a my-message');
}
}
Note that this supposes that the class my-message is available in the root element, this way you can use any wrapper element.

Use global functions in vue directives

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.