Meteor session trying to trigger template render - javascript

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.

Related

How to send data from v-for to event handler in Vue?

I am having problems with sending the right data to the event handler of a subcomponent in my Vue component.
Here is my code:
<template>
<div>
<div v-for="item in [1, 2, 3]">
<div #click=test(item)>test</div>
<ConfirmModal v-if="showModal" #confirmed=test(item) />
<button v-on:click="showModal = true" ></button>
</div>
</div>
</template>
<script>
import ConfirmModal from 'ConfirmModal'
export default
{
components: {ConfirmModal},
data()
{
return {showModal: false}
},
methods:
{
test(item)
{
console.log(item);
this.showModal = false;
},
},
}
</script>
And here is the code for a subcomponent:
<template><div class="modal" #click.self="$emit('confirmed')">subtest</div></template>
So, basically, I here have an array of numbers and am using the v-for to iterate over an array.
For each item one test label, one subcomponent (containing a subtest label) and one button is created. When button is clicked, subcomponent is shown. When I click on the subcomponent (subtest label) it disappears.
div is a standard component which emmits #click events when it is clicked upon.
confirmModal is a very simple component which listens for #click events and redirects them into #confirmed events.
So, this code generates 3 times 3 items. When I click on any of the labels, test method is called which logs the value of the item.
When I click on any of the test labels, I get the correct value in the console, that is, 1 for the first item, 2 for the second item and so on.
But, when I click on any of the subtest labels, I allways get the 3, whichever link I clicked. (If I change the order of items in the array, then I allways get whichever item I put on the last place in the array).
I do not understand what is going on here, it is the same method called on the same way. Why is 3 allways logged in console when I click on the subtest label?
If you wonder what is class=modal in my subcomponent used for, I have no idea. This is a skeleton of two components in a very complex project consisting of thousands of files built on top of the Liferay, and there are more then ten files where is .modal defined in many directories. So I am not sure from which file does this particular component load this class. I did not throw it out of this skeleton code just because when I remove this class from the div in the subcomponent, everything works fine. I have no idea why. How can a class stop the correct data from being received? I do not get this.
I know that this bug may not be reproducable, because I did not provide the definition of the .modal style, but I posted it anyway, because I hope that you can see something else in the code what is wrong or propose another way how to execute the test method on #confirmed event.
I am using Vue 2.

Vue.js v-for generated html block capturing collapse state with two way data binding

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])

Meteor : Blink data when updated

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);
}
});
});

Pass information to bootstrap modal from Angular.js controller

Simplified problem
I have a store. For a product to be included in the store there needs to be a shelf for it. So, to add a new product to the store the workflow is:
Add a shelf
Add product to that shelf
(The workflow can not be changed)
Realization
The shelf is realized by a row in a table, which in turn is controlled by an Angular.js controller (each shelf is an object in an array). To add an product the user selects "create product" in a drop-down menu that is present on each row. This will show an bootstrap modal where I have from a controller added a tab for each product that is possible to add (since each product needs configuration :) ) , then when the user presses a "create" button in the modal a JavaScript method is called interfacing a REST interface to add the product (the UI is updated by a Socket.io event send from the server when the product has been added successfully.
Problem
The JavaScript method (CreateProduct) needs to now what row (R) was affected as well as what tab (T) was selected so that the "onclick" method for the button is CreateProduct(R, T);
My current solution is pretty ugly imho, I have two global variables for R and T, then I use jQuery to capture show event and tab event from the modal, the link in the dropdown has a field "data-row-id" that is identifying the row
HTML (Jade) snippet from dropdown menu:
a(data-toggle="modal", href="#createProduct", data-row-id="{{row.RowID}}") Create Product
JavaScript:
var R = null;
$('#productModal').on('show.bs.modal', function(e) {
R = $(e.relatedTarget).data('row-id');
});
var T = null;
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
T = e.target.text;
});
I hope there is a better solution to this, I probably am just thinking a bit upsidedown due to inexperience with Angular.js , perhaps there is a way to pass these through the model? Perhaps add these to the modal controller, but then, how to pass the row? My dream would be something like this on the button code
button(type="button", class="btn btn-default", data-dismiss="modal", ng-click="storeCtrl.CreateProduct({{modalCtrl.shelf, modalCtrl.Product)") Create Product
I found a better way (at least I don't need to use jQuery) using ModalService
I created a CreateProductModalController having a variable selectedProduct, this is set on a ng-click event in the tab
ul(class="nav nav-pills", id="tabContent")
li(ng-repeat="prod in products", ng-class="{active: $index == 0}", ng-click="activateTab(prod.name)")
a(href="#{{prod.name}}", data-toggle="tab") {{prod.name}}
The ModalService is called with the rowID that was clicked.
The only problem I have now is that all must be in $scope, I want it to be more encapsulated

Meteor Select Box - mark option as selected depending on data context

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);
}
});

Categories

Resources