Using v-on:click I'd like to set a variable with the id of the div in Vue.JS - how do I reference this?
<div id="foo" v-on:click="select">...</div>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
select: function(){
divID = this.id // ??
alert(divID)
}
}
})
</script>
You can extend your event handler with the event object $event. That should fit your needs:
<div id="foo" v-on:click="select($event)">...</div>
The event is passed on in javascript:
export default {
methods: {
select: function(event) {
targetId = event.currentTarget.id;
console.log(targetId); // returns 'foo'
}
}
}
As mentioned in the comments, `$event` is not strictly necessary, when using it as the only parameter. It's a nice reminder that this property is passed on, when writing it explicitly.
However, nobody will stop you from writing the short notation:
<div id="foo" #click="select">...</div>
Beware that the method will not receive the `$event` object when you add another parameter. You need to explicitly add it at the position you will handle it in the listener. Any parameter order will work:
<div id="foo" #click="select(bar, $event)">...</div>
To find more options of the v-on directive, you can look through the corresponding entry in the vue documentation:
Vue API Documentation - v-on
Inspired by #nirazul's answer, to retrieve data attributes:
HTML:
<ul>
<li
v-for="style in styles"
:key="style.id"
#click="doFilter"
data-filter-section="data_1"
:data-filter-size="style.size"
>
{{style.name}}
</li>
</ul>
JS:
export default {
methods: {
doFilter(e) {
let curTarget = e.currentTarget;
let curTargetData = curTarget.dataset;
if (curTargetData) {
console.log("Section: " + curTargetData.filterSection);
console.log("Size: " + curTargetData.filterSize);
}
}
}
}
Just to highlight another option than the selected answer for the same question, I have a delete button on a record and want to perform an action with the record's unique id (a number). I could do the selected answer as before:
<button :id="record.id" #click="del">×</button>
This leaves the unfortunate reality that my del function needs to pull the id attribute out of the javascript event, which is more about the API (the DOM) than my domain (my app). Also using a number as an element id isn't ideal and could cause a conflict if I do it more than once in a view. So here's something that's just as clear and avoids any future confusion:
<button #click="()=>del(record.id)">×</button>
methods: {
del(id) {
fetch(`/api/item/${id}`, {method:"DELETE"})
}
}
You see, now my del function takes the record id instead of an event, simplifying things.
Note that if you do this wrong, you will invoke your delete function immediately, which is not what you want. Don't do this:~~
<button #click="del(record.id)">×</button>
If you end up doing that, Vue will call the del function every time this html fragment is rendered. Using the anonymous function ()=>del(record.id) will return a function that's ready to be executed when the click event happens.
Actually #nirazul proved this is fine. Not sure what my issue was.
Related
I have a parent/child component setup where the parent is loading data from the server and passing it down to children via props. In the child I would like to instantiate a jQuery calendar with some of the data it receives from the parent.
In order to wait for the data before setting up the calendar, I broadcast an event in the parent that I have an event listener setup for in the child.
The listener is being fired in the child but if I this.$log('theProp'), it's undefined. However, if I inspect the components with the VueJs devtools, the parent/child relationship is there and the child has received the prop in the meantime.
The prop is defined on the child as a dynamic prop :the-prop="theProp". Since the child does receive the prop in the end, I'm assuming my setup is correct but there seems to be some sort of delay. The parent sets the props in the return function of the ajax call and again: it's working, just with a slight delay it seems.
I also tried registering a watch listener on the prop in the child so I could setup the calendar then and be sure that the prop is there. However, the watch listener fires, but this.$log('theProp') is still undefined.
If I pass the data along with the the broadcast call, like this.$broadcast('dataLoaded', theData) the child receives it just fine. But it seems wrong to do it that way as I'm basically building my own prop handler.
I'm not posting any code because the components are rather large and the VueJs devtools are telling me the parent/child situation is working.
Am I missing some information? Is there a slight delay between setting a value in the parent and the child receiving it? What would be the proper way to wait for parent data in the child?
Normally, when you're just rendering the data out into the template, the timing doesn't matter so much since the data is bound to the template. But in this case, I really need the data to be there to setup the calendar or it will be wrong.
Thanks.
edit 1: here's a jsfiddle: https://jsfiddle.net/dr3djo0u/1/
It seems to confirm that the data is not available immediately after the broadcast. However, the watcher does work, though I could almost swear that sometimes this.$log('someData') returned undefined when I setup that testcase.
But I guess my problem might be somewhere else, I'll have a look tonight, don't have the project with me right now.
edit 2: did some more tests. My problem was that a) event listeners do not seem to receive the data instantly and b) I was also trying to init the calendar in the route.data callback if someData was already around (e.g. when coming from parent), but that route callback is called before the component is ready, so it wasn't working there either.
My solution is now this:
// works when the child route is loaded directly and parent finishes loading someData
watch: {
someData() {
this.initCalendar();
}
},
// works when navigating from parent (data already loaded)
ready() {
if (this.someData && this.someData.length) {
this.initCalendar()
}
}
As far as I know, you should not need events to pass data from parent to child.
All you need is, in the child component: props: ['theProp']
And when using the child component in the parent: <child :theProp="someData"></child>
Now, wherever in the parent you change someData, the child component will react accordingly.
You don't need events, you don't need "watch", you don't need "ready".
For example: after an AJAX call, in the parent's "ready", you load some data:
// at the parent component
data: function () {
return {
someData: {}
}
},
ready: function () {
var vm = this;
$.get(url, function(response) {
vm.someData = response;
});
}
Now, you do not need anything else to pass the data to the child. It is already in the child as theProp!
What you really need to do is to have, in the child, something which reacts to data changes on its own theProp property.
Either in the interface:
<div v-if="theProp.id > 0">
Loaded!
</div>
Or in JavaScript code:
// at the child component
computed: {
// using a computed property based on theProp's value
awesomeDate: function() {
if (!this.theProp || (this.theProp.length === 0)) {
return false;
}
if (!this.initialized) {
this.initCalendar();
}
return this.theProp.someThing;
}
}
Update 1
You can also, in the parent, render the child conditionally:
<child v-if="dataLoaded" :theProp="someData"></child>
Only set dataLoaded to true when the data is available.
Update 2
Or maybe your issue is related to a change detection caveat
Maybe you're creating a new property in an object...
vm.someObject.someProperty = someValue
...when you should do...
vm.$set('someObject.someProperty', someValue)
...among other "caveats".
Update 3
In VueJS 2 you are not restricted to templates. You can use a render function and code the most complex rendering logic you want.
Update 4 (regarding OP's edit 2)
Maybe you can drop ready and use immediate option, so your initialization is in a single place:
watch: {
someData: {
handler: function (someData) {
// check someData and eventually call
this.initCalendar();
},
immediate: true
}
}
It's because tricky behavior in Vue Parent and Child lifecycle hooks.
Usually parent component fire created() hook and then mount() hook, but when there are child components it's not exactly that way: Parent fires created() and then his childs fire created(), then mount() and only after child's mount() hooks are loaded, parent loads his mount() as explained here. And that's why the prop in child component isn't loaded.
Use mounted() hook instead created()
like that https://jsfiddle.net/stanimirsp5/xnwcvL59/1/
Vue 3
Ok so I've spent like 1.5h trying to find out how to pass prop from parent to child:
Child
<!-- Template -->
<template>
<input type="hidden" name="_csrf_token" :value="csrfToken">
<span>
{{ csrfToken }}
</span>
</template>
<!-- Script -->
<script>
export default {
props: [
"csrfToken"
]
}
</script>
Parent
<!-- Template -->
<template>
<form #submit.prevent="submitTestMailForm" v-bind:action="formActionUrl" ref="form" method="POST">
...
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
...
</form>
</template>
<!-- Script -->
<script>
...
export default {
data(){
return {
...
csrfToken : "",
}
},
methods: {
/**
* #description will handle submission of the form
*/
submitTestMailForm(){
let csrfRequestPromise = this.getCsrfToken();
let ajaxFormData = {
receiver : this.emailInput,
messageTitle : this.titleInput,
messageBody : this.bodyTextArea,
_csrf_token : this.csrfToken,
};
csrfRequestPromise.then( (response) => {
let csrfTokenResponseDto = CsrfTokenResponseDto.fromAxiosResponse(response);
this.csrfToken = csrfTokenResponseDto.csrToken;
this.axios({
method : "POST",
url : SymfonyRoutes.SEND_TEST_MAIL,
data : ajaxFormData,
}).then( (response) => {
// handle with some popover
})
});
},
/**
* #description will return the csrf token which is required upon submitting the form (Internal Symfony Validation Logic)
*/
getCsrfToken(){
...
return promise;
}
},
components: {
CsrfTokenInputComponent
}
}
</script>
Long story short
This is how You need to pass down the prop to child
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
NOT like this
<CsrfTokenInputComponent csrf-token="csrfToken"/>
Even if my IDE keep me telling me yeap i can navigate with that prop to child - vue could not bind it.
solution (testing ok)
In child component just using the props data, no need to re-assignment props's values to data, it will be cause update bug!
vue child component props update bug & solution
https://forum.vuejs.org/t/child-component-is-not-updated-when-parent-component-model-changes/18283?u=xgqfrms
The problem is not how to pass data with props, but rather how to do two things at almost the same time.
I have an user account component that can edit users (with an user id) and add users (without id).
A child component shows checkboxes for user<->company assignments, and needs the user id to prepare API calls when the user account is saved.
It is important that the child component shows before saving the user account, so that things can be selected before the user is saved and gets an id.
So it has no user id at first: the id is passed to the child component as 'null'.
It updates when the user is stored and gets an id.
But at this point, it takes a very short time for the child to get the new id into its model.
If you call a function in the child component that relies on data that was just changing, it might happen that the function executes before the data is updated.
For cases like this, nextTick() is your friend.
import { nextTick } from 'vue';
...
saveAccount() {
axios.post(URL, this.userModel).then((result)) {
// our model gets an id when persisted
this.userModel.id=result.data.id;
nextTick( () => {
this.$refs.childComponent.doSomething();
});
}
}
If I have the following in my Ractive template:
<span on-click='handleClick'>click me</span>
Then I can listen for the click with this:
app.on({
handleClick:function() {
alert("clicked!") ;
}
})
But lets say I have that same markup stored in a string variable called clicklyspan:
app.set("clicklyspan", "<span on-click='handleClick'>click me</span>")
and I render it in the template using the triple-stash syntax:
{{{clicklyspan}}}
The handleClick listener no longer gets fired. Is there anything I can do to force some kind of update to the rendered template so that the listener works? Say, after I do that app.set() call?
Here's a fiddle demonstrating the problem.
Thanks,
Dave
I have never used Ractive, but I did some research and it seems you have to use partials, like this:
var app = new Ractive({
el: 'container',
template: '#template',
data: {
myFunction: function() {
var template = '<a on-click="handleClick">I can now be clicked as well!</a>';
if (!this.partials.myFunction) {
this.partials.myFunction = template;
}
else {
this.resetPartial('myFunction', template);
}
return 'myFunction';
}
}
});
You will also need to use this instead of the triple mustache:
{{> myFunction() }}
Here's the corresponding jsfiddle.
Of course, replace myFunction with whatever name you like.
Related question I found useful:
RactiveJS events on tripple mustache
Could anybody point me how to access an original TemplateInstance from the meteor helper. I'm aware of the Template.instance() but it appears to return the template instance where the helper was called, not the template instance for which the helper was defined.
Imagine we have two tiny templates:
<template name='demo'>
<h1>{{helper}}</h1>
{{# border}}
<h2>{{helper}}</h2>
{{/border}}
</template>
<template name='border'>
<div style="border:1px solid red">
{{> UI.contentBlock}}
</div>
</template>
With the following behavior:
Template.demo.created = function() {
this.result = "OK";
}
Template.demo.helpers({
helper: function() {
var tmpl = Template.instance();
return tmpl.result || "FAILED";
}
});
I've expected to obtain two "OK" for the demo template: the second one should be in the red border. But since Template.instance() returns original TemplateInstance only when helper is called at the top level of its owner template the result is "FAILED" (of course in the red border).
Question: Is there any public api to get the original TemplateInstance (without need to traverse view/parentView/_templateInstace)?
I think the best way to do this might be to either just set a Session variable, or use a Reactive Variable (using the reactive-var package - here is the documentation).
I've made a meteor pad to show how this more - here.
Basically:
Template.demo.created = function() {
result = new ReactiveVar('OK');
}
Template.demo.helpers({
helper: function() {
return result.get() || "FAILED";
}
});
I think your main problem is that you not setting a template instance variable correctly. Try the below code...
Set an instance variable:
Template.instance().result.set("OK");
Get an instance variable:
Template.instance().get("result");
So your updated code would be:
Template.demo.created = function() {
Template.instance().result.set("OK");
}
Template.demo.helpers({
helper: function() {
return Template.instance().get("result") || "FAILED";
}
});
It seems that it's known and already fixed (?) Meteor bug. More here: https://github.com/meteor/meteor/issues/3745
Comment from rclai on GitHub:
This was already addressed and fixed for the next release.
Run meteor like this, not sure if it still works:
meteor --release TEMPLATE-CURRENT-DATA#0.0.1
Another alternative is to use aldeed:template-extensions, which has super nice features, especially with dealing with template instances and I believe their way of fetching the template instance is a workaround this issue.
I have a sortable list.
<template name="the_playlist">
{{#each main_list}}
<li id="{{index}}" class="list_element">
<div class="next_song">...</div>
<div class="destroy">...</div>
<div class="element_style">{{song_title}}</div>
</li>
{{/each}}
</template>
And this is the main_list that it prints from.
Template.the_playlist.main_list = function(){
//if ret is valid, it will have a songs member
var ret = Links.find().fetch()[0];
if (typeof ret == 'undefined'){
ret = []
}
else {
ret = Links.find().fetch()[0].songs;
}
return ret;
}
And I am using the sortable plugin and more importantly its update callback which updates everytime the user changes a position the list or an element is added to the list.
$(function() {
$( "#playlist" ).sortable({
update: function(){
Template.list.updateList(); //MODIFIES DB CONTENTS, AND MAIN_LIST's VALUES CHANGE
}});
$( "#playlist" ).disableSelection();
});
*The problem: * If a page already has list elements when it's loaded, for one time only, I would like to add a class that hides (.addClass("hide")) each of the next_song elements that are on the page at that time. This *will work only until main_list changes* by a call to Template.list.updateList above, after which automagically, the added class will disappear - most likely due to the re-rendering that is occuring since the main_list depends on the db changes.
The following in the JQuery snippet I use to try and accomplish this.
$("#playlist li .next_song").each(function(){
$(this).addClass("hide_song");
})
Here is a demo. Try plugging in the above JQUery code into the console. and then move the list elements around to see the problem.
Can you not just determine whether that will be the case in a helper function?
Template.the_playlist.helpers({
'list_elements_exist': function() {
return (!!$('#playlist li').length);
}
}
Then you can just insert the logic straight into the template:
<div class="next_song{{#if list_elements_exist}} hide_song{{/if}}">...</div>
To be honest, I'm not 100% sure that this will float with reactivity depending on the structure of your app. If it doesn't work properly, I'd introduce a new session boolean, list_elements, the value of which is returned by the helper function above. It should be fairly easy to update its value in event handlers or created callbacks to keep it tracking whether there are any items in the list or not, and this will guarantee the list renders as required regardless of other dependencies changing.
Please see this pen for a demo of the issue (based on the slideshow from the tutorial). When clicking on "next" and "prev" arrows, you'll notice that the imgIndex mustache updates correctly, but the expression mustaches such as <p>{{ curImageCaption() }}</p> do not recognize when their values are changing.
That is, the object is mutated such that the mustache value would change if the expressions were re-evaluated, but ractive doesn't seem to realize that. Is there any way to get this to work, barring writing adaptors? Am I misunderstanding how magic mode works? The interesting thing is that even if I explicitly call ractive.update() inside the event handlers, ractive still doesn't respond.
UPDATE WITH NEW INFO
After more fiddling, I came up with this hack that gets it working. The hack is to change, eg, <p>{{ curImageCaption() }}</p> to <p>{{ curImageCaption(imgIndex) }}</p> -- adding a simple primitive to the mustache expression which ractive understands how to watch correctly.
I think I see what's going on now, but having to explicitly add arguments to the mustache expression containing changing primitives defeats much of the purpose of having the separate domain object -- that is, now you are coding your domain object with ractive in mind, using changing primitives a sort of basic pub/sub mechanism for notifying ractive of changes.
Having to create a real pub/sub mechanism on my custom objects, which ractive then explicitly subscribes to, would be fine. The problem is, as I noted in the OP, even when ractive is notified of a change via ractive.update(), it still doesn't know it should recompute the mustaches unless I use the fake argument hack. So it's not clear what callback ractive should be registering to make everything work.
I don't understand the inner-working of ractive well enough to do this, but I suspect what's needed is the ability to directly work with the _deps stuff, and manually trigger recomputes for expressions. If this sounds right, an example of how to accomplish it would be appreciated.
UPDATE 2 -- A decent solution
Here is a proof of concept for a not-too-hacky workaround.
The idea is to use ECMA5 properties to decorate your custom domain object, providing properties that delegate to the existing methods you want to use but which don't work inside ractive templates. The properties, otoh, work just fine.
So instead of <p>{{ curImageCaption() }}</p> we simply write <p>{{ imageCaption }}</p>, and then we decorate our custom domain object like so:
Object.defineProperty(mySlideshow, "imageCaption", {
configurable: true,
get: function() { return this.curImageCaption() },
set: function() { }
});
This decoration, a bit verbose in my demo, can easily be slimmed down by creating a helper method which accepts an object mapping your new ractive-friendly property names to names of existing methods on your object, and takes care of the above boilerplate for you.
NOTE: One drawback of this method is that you do have to call ractive.update() manually in your event handlers. I'd like to know if there's a way of getting around that. And if there is not, how big of a performance hit does this cause? Does it defeat the whole purpose of ractive's surgical updates?
Update 3 -- A better decent solution?
This pen takes yet another approach, in which link our custom domain model with ractive via a generic dispatcher object (an object that implements notify()). I think this is my favorite of the approaches so far....
It's similar to the official ractive adaptors, but we are using DI to pass our unofficial ractive adapter to our domain object, rather than wrapping our object. At first glance it might seem we are "coding to ractive," but in fact this is only partially true. Even if we were using another framework, we'd need to use some notification mechanism to broadcast changes to our view model so that views could react to it. This DI approach seems to require less boilerplate than official ractive adaptors, though I don't understand them well enough to know this for sure. It is not as completely general a solution as the official adaptors either.
Code from pen for posterity
HTML
<div id='output'></div>
<script id='template' type='text/ractive'>
<div class='slideshow'>
<div class='main'>
<a class='prev' on-tap='prev'><span>«</span></a>
<div class='main-image' style='background-image: url({{ curImageSrc() }});'></div>
<a class='next' on-tap='next'><span>»</span></a>
</div>
<div class='caption'>
<p>{{ curImageCaption() }}</p>
<p>Image index: {{ imgIndex }} </p>
</div>
</div>
</script>
JS
// Fix JS modular arithmetic to always return positive numbers
function mod(m, n) { return ((m%n)+n)%n; }
function SlideshowViewModel(imageData) {
var self = this;
self.imgIndex = 0;
self.next = function() { self.setLegalIndex(self.imgIndex+1); }
self.prev = function() { self.setLegalIndex(self.imgIndex-1); }
self.curImage = function() { return imageData[self.imgIndex]; }
self.curImageSrc = function() { return self.curImage().src; }
self.curImageCaption = function() { return self.curImage().caption; }
self.setLegalIndex = function(newIndex) { self.imgIndex = mod(newIndex, imageData.length); }
}
var mySlideshow = new SlideshowViewModel(
[
{ src: imgPath('problem.gif'), caption: 'Trying to work out a problem after the 5th hour' },
{ src: imgPath('css.gif'), caption: 'Trying to fix someone else\'s CSS' },
{ src: imgPath('ie.gif'), caption: 'Testing interface on Internet Explorer' },
{ src: imgPath('w3c.gif'), caption: 'Trying to code to W3C standards' },
{ src: imgPath('build.gif'), caption: 'Visiting the guy that wrote the build scripts' },
{ src: imgPath('test.gif'), caption: 'I don\'t need to test that. What can possibly go wrong?' }
]
);
var ractive = new Ractive({
el: '#output',
template: '#template',
data: mySlideshow,
magic: true
});
ractive.on( 'next', function(event) {
ractive.data.next();
});
ractive.on( 'prev', function(event) {
ractive.data.prev();
});
function imgPath(name) { return 'http://learn.ractivejs.org/files/gifs/' + name; }
I'll try and explain what's going on under the hood before presenting a possible solution:
Wrapping objects in magic mode
In magic mode, when Ractive encounters an unwrapped data descriptor of an object, it wraps it by replacing it with an accessor descriptor - the get()/set() functions. (More info on MDN, for those interested.) So when you do self.imgIndex = 1, you're actually triggering the set() function, which knows how to notify all the dependants of the imgIndex property.
The key word here is 'encounters'. The only way Ractive knows that it needs to wrap imgIndex is if we do ractive.get('imgIndex'). This happens internally because we have an {{imgIndex}} mustache.
So that's why the index property updates.
Dependency tracking with computed values
Within an ordinary template, you can have what basically amount to computed values, using the get() method:
<p>{{ curImageCaption() }}</p>
ractive = new Ractive({
el: 'body',
template: template,
data: {
images: images,
imgIndex: 0,
curImageCaption: function () {
var imgIndex = this.get( 'imgIndex' );
return this.get( 'images' )[ imgIndex ].caption;
}
}
});
Here, because we're calling ractive.get() inside the curImageCaption function, Ractive knows that it needs to rerun the function each time either images or imgIndex changes.
What you're in effect asking is a reasonable question: why doesn't retrieving the value of self.imgIndex in magic mode work the same as doing ractive.get('imgIndex')?
The answer comes in two parts: Firstly, I hadn't thought of adding that feature, and secondly, it turns out it doesn't work! Or rather, it's extremely fragile. I changed magic mode so that the get() accessor captured the dependency the same way ractive.get() does - but self.imgIndex is only an accessor descriptor (as opposed to a data descriptor) if Ractive has already encountered it. So it worked when we had <p>Image index: {{ imgIndex }} </p> at the top of the template, but not when it's at the bottom!
Normally the prescription would be fairly simple: use ractive.get() to make the dependency on self.imgIndex explicit inside curImageSrc() and curImageCaption(). But because you're using a custom viewmodel object, that's not ideal because it effectively means hard-coding keypaths.
A solution - creating a custom adaptor
Here's what I'd recommend - making an adaptor that works with the custom viewmodel object:
Ractive.adaptors.slides = {
filter: function ( object ) {
return object instanceof SlideshowViewModel;
},
wrap: function ( ractive, slides, keypath, prefix ) {
var originalNext, originalPrev;
// intercept next() and prev()
originalNext = slides.next;
slides.next = function () {
originalNext.call( slides );
ractive.update( keypath );
};
originalPrev = slides.prev;
slides.prev = function () {
originalPrev.call( slides );
ractive.update( keypath );
};
return {
get: function () {
return {
current: slides.curImage(),
index: slides.imgIndex
};
},
teardown: function () {
slides.next = originalNext;
slides.prev = originalPrev;
}
};
}
};
var ractive = new Ractive({
el: '#output',
template: '#template',
data: mySlideshow,
adaptors: [ 'slides' ]
});
This is a very simple adaptor, and it could probably be improved, but you get the gist - we're intercepting calls to next() and prev(), and letting Ractive know (via ractive.update()) that it needs to do some dirty checking. Note that we're presenting a facade (via the get() method of the wrapper), so the template looks slightly different - see this pen.
Hope this helps.
Maybe this is an academic exercise, and I'm new to Ractive, but it seems the problem lies in the template not having a context to the current image.
EDITED: Use current Image as a context block instead of looping through collection.
<div class='slideshow'>
{{#curImage}}
<div class='main'>
<a class='prev' on-tap='prev'><span>«</span></a>
<div class='main-image' style='background-image: url({{ src }});'></div>
<a class='next' on-tap='next'><span>»</span></a>
</div>
<div class='caption'>
<p>{{ caption }}</p>
<p>Image index: {{ imgIndex }} </p>
</div>
</div>
...
function SlideshowViewModel(imageData) {
...
self.curImage = imageData[self.imgIndex]
...
self.setLegalIndex = function(newIndex) {
self.imgIndex = mod(newIndex,imageData.length);
self.curImage = imageData[self.imgIndex]
}
}
This is using your original pen with just the key modifications. Here is new pen.
I would still move the buttons into an outer part of the template so the display in the middle could be made into a partial:
<div class='main'>
<a class='prev' on-tap='prev'><span>«</span></a>
{{#current}}
{{>partial}}
{{/}}
{{/current}}
<a class='next' on-tap='next'><span>»</span></a>
</div>
and encapsulate in Ractive.extend, but if ViewModel works for you...