I hope someone can help with this because it's quite frustrating.
I have examined all animation packages around, but they all seem to be activated by user action, for example clicking an item. What if the change is triggered from the server?
I want to display an animation when my Collection is changed from non-Meteor source. I have a Java demo app that can call a Meteor server method, which in turn updates the Collection.
The changes are displayed in the client so I know the basics are working, but how shall I code the client so that it for examples blink when changed?
Think "Stock market app".
A simple example that blinks an item in red when updated would be great. Also, how do I detect when items are inserted and deleted?
Let's assume you have a template for a list of favorite stocks, and a template for each stock:
<template name="favorites">
{{#each favorites}}
{{> stock}}
{{/each}}
</template>
<template name="stock">
<div class="row" id="{{_id}}">
<div class="col-xs-6 symbol">{{symbol}}</span>
<div class="col-xs-6 quote">{{quote}}</span>
</div>
</template>
In the onRendered template event you can create an observer to watch for changes to a specific query. Just use the same query you use to generate the favorites list. When a change occurs for any record, find it using jQuery and animate the highlight effect:
Template.favorites.onRendered(function() {
var query = Favorites.find({}, { sort: { symbol: 1 } });
var handle = query.observeChanges({
changed: function (id, fields) {
//this will highlight the row of the updated favorite, using the id from the Favorites collection
$('#' + id).effect('highlight', {}, 1500);
}
});
});
Related
I'm working with vue.js and I'm wondering if I can save the amount as input via buttons, so that when a user comes back to the pen, they can see what the current number recording is.
Essentially I just want the virtual DOM to stay updated with the current number, but I don't know how to go about this.
My code:
var vm = new Vue ({
el: "#app",
data: {
amount: 0
},
methods: {
addOne: function(){
this.amount++;
},
subtractOne: function(){
this.amount--;
}
}
});
<div id="app">
<h2>Amount of Red Cars I Saw Today</h2>
<p id="times">{{amount}}</p>
<button id="plus-one" #click.prevent="addOne">Add One</button>
<button id="minus-one" #click.prevent="subtractOne">Subtract One</button>
</div>
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
If you want feel free to fork the pen here: https://codepen.io/anicaise/pen/vRRaoB
To do what you are attempting both you and others would have to be connecting to the same data somewhere. So unfortunately, without contacting and some outside source to store your updates you will be unable to do what you are trying above.
The two main solution for your problem would be saving to a database or saving to a file. Without a backend the easiest solution would be to use one of the many API based database services where you can pull and push data to one repository so everyone visiting your site will get and set the same data.
My favorite is firebase because they have a free option so you can play around with it.
And here is an article on how to get it set up with Vue.js.
I'm trying to capture click action state (for collaps function) within html v-for generated block. To achieve this, I'm using declared data table, and it looks like state is being captured correctly. Below I'm ataching simplified v-for section, where I display state after click action. Displayed state is always false, even though after click, console.log shows table fields changes. Can someone please try to explain me why is that, and how to achieve what is expected here? I'm pretty new to vue, and must be doing something wrong...
<div v-for="address in userAddressData">
<a #click="expandControl(address.id)">
Address {{expandArea[address.id]}}
</a>
</div>
...
export default {
data () {
return {
userAddressData: '',
expandArea: [false,false,false]
}
},
methods: {
expandControl (id) {
this.expandArea[id] = !this.expandArea[id]
console.log(this.expandArea)
}
},
...
If I understood right, the problem you are having is that the template is not being updated according to the data, as we can see on this fiddle: https://jsfiddle.net/gen1kL3y/
This is issue is related to how Vue Reactivity works.
You can find another similar question here and I made a working JS fiddle for you here: https://jsfiddle.net/vz11zdjc/
Basically, instead of setting the value as this.expandArea[id] = !this.expandArea[id], you should (among other options) use Vue.set(this.expandArea, id, !this.expandArea[id])
I have been trying to figure out how to set the 'selected' attribute on an option within a select box and have fallen short today. Any help would be appreciated.
What I'm trying to accomplish:
When a user chooses an option and hits submit, the data saves to a new mongo document and the new entry is shown on the screen (this part works). Upon browser refresh, the state is lost (due to no selected attribute being set) and the select box goes back to the first option. I need to figure out how to set the selected state depending on the value the user has saved on that particular document. On this page there are many of the same select boxes allowing the user to choose different options for other entries.
References (tried to implement with no luck):
Check for equality in Spacebars?
https://github.com/meteor/meteor/wiki/Using-Blaze#conditional-attributes-with-no-value-eg-checked-selected
How do I indicate 'checked' or 'selected' state for input controls in Meteor (with spacebars templates)?
select box template:
<template name="tEdit">
<div class="form-group">
<div class="controls">
{{> fFEdit}}
</div>
</div>
</template>
select box option template:
<template name="fFEdit">
<select class="form-control" name="fFSelect">
{{#each fFSelect}}
<option value="{{fId}}">{{fF}}</option>
{{/each}}
</select>
</template>
setting data context
Template.fFEdit.helpers({
fFSelect: function() {
var fFArray = [
{ fF: "foo", fId: 0 },
{ fF: "bar", fId: 1 },
{ fF: "buzz", fId: 2 }
];
return fFArray;
}
});
If you want state to be persisted across browser refreshes, you should be able to either fetch it from the server or from something like the Meteor Session.
The second thing you'd need is a helper to get the selected state. Conveniently, Meteor makes the data context available under this (for an each loop, this context is an individual item).
Template.userEdit.helpers({
userSelected: function() {
if(this.id === Session.get("selectedUserId")) {
return "selected";
}
}
});
This assumes you've set the Session variable previously with something like Session.set("selectedUserId", user._id).
Then in your template:
<template name="userEdit">
<select class="form-control" name="userSelect">
{{#each users}}
<option {{userSelected}}>{{username}}</option>
{{/each}}
</select>
</template>
(Note: The selected state can not actually be seen by inspecting the DOM with an element inspector.)
I'm not sure if this is the best answer to my problem, though this is what I came up with to add the selected attribute to the saved option. I welcome anyone else with a better/Meteor solution for this.
When the user clicks the edit link, I find the inputs that need to be updated and assign the values onClick. Ideally I'd rather have this already done on pageLoad, though feel that that solution may impact the initial pageLoad time.
Template.tItem.events({
'click .edit-t-item': function(e) {
e.preventDefault();
var $editTWrapper = $(e.currentTarget).next('.edit-t-wrapper');
$editTWrapper.find('[name=tNEdit]').val(this.tName);
$editTWrapper.find('[name=fFSelect]').val(this.fFId);
}
});
currently i'm having trouble with meteor sessions and how my code triggers it to render a template. Currently I have a session that sets its ._id to whatever is clicked.
Template.sidebar.events({
/* on click of current sidecat class change to sidecat selected */
'click .sidecat': function (event) {
Session.set("selected_project", this._id);
}
});
And I have it add a css class if selected_player equals the div.
Template.sidebar.sidebarselected = function () {
return Session.equals("selected_project", this._id) ? "sidebarselected" : '';
}
Now I render a template when there is sidebarselected and another class present. On click of the item it renders the template.
Template.sidebar.projectselected = function() {
var find = Session.get("selected_project");
var find2 = ($("#"+find).attr('class'));
/* if exists return true render the template */
if (find2 == 'project sidebarselected')
{
return true
}
/* else don't render it return null */
else {
return null
}
};
Everything up until now works great.
Now I have a Button that creates a new item in the list and makes it the selected item. This is when the trouble occurs. When I create a new Item the list item is rendered and given the class sidebarselected but it does not render the template which should be called. It does render the template when I click an item. But not when using the button to create a new list item. It becomes selected but does not render the template. Here is the code for that.
Template.sidebar.events({
/* add bar settings menu functions for clicks */
'click #newProject': function(event){
var create = NewProject();
Session.set("selected_project", create);
},
This is the NewProject Function
/* add a new project to the side bar */
function NewProject() {
id = Aprojects.insert({
name: "New Project",
type: "project"
});
doc = Aprojects.findOne({_id:id});
return doc._id;
}
Ok there is everything. Item is created when I click on button, class is added but template is not rendered. This is all of the javascript, if html is need let me know and I will provide it.
Let me add some details and html template stuff. But anyways what does work is
you can select a project in the list
when you select a project (by clicking on it), it's given a classname to indicate that it's selected
when you click "new project" you want to add an item to the list and immediately select it
That all works correctly. The problem is when clicking the new project button. It still selects the newly made project properly and gives it a class of selected. What doesn't happen is if it finds that the selectedbar class also has a class of project with it. It renders a separate template (projectpicked). Projectpicked shows up when items are clicked. Not when #newproject is clicked. Even through it ends up selected it does not show projectpicked template. Let me see if the code can do more of the talking.
<body>
{{> sidebar}}
</body>
<template name="sidebar">
{{#if aproject}}
{{#each aproject}}
<div class="sidecat {{sidebarselected}} project" id="{{divids}}">
{{name}}
</div>
{{/each}}
{{/if}}
{{#if projectselected}}
{{> projectpicked}}
{{/if}}
</template>
<template name="projectpicked">
project was picked from sidebar
</template>
The reasoning behind the application logic and DOM structure combo is that I have other things besides projects like categories. I figured using session gets the id and then we figure out what class it is like project or category and display a different template based off of what class it is. Don't know if its the best way but it's what I came up with. Open to suggestions. If somethings not clear not me know and I'll try to explain it. Thanks for the help.
I would rewrite the projectselected helper to not depend on a HTML classname to execute its logic, but to depend on a Session value. You're using Session correctly in most of your code, but it seems weird to then couple your application logic to the DOM structure.
The way your code works at the moment (without seeing your HTML template - you should post that too!), it looks like you want it to do the following:
you can select a project in the list
when you select a project (by clicking on it), it's given a classname to indicate that it's selected
when you click "new project" you want to add an item to the list and immediately select it
Here's how I'd solve that scenario:
Your template
<body>
{{> sidebar}}
</body>
<template name="sidebar">
<ul>
{{#each items}}
<li class="{{selected}} project" id="id_{{_id}}">{{_id}}</li>
{{/each}}
{{> newproject}}
</ul>
{{> projectpicked}}
</template>
<template name="newproject">
<li id="newproject">Create a new project</li>
</template>
<template name="projectpicked">
{{#if projectpicked}}
A project was picked!
{{/if}}
</template>
Your javascript
// When clicking an item in the list, remember which one we just clicked
Template.sidebar.events({
"click li": function() {
Session.set("selected_project", this._id);
}
});
// Add the selected class to the item that was remembered
// by the session when we clicked on it
Template.sidebar.selected = function() {
return Session.equals("selected_project", this._id) ? "selected" : "";
}
Template.sidebar.items = function() {
// or whatever code you have to return the list of items
return Projects.find();
}
// when clicking the new project button, create the new project
// and remember the id, then store that as the selected value in the session.
Template.newproject.events({
"click #newproject": function() {
var newprojectId = newProject(); // do your thing
Session.set("selected_project", newprojectId);
}
});
Template.projectpicked.projectpicked = function() {
return !Session.equals("selected_project", undefined);
}
The key here is to only set the selected_project Session value when you click on something. That happens twice; when you click an item to select it, and when you click the new project.
To get your template to draw the selected item, all you need to do in Meteor is describe when an item is selected: an item is selected when some session value matches its id. That's all.
I haven't tested this code and it's obviously incomplete, but hopefully this points you in the right direction for refactoring your code a bit.
Well, after getting rather frustrated and rewriting most of the code over again I found the problem. In my projectpicked template instead of using Session.get and then using it to find the class. In stead I just have a
if ($('.sidecat.project').hasClass('sidebarselected')) {
return true
}
While the original was:
if (find2 == 'project sidebarselected')
{
return true
}
So I used some jquery instead of directly comparing variable find2 to a string.
I am building a site and I have a list of status updates and I want to allow the users to write comments for each of of the items in the list
However I am trying to implement a UI similar to the way stack overflow works
specifically the collapsible comment form / list where a user clicks on add comment on the specific status update in the list, and below that item in the list the comment entry form shows up along with the specific comments already posted.
How do I accomplish this using Jquery?
Note: looking also for the markup example another words a working sample. Thanks
And yes if you could show Async postback that would be nice too
To load the content you can just hook up a click event to populate a div using the load method.
For example in the view you could have something like:-
<%= Html.ActionLink("Comments", "CommentList", "Questions", new { Id = this.ViewData.Model.Id }, new { id = "commentLink" })%>
<div id="commentContainer" style="display:none;">
Loading...
</div>
while the javascript to hook everything up would be:-
$(function() {
$("#commentLink").click(function() {
$("#commentContainer").toggle();
if ($("#commentContainer").is(":visible")) {
$("#commentContainer").load($(this).attr("href"));
} else {
$("#commentContainer").html("Loading..."); //Or just leave it as is...
}
return false; //Prevent default action
});
});
The quick approach (for just showing / hiding the comment area) would resemble something like this:
$(function(){
$('#id_of_element_to_use_for_click').click(function(){
$('#id_of_comment_area').slideToggle();
});
});
The jQuery site will provide you with doco on different approaches such as fades, slides or other combined animations.
Your "Comment Area" I've used in the example here would likely be a <div> tag that contains your existing comments, plus whatever textarea or text input box you wanted users to type their answers into.
Do you need to do an asynchronous postback?