I want to change a data property and run a method on my Vue instance within Laravel. However due to using webpack and laravel I can't seem to access the instance how I would expect to:
So window.app doesn't appear to be the correct instance of my Vue class.
Below is the Blade View i'm loading, as you can see I append a script tag to my main layout.blade.php, simply trying to change the Vue instance data property, and run a method.
#push('scripts')
<script>
app.unsaved = true;
app.triggerEvent(null, 'models', null);
</script>
#endpush
Below is part of my app.js (resources/assets/js/app.js):
const app = new Vue({
el: '#app',
components: {
'models-select': ModelsSelect
},
data: {
showModel: false,
unsaved: false
},
mounted: function() {
let _self = this;
(function() {
document.querySelectorAll("input, textarea, select").forEach(function(e) {
e.addEventListener('change', function() {
_self.unsaved = true;
e.classList.add('is-changed');
});
});
function unloadPage(){
if (_self.unsaved) return 'You appear to have un-saved changes!';
}
window.onbeforeunload = unloadPage;
})();
},
methods: {
triggerEvent: function(event, target, property)
{
app.$refs[target].update(event, property);
}
As you can see i'd expect to manipulate the Vue instance through the global app variable I have defined within the app.js. However this doesn't appear to be the case.
I get the following error when running the triggerEvent method:
app.triggerEvent is not a function
In your app.js file, change const app = new Vue({ to window.app = new Vue({.
Then within your <script> tags, change it to this.
<script>
window.app.unsaved = true;
window.app.triggerEvent(null, 'models', null);
</script>
Related
I'm trying to append a click event to an already existing dom element.
<div class="logMe" data-log-id="{{ data.log }}"></div>
...
<div id="events"></div>
I can't seem to access outside vue methods within my jquery click handler. It will throw logData is not defined.
new Vue({
el: '#events',
mounted() {
$('.logMe').on('click', function() {
const data = $(this).data('log-id');
this.logData(data); // throws logData is not defined
});
},
methods: {
logData(id) {
console.log(id); // never fires
... hit server
},
},
});
If anyone knows of a better way to append click events to .html elements that would be great! But how can I bubble up and find my vue methods? Here's a fiddle to illustrate
To get your code working you would write it like this:
console.clear()
new Vue({
el: '#events',
mounted() {
$('.logMe').on('click', (evt) => {
console.log("called")
const data = $(evt.target).data('logId');
this.logData(data);
});
},
methods: {
logData(id) {
console.log(id);
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://unpkg.com/vue#2.4.2"></script>
<div class="logMe" data-log-id="10">Something</div>
<div id="events"></div>
Note that the code uses an arrow function to define the click handler so that the appropriate this is captured to call the logData method on the Vue. However, doing that means you lose the this you want to get the data from the data property, so instead the example uses evt.target.
There is an alternative approach where you capture the Vue in a variable and call the method directly from your jQuery event.
console.clear()
const app = new Vue({
el: '#events',
methods: {
logData(id) {
console.log(id); // never fires
},
},
});
$('.logMe').on('click', function(){
app.logData($(this).data("logId"))
});
<script src="https://unpkg.com/vue#2.4.2"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="logMe" data-log-id="10">Something</div>
<div id="events"></div>
Much easier solution =
window.app = new Vue({
el: "#app",
methods: {
myMethod(data) { ... }
}
}
// jQuery
window.app.myMethod(data);
This is a pseudocode to illustrate my question. When the script is run, it will encounter the error func1 is not defined. How can I go about this?
app.js
require ('./helper');
new Vue ({
el: "#app",
created: function () {
func1()
}
});
helper.js
module.exports = function () {
$.notify('Hi', {
position: "bottom right",
className: "success"
});
};
Thanks in advance!
You might have to save the imported function to a variable in your app.js file. It depends on what bundler you use (e.g. browserify or webpack or else). Functions declared outside of the parameter object you are passing to Vue should be accessible:
// app.js
var func1 = require('./helper');
new Vue ({
el: "#app",
created: function () {
func1();
}
});
In your helpers.js, you need a reference to jQuery. You can either include the library (jQuery and $ will be available globally on the window object) or like in the example below, install it from https://www.npmjs.com/package/jquery and bundle it with your own code.
// helper.js
var $ = require('jquery');
module.exports = function () {
$.notify('Hi', {
position: "bottom right",
className: "success"
});
};
If you want to call this function in more than one component's created method, you can create a mixin, which then will be passed to specific component.
//YourMixin.js
new Vue({
created: function () { ... }
})
//Component
let mixin = require('YourMixin')
new Vue({
mixins: [mixin],
...
})
I have a very basic Vue.js app that looks like this:
index.html (just the <body> for succinctness)
<div id="root"></div>
main.js
var config = {
title: 'My App',
}
new Vue({
el: '#root',
template: '<div>{{ config.title }}</div>',
})
This gives me:
Property or method "config" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in root instance)
I'm guessing it's because Vue is looking for it in the data store rather than window. Is there some way to signal that config is a global here, or a better way to do this entirely? I suppose I could pass the config object as a prop to my root Vue instance, but it seems like only components accept props. Thanks for any insights on this.
You can try to define your app as follows:
new Vue({
el: '#root',
template: '<div>{{ config.title }}</div>',
data: function() {
return {
config: window.config
}
}
})
In the above definition, your this.config within the root component points to window.config - the global variable.
If you are just trying to pass the App Name in as a hard-coded string you can do something like this:
var nameMyApp = new Vue({
el: '#root',
data: { input: 'My App' },
computed: {
whosapp: function () {
return this.input
}
}
})
With your html like this:
<div id="root">
<div v-html="whosapp"></div>
</div>
Which will pass the value of input to the whosapp div.
But, if you're looking for something more dynamic, this would be the way to go:
With the same html as above...
var nameMyApp = new Vue({
el: '#root',
data: { input: 'My App' },
The initial value is set to My App, which is what will display if not changed through a function call on the method.
methods: {
update: function(name) {
this.input = name;
}
},
Here is where we define a method that, when called, passes in the parameter to update this.input.
computed: {
whosapp: function () {
return this.input
}
}
})
nameMyApp.update("New Name");
And here is where we call the function, giving it the new parameter; making sure that the parameter is a string.
I hope this is what you're looking for.
I am writing a webapp with VueJs, I am trying to setup unit test for it, I got inspired from vue-mdl unit-tests. But the tests are not running properly for my code and I am getting vm.$el as undefined, so not able to move forward at all.
Here is the component, I am trying to test:
Confirmation.vue
<template>
<div>
Your order has been confirmed with the following details.
</div>
</template>
<script type="text/javascript">
export default {
data () {
return {
data_from_pg: null
}
}
}
</script>
and here is test for it, which fails
Confirmation.spec.js
import Confirmation from 'src/components/Confirmation'
import { vueTest } from '../../utils'
describe('Confirmation', () => {
let vm
let confirmation
before(() => {
vm = vueTest(Confirmation)
console.log('vm.$el ' + vm.$el) => this prints undefined
confirmation = vm.$el.querySelector('#confirmation') => so this line gives error
// confirmation = vm.$('#confirmation')
})
it('exists', () => {
confirmation.should.exist
confirmation.should.be.visible
})
})
utils.js
export function vueTest (Component) {
const Class = Vue.extend(Component)
Class.prototype.$ = function (selector) {
return this.$el.querySelector(selector)
}
Class.prototype.nextTick = function () {
return new Promise((resolve) => {
this.$nextTick(resolve)
})
}
const vm = new Class({
replace: false,
el: 'body'
})
return vm
}
My complete code is available here, with all the test config, which I have tried to change many times, but could not figure out how to make it work. Please let me know if you see some error somewhere.
The vueTest function in utils is trying to load the Vue instance into the body tag:
const vm = new Class({
replace: false,
el: 'body'
})
return vm
The unit tests do not load index.html as an entry point into the app, but rather the individual components that you want to test; Therefore, you do not have access to document or html elements and the component is never mounted. I'd suggest using vm.$mount():
If elementOrSelector argument is not provided, the template will be rendered as an off-document element.
You could change the above lines to something like the following
const vm = new Class();
vm.$mount();
return vm;
Your tests should now have access to the $el property.
I'm trying to build a simple app in vue and I'm getting an error. My onScroll function behaves as expected, but my sayHello function returns an error when I click my button component
Property or method "sayHello" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option. (found in component )
Vue.component('test-item', {
template: '<div><button v-on:click="sayHello()">Hello</button></div>'
});
var app = new Vue({
el: '#app',
data: {
header: {
brightness: 100
}
},
methods: {
sayHello: function() {
console.log('Hello');
},
onScroll: function () {
this.header.brightness = (100 - this.$el.scrollTop / 8);
}
}
});
I feel like the answer is really obvious but I've tried searching and haven't come up with anything. Any help would be appreciated.
Thanks.
But for a few specific circumstances (mainly props) each component is completely isolated from each other. Separate data, variables, functions, etc. This includes their methods.
Thus, test-item has no sayHello method.
You can get rid of the warning by using .mount('#app') after the Vue instance rather than the el attribute.
Check the snippet below;
var app = new Vue({
data: {
header: {
brightness: 100
}
},
methods: {
sayHello: function() {
console.log('Hello');
},
onScroll: function () {
this.header.brightness = (100 - this.$el.scrollTop / 8);
}
}
}).mount('#app');
Please note; the following might not be necessary but did it along the way trying to solve the same issue: Laravel Elixir Vue 2 project.