I have page wich displays time-dependent text-snippets from a json with Ractive.js.
After initialisation, I want to check every minute, if the stop-time, which is stored in the json, is due. Then the text-snippet should be hidden.
Things I've tried:
{{#if time_due > Date.time()}}
do whatever
{{/if}}
The above doesn't work for me.
I also played around with computed properties of Ractive.js, but that didn't work either.
Has somebody a good idea and/or can help me?
Ractive won't recompute expressions (like time_due > Date.now()) unless it thinks something has changed. The easy solution is to assign the current time to a data property and update it periodically:
var ractive = new Ractive({
el: 'main',
template: '#template',
data: {
timeNow: Date.now(),
displayUntil: Date.now() + 5000
}
});
setInterval( function () {
ractive.set( 'timeNow', Date.now() );
}, 500 );
<script src='http://cdn.ractivejs.org/edge/ractive.js'></script>
<main></main>
<script id='template' tyle='text/html'>
{{#if timeNow < displayUntil}}
<p>now you see me</p>
{{else}}
<p>now you don't</p>
{{/if}}
<p>(time remaining: {{displayUntil - timeNow}}ms)</p>
</script>
Related
i have simple html code :
<script src="vue.js"></script>
<div id="app1">
<h1 ref="heading">{{ title }}</h1>
<button v-on:click="change" ref="myButton">Change Title</button>
</div>
<script src="app.js"></script>
and this is app.js
let v1=new Vue({
el: '#app1',
data: {
title: 'The VueJS Instance'
},
methods: {
change: function() {
this.title ='The VueJS Instance (Updated)';
}
},
watch: {
title: function(value) {
alert('Title changed, new value: ' + value);
}
}
});
v1.$refs.heading.innerText="BEFORE Change";
As you can see i am setting innertext for "h1 heading" element. After that i am clicking on button which will call change method, after that call it opens a new dialog window saying "Title changed, new value: The VueJS Instance (Updated)" but the page (h1 heading) not updated it still remains same "BEFORE Change", What is wrong in my code, i think it should update the heading (i am using Vue.js v2.6.11 version). Thanks
You should never update the DOM of a Vue template directly - otherwise the synchronization between the virtual DOM and the real DOM will be lost and you will get all kinds of strange errors or awkward behavior.
Noone can save you from shooting yourself in the foot. Remove the last statement in your code and you will see that the title is properly updated.
I'm using Vue.Js for a survey, which is basically the main part and the purpose of the app. I have problem with the navigation. My prev button doesn't work and next keeps going in circles instead of only going forward to the next question. What I'm trying to accomplish is just to have only one question visible at a time and navigate through them in correct order using next and prev buttons and store the values of each input which I'll later use to calculate the output that will be on the result page, after the survey has been concluded. I've uploaded on fiddle a short sample of my code with only two questions just to showcase the problem. https://jsfiddle.net/cgrwe0u8/
new Vue({
el: '#quizz',
data: {
question1: 'How old are you?',
question2: 'How many times do you workout per week?',
show: true,
answer13: null,
answer10: null
}
})
document.querySelector('#answer13').getAttribute('value');
document.querySelector('#answer10').getAttribute('value');
HTML
<script src="https://unpkg.com/vue"></script>
<div id="quizz" class="question">
<h2 v-if=show>{{ question1 }}</h2>
<input v-if=show type="number" v-model="answer13">
<h2 v-if="!show">{{ question2 }}</h2>
<input v-if="!show" type="number" v-model="answer10">
<br>
<div class='button' id='next'>Next</div>
<div class='button' id='prev'>Prev
</div>
</div>
Thanks in advance!
You should look at making a Vue component that is for a survey question that way you can easily create multiple different questions.
Vue.component('survey-question', {
template: `<div><h2>{{question.text}}</h2><input type="number" v-model="question.answer" /></div>`,
props: ['question']
});
I've updated your code and implemented the next functionality so that you can try and create the prev functionality. Of course you should clean this up a little more. Maybe add a property on the question object so it can set what type the input should be. Stuff like that to make it more re-useable.
https://jsfiddle.net/9rsuwxvL/2/
If you ever have more than 1 of something, try to use an array, and process it with a loop. In this case you don't need a loop, but it's something to remember.
Since you only need to render one question at a time, just use a computed property to find the current question, based on some index. This index will be increased/decreased by the next/previous buttons.
With the code in this format, if you need to add a question, all you have to do is add it to the array.
https://jsfiddle.net/cgrwe0u8/1/
new Vue({
el: '#quizz',
data: {
questions:[
{question:'How old are you?', answer: ''},
{question:'How many times do you workout per week?', answer: ''},
],
index:0
},
computed:{
currentQuestion(){
return this.questions[this.index]
}
},
methods:{
next(){
if(this.index + 1 == this.questions.length)
this.index = 0;
else
this.index++;
},
previous(){
if(this.index - 1 < 0)
this.index = this.questions.length - 1;
else
this.index--;
}
}
})
Following an answer to my question on debouncing I am wondering if vue.js and lodash/underscore are compatible for this functionality. The code in the answer
var app = new Vue({
el: '#root',
data: {
message: ''
},
methods: {
len: _.debounce(
function() {
return this.message.length
},
150 // time
)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://unpkg.com/underscore#1.8.3"></script> <!-- undescore import -->
<div id="root">
<input v-model="message">Length: <span>{{ len() }}</span>
</div>
indeed holds on the execution of my function when there is continuous input, but when it is finally executed after some inactivity, the input for function() seems to be wrong.
A practical example after starting the code above:
quick sequence of characters, then no activity:
One extra character (b) added, and no activity -- the length is updated (but wrongly, see below)
Erase all the characters with Backspace in a quick sequence:
Add one character:
It looks like the function is ran on the last but one value of message.
Could it be that _.debounce handles the vue.js data before it is actually updated with the <input> value?
Notes:
tested with both lodash and underscore, with the same results (for both debounceand throttle functions).
I also tested it on JSFiddle in case there would be some interference with the SO snippet
Here's an improved version of #saurabh's version.
var app = new Vue({
el: '#root',
data: {
message: '',
messageLen: 0
},
methods: {
updateLen: _.debounce(
function() {
this.messageLen = this.message.length
}, 300)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://unpkg.com/underscore#1.8.3"></script> <!-- undescore import -->
<div id="root">
<input v-model="message" v-on:keyup="updateLen">Length: <span>{{ messageLen }}</span>
</div>
Why this is happening is because Vue invokes methods only when vue variables used in the methods changes, if there are no changes in the vue varaibles, it will not trigger those methods.
In this case as well, once we stop typing, it will continue to show last called method's output and will only show again once you enter the input again.
One alternate approach if you dont want to call an function on all inputs, you can call a mehtod on blur event, so method will be invoked only when focus goes out of input field, like following:
var app = new Vue({
el: '#root',
data: {
message: '',
messageLen: 0
},
methods: {
updatateLen:
function() {
this.messageLen = this.message.length
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.6/vue.js"></script>
<script src="https://unpkg.com/underscore#1.8.3"></script> <!-- undescore import -->
<div id="root">
<input v-model="message" v-on:blur="updatateLen">Length: <span>{{ messageLen }}</span>
</div>
I was thinking on rewriting our legacy app which is using pure jQuery. It renders log data which it gets by websocket, it shows only last 100 records by removing old ones and appending new ones.
Because rendering speed matters, I've tried first to render random incoming data and Ractive's twice slower than our jQuery code. By benchmarks jQuery renders 1000 records in 15 seconds and Ractive's version in 30 seconds. ( our backend code pushes each event with 0.01 s delay )
Thus I wonder is there any tweak settings? The code I use is simple:
var LogApp = Ractive.extend({
template: '#items',
init: function() {
var self = this;
socket.bind("logs", function(data_raw) {
var data = JSON.parse(data_raw);
if (self.data.items.length > 100) {
self.pop('items');
}
self.unshift('items', data);
});
}
});
var ractive = new LogApp({
el: react,
data: {
items: []
}
});
<script id='items' type='text/ractive'>
{{#each items:i}} {{>item}} {{/each}}
</script>
<script id='item' type='text/ractive'>
<tr>
<td>{{id}}</td>
<td>{{log_level}}</td>
<td>{{log_message}}</td>
</tr>
</script>
With Ractive 0.7, performance is now better. It performs at ~11 seconds, with each item being about 10ms (See http://jsfiddle.net/aqe53ocm/).
You can also try using a merge instead of two operations, pop and unshift:
var copy = self.get('items').slice();
if (copy.length > 100) {
copy.pop();
}
copy.unshift(data);
self.merge('items', copy);
See http://jsfiddle.net/56hfm4bt/.
For example and timings having the dev tools open will impact times because it's console.time logging each item, so try without.
For the curious, there are changes coming in 0.8 that will spead this up to ~1ms per item.
I am quite new with Meteor but have really been enjoying it and this is my first reactive app that I am building.
I would like to know a way that I can remove the .main element when the user clicks or maybe a better way would be to remove the existing template (with main content) and then replace with another meteor template? Something like this would be simple and straightforward in html/js app (user clicks-> remove el from dom) but here it is not all that clear.
I am just looking to learn and for some insight on best practice.
//gallery.html
<template name="gallery">
<div class="main">First run info.... Only on first visit should user see this info.</div>
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
//gallery.js
firstRun = true;
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
firstRun = false;
}
})
if (Meteor.isClient) {
function showSelectedPhoto(photo){
var container = $('#gallery');
container.fadeOut(1000, function(){
Session.set('selectedPhoto', photo);
Template.gallery.rendered = function(){
var $gallery = $(this.lastNode);
if(!firstRun){
$(".main").css({display:"none"});
console.log("not");
}
setTimeout(function(){
$gallery.fadeIn(1000);
}, 1000)
}
});
}
Deps.autorun(function(){
selectedPhoto = Photos.findOne({active : true});
showSelectedPhoto(selectedPhoto);
});
Meteor.setInterval(function(){
selectedPhoto = Session.get('selectedPhoto');
//some selections happen here for getting photos.
Photos.update({_id: selectedPhoto._id}, { $set: { active: false } });
Photos.update({_id: newPhoto._id}, { $set: { active: true } });
}, 10000 );
}
If you want to hide or show an element conditionaly you should use the reactive behavior of Meteor: Add a condition to your template:
<template name="gallery">
{{#if isFirstRun}}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{/if}}
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
then add a helper to your template:
Template.gallery.isFirstRun = function(){
// because the Session variable will most probably be undefined the first time
return !Session.get("hasRun");
}
and change the action on click:
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
Session.set("hasRun", true);
}
})
you still get to fade out the element but then instead of hiding it or removing it and having it come back on the next render you ensure that it will never come back.
the render is triggered by changing the Sessionvariable, which is reactive.
I think using conditional templates is a better approach,
{{#if firstRun }}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{else}}
gallery ...
{{/if}}
You'll have to make firstRun a session variable, so that it'll trigger DOM updates.
Meteor is reactive. You don't need to write the logic for redrawing the DOM when the data changes. Just write the code that when X button is clicked, Y is removed from the database. That's it; you don't need to trouble yourself with any interface/DOM changes or template removal/redrawing or any of that. Whenever the data that underpins a template changes, Meteor automatically rerenders the template with the updated data. This is Meteor’s core feature.