After struggling for a while, I was able to get Vue working with webpacker in my Rails 4.2.5 app. This is my first Vue project and I am still unsure of a lot of things when using it.
I am using Buefy, which are Vue.js components based on Bulma CSS (https://buefy.github.io/#/documentation/tabs). I have some tabs setup inside of a modal and I want each tab to render a form for a different partial, however, I don't know how to set this up as Vue can't run the ruby code. The result of this code is that the tabs work properly, but my Ruby render code is shown as a string and not run.
I could just copy the contents of the partials into the vue.js file, but there is still more ruby I am calling in there. I am sure there is a proper way to do all of this, but I just can't figure it out.
Here is my html/haml file calling the modal
#buefy
= javascript_pack_tag 'buefy_modal'
Here is my buefy_modal.js
import Vue from 'vue'
import App from './B_modal.vue'
import Buefy from 'buefy'
Vue.use(Buefy)
new Vue({
el: '#buefy',
render: h => h(App)
})
and here is the B_modal.vue
<template>
<section>
<button class="button is-primary is-medium"
#click="isCardModalActive = true">
Social Save
</button>
<b-modal :active.sync="isCardModalActive" :width="640" has-modal-card>
<div class="card">
<div class="card-content">
<section>
<b-tabs v-model="activeTab">
<b-tab-item label="Lists">
<%=render partial: 'shared/list_item/list_form', locals: {media: #media} %>
</b-tab-item>
<b-tab-item label="Clubs">
<%=render partial: 'shared/club_item/club_form', locals: {media: #media} %>
</b-tab-item>
<b-tab-item label="Challenges">
<%=render partial: 'shared/challenge_item/challenge_form', locals: {media: #media} %>
</b-tab-item>
</b-tabs>
</section>
</div>
</div>
</b-modal>
</section>
</template>
<script>
export default {
data() {
return {
isCardModalActive: false,
activeTab: 0,
}
}
}
</script>
Maybe try to rename file to something.vue.erb
Related
given the following generated html
<a href="#" class="primaryInversed v-btn v-btn--large v-btn--round"
<div class="v-btn__content">STOP!
<i aria-hidden="true" class="v-icon v-icon--right material-icons">pause_circle_outline</i>
</div>
</a>
when I test with the .toEqual Jest matcher
console.log(playLink.text())
expect(playLink.text()).toEqual("STOP!");
test is failing because of the icon
console.log tests/unit/Heading.spec.js:46
STOP!
pause_circle_outline
It foes not fail if I use the .toMatch watcher
expect(playLink.text()).toMatch(/STOP!/);
Is it the normal test to be written or is there anyway to use the .toEqual watcher ?
NOTE : I used 'mount' and not 'shallowMount' as I need to generate html from vuetify components
thanks for feedback
One technique is to wrap the <v-btn>'s text content in a <span>, and use wrapper.find(selector) to select the <span> to get its text:
foo.vue template:
<v-btn>
<span class="text">STOP!</span>
<v-icon>pause_circle_outline</v-icon>
</v-btn>
foo.spec.js
it('contains the expected text', () => {
expect(wrapper.find('.text').text()).toEqual('STOP!');
});
demo
I try to get working the electron-vue boilerplate. After setting up the project everything works, but as I create a new .vue file (TopMenu.vue) I get:
vue.common.js?4eb4:2569 [Vue warn]: Unknown custom element: <topmenu> -
did you register the component correctly? For recursive components, make
sure to provide the "name" option. (found in component <landing-page>)
I use the exact syntax as the original .vue files which came with the boilerplate:
LandingPageVue.vue:
<style scoped>
img {
margin-top: -25px;
width: 450px;
}
</style>
<template>
<div>
<!-- <img src="./LandingPageView/assets/logo.png" alt="electron-vue"> -->
<h1>Welcome.</h1>
<topmenu></topmenu>
<current-page></current-page>
<versions></versions>
<links></links>
<div class="container">
</div>
</template>
<script>
import TopMenu from './LandingPageView/TopMenu'
import CurrentPage from './LandingPageView/CurrentPage'
import Links from './LandingPageView/Links'
import Versions from './LandingPageView/Versions'
export default {
components: {
TopMenu,
CurrentPage,
Links,
Versions
},
name: 'landing-page'
}
</script>
TopMenu.vue (my file):
<template>
<p>
TOPMENU
</p>
</template>
By the way, how the hack does <current-page></current-page> work (notice the "-" dash) if bellow it is declared without?
It's not working because you're not exporting anything in your vue file.
Try this in your TopMenu.vue file:
<template>
<p>
TOPMENU
</p>
</template>
<script>
export default {
}
</script>
Also change the html <topmenu></topmenu> to <top-menu></top-menu>
For your second question, HTML is case insensitive so your title case components wouldn't match with html tags. So Vue translates your title case components to a 'dash-case'.
From the documentation itself there's the explanation why:
Note that Vue does not enforce the W3C rules for custom tag names (all-lowercase, must contain a hyphen) though following this convention is considered good practice.
You can read more from the the docs
I've got a vue app in which I'm using the vue-router.
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
let router = new VueRouter()
// Components
import App from './App.vue'
import Mapper from './components/Mapper/mapper.vue'
import ToDos from './components/Todos/ToDoApp.vue'
import Punchlist from './components/Punchlist/punchlist.vue'
// Transitions
Vue.transition('slide',{
enterClass: 'slideInRight',
leaveClass: 'slideOutRight'
})
// Redirects
router.redirect({
'*': 'punchlist'
})
// Mappings
router.map({
'/mapper': {
component: Mapper
},
'/todos': {
component: ToDos
},
'/punchlist': {
component: Punchlist
}
})
router.start(App, '#app')
I have a specific transition registered called slide that I would like to use when navigating between routes. In my App component I added the v-transition and transition-mode directives to the route-view:
<template>
<div class="container">
<h1>Component Gallery</h1>
<p>
<a class='btn btn-primary' v-link="{ path: '/punchlist' }">Punchlist</a>
<a class='btn btn-primary' v-link="{ path: '/todos' }">Todos</a>
<a class='btn btn-primary' v-link="{ path: '/mapper' }">Mapper</a>
</p>
<router-view v-transition="slide" transition-mode="out-in" :google-maps-api-key="googleMapsApiKey"></router-view>
</div>
</template>
When I try to run it, I get the console error:
[Vue warn]: Failed to resolve directive: transition (found in component: )
I've been reading through the docs and looking at examples but I can't figure out why it's erroring out when trying to resolve the binding.
Any ideas?
Transition is an attribute, not a directive. No v-:
<router-view transition="slide">
Is it possible to have an if / else statement which does not render any html in a view similar to knockout:
<!-- ko if: someExpressionGoesHere -->
but it needs to be on an element
Yes, but if v-if conditional is false, it's not added to DOM tree.
HTML
<div id="main"></div>
JavaScript
new Vue({
el: "#main",
template: '<div v-if="name"><span v-text="name"></span></div>',
data: {
// name: "bob"
}
});
console.log(document.body.innerHTML);
// <div id="main"><!--vue-if--></div>
Still not good for you?
I know the question was already answered, but thought I would pass along something I use, now that I am writing sites with Vue (which I love.) I am a fan of Knockout and have many sites written in it using the:
<!-- ko if: someExpressionGoesHere -->
You could do a similar thing in Vue like this:
<template v-if="someExpressionGoesHere">
<p>Expression is True</p>
</template>
<template v-else>
<p>Expression is False</p>
</template>
The templates will not render anything to the page. The resulting html will be a single p of the 'Expression is xxx'.
I think it is a bit more clear of what the intent of the code is here than the actual answer to this post IMHO.
you can also use this way to write if else condition in vue.js
<template>
<div id="app">
<p v-if="someConditionHere">Condition True</p>
<p v-else>Condition False</p>
</div>
</template>
I have this wrapper around Ember.Select, to activate Select2 features:
App.Select2SelectView = Ember.Select.extend({
prompt: 'Please select...',
classNames: ['input-xlarge'],
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, 'processChildElements');
},
processChildElements: function() {
this.$().select2({
// do here any configuration of the
// select2 component
escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
});
},
willDestroyElement: function () {
this.$().select2('destroy');
}
});
Sometimes I need to make a drop-down invisible, and I do it like this:
{{#if cityVisible}}
<div class="control-group">
<label class="control-label">City</label>
<div class="controls">
{{view SettingsApp.Select2SelectView
id="city-id"
contentBinding="currentCities"
optionValuePath="content.city"
optionLabelPath="content.city"
selectionBinding="controller.selectedCity"
prompt="Select a city"}}
<i class="help-block">Select the city for your geographical number</i>
</div>
</div>
{{/if}}
But whenever the drop-down is invisible, I get the following error:
Uncaught TypeError: Cannot call method 'select2' of undefined
I guess the element is inserted, but then removed by Ember from the DOM (bound property cityVisible), so that jQuery is not able to find it?
What can I do to avoid that error message? I do not want to make the view visible/invisible, I want to keep the whole control-group under the cityVisible control.
This is normal behaviuor that ember removes the view, as a workaround you could do the following:
HTML
<div {{bindAttr class="view.cityVisible::hideCities"}}>
<div class="control-group">
...
</div>
</div>
CSS
.hideCities {
display: none;
}
Remove the {{#if}} around the html block, and wrap it with a div instead on which you set a css class which contains display: none; you could use the cityVisible or a different property in your view or controller and set it to true/false to toggle it's visibility. This mecanisnm should leave your html markup in the DOM an thus available for jQuery. Note that if your citiesVisible property lives in your controller then remove the view. prefix from view.citiesVisible to be only citiesVisible, this depends on your setup.
See demo here.
Hope it helps.