I'm pretty new to Meteor and I'm struggling with arkham:comments-ui.
Here's a brief overview of what I'm doing. I hope it's not too detailed.
I'm subscribed to a published collection called 'articles'.
Articles = new Mongo.Collection("articles");
I'm iterating through this on load to display a preview of each article in the client via a template named 'articlepreview'.
Template.body.helpers({
articles: function(){
return Articles.find({}, {sort: {createdAt: -1}});
}
});
I've added the unique id of each article to a custom attribute (data-id) of the 'read more' button at the bottom of each preview.
<template name="articlepreview">
<h2>{{head}}</h2>
<p>{{{summary}}}</p>
<p class="readmore" data-id="{{_id}}">Read more</p>
</template>
I've added an event listener for this button which gets the custom attribute and calls a function to display the full article that was clicked as well as setting a Session variable with the unique id.
"click .readmore": function (event) {
event.preventDefault();
var articleID = event.target.getAttribute("data-id");
Session.set('articleUID',articleID);
Meteor.call("loadArticle", articleID);
}
This function populates a template named 'articlefull' via a helper; essentially I use Session.set to set a variable containing the text of the full article by using findOne with the unique ID that has been set.
HELPER:
Template.articlefull.helpers({
articlebody: function () {
return Session.get('articlebody');
},
uniqueid: function(){
return Session.get('articleUID');
}
});
TEMPLATE:
<template name="articlefull">
<div id="article">
<p>{{{articlebody}}}</p>
</div>
<div id="comments" class="comment-section">
{{> commentsBox id=uniqueid}}
</div>
</template>
Part of this template is a comment box. I'm setting the id of the comment box to match that of the article loaded, but something really odd happens at this point: the comments box allows me to type a comment and click 'Add', but once I do the comment briefly flashes on the screen and then disappears.
Any thoughts on what I'm doing wrong? If I pop {{uniqueid}} into the template just below the comment box it displays the right value, which means it is getting pulled through, but something is still going wrong...
PS: Please also tell me if I'm going about this in totally the wrong way. I'm sure there's a simpler way to do what I'm trying to but as I said, I'm new to this. Thanks!
Based on your detailed description about your implementation, I assume that this issue occurs due to a missing publish and subscribe function for your Comments collection.
Depending on your use case, you'll need to add Meteor.publish(name, func) and Meteor.subscribe(name, [arg1, arg2...], [callbacks]) functions.
Meteor.publish('allComments', function() {
return Comments.getAll();
});
Meteor.publish('articleComments', function(docId) {
return Comments.get(docId);
});
Meteor.subscribe('allComments');
Meteor.subscribe('articleComments', Session.get('articleUID'));
Related
I have a problem that has left me scratching my head for days. My knowledge of JavaScript is not great, I'm still learning, and to top it off I'm working on a project where I'm forced to use handlebars, Marionette and other stuff I'm not familiar with.
I have a handlebars template which looks like this:
{{#if images}}
{{#each images}}
<div class="image-thumbnail">
<i class="delete-button" style="cursor:pointer" id="delete-button-id" data-imgid="{{id}}"></i>
<img src="{{thumbnail}}"/></a>
</div>
{{/each}}
This all looks fine when the page loads, no problems there. If I put {{id}} between <i>{{id}}</i> then the value is output correctly to the browser. The problem I'm having is accessing that id from a pop-up which I'm generating using:
deleteImage: function(event) {
new DeleteView({model: new Backbone.Model()}).render().$el.appendTo('body');
},
I've tried adding the following:
new DeleteView({model: new Backbone.Model({ imgid: imageID })}).render().$el.appendTo('body');
And setting imgid using:
var imageElement = document.getElementById('delete-button-id');
var imageID = imageElement.getAttribute('data-imgid');
Unfortunately this only gets the last imgid and it's the same for every one. The page is basically a list of photos which are generated from the handlebars loop. There can be dozens on the page at once, and the imgid I get when the pop-up fires needs to be specific to the one I clicked.
My main view for the page where the images appear:
var ThumbnailView = Marionette.ItemView.extend({
template: getTemplate('profile/thumbnail'),
className: 'main-gallery',
events: {
'click .delete-button': 'deleteImage'
}
... more code follows ...
Can anyone tell me what I'm doing wrong? I need to get the correct dynamically-generated imgid from data-imgid="{{id}}" but I'm only getting the same one each time.
Any help would be greatly appreciated.
Thanks in advance!
The reason why you're getting the last ID each time is because you are selecting the element via the ID attribute:
document.getElementById('delete-button-id');
and unfortunately, your template has id="delete-button-id" on each of the <i> elements being rendered, thus when you try to do document.getElementById it will always return the last element with that attribute. The id attribute (unlike class) is intended to be unique, and according to W3C it must be unique in the document. If it isn't, it can cause issues like the one you're seeing (see here for more info: https://developer.mozilla.org/en-US/docs/Web/API/Element/id, https://www.w3.org/TR/html401/struct/global.html#h-7.5.2).
To fix this, remove the id attribute from the <i>. Also remove the dangling </a> as well. Your template should now look like:
{{#each images}}
<div class="image-thumbnail">
<i class="delete-button" style="cursor:pointer" data-imgid="{{id}}"></i>
<img src="{{thumbnail}}"/>
</div>
{{/each}}
Now when you're trying to select the element in deleteImage, you can do so via jQuery/Marionette by doing:
var imageId = this.$(event.currentTarget).data('imgid');
Or if you're keen on using plain JavaScript, you can obtain the ID like so:
var imageId = event.currentTarget.getAttribute('data-imgid');
And that should do it. You can play around with this JSBin to see how it will work:
https://jsbin.com/fotiqisoye/edit?html,js,output
the marionette way to restructure this solution would be to replace your each loop with a CompositeView, and feed it a collection of models, which will each render an ItemView, which will be represented in your dom as a single line-item of image.
when you click delete on that image, the ItemView receives the event, and the model.id passed into it will be correct.
more on this here: http://marionettejs.com/docs/v2.4.7/marionette.compositeview.html
hope this helps
Im new to knockoutJS and really loving it. I'm trying to build something very similar to this http://jsfiddle.net/mac2000/N2zNk/light/. I tried copying the code and adapting it to my need. The problem with that is that I get my data from the server using $.getJSON it seems that the jsfiddle example was made for a different format of data which just confuses me.
So instead of asking for help to find the issue with my code I rather take a different approach. Hopefully you guys wont mind. Im starting from scratch and trying to learn each steps so I know what im doing.
Here is my code so far, this works great to simply display my data on my table.
<script type="text/javascript">
function EmployeeModal() {
var self = this;
self.Employees = ko.observableArray([]);
$.getJSON("../../../../_vti_bin/listData.svc/GDI_PROD_Incidents?$filter=ÉtatValue%20ne%20%27Fermé%27&$orderby=PrioritéValue desc",
function (data) {
if (data.d.results) {
self.Employees(ko.toJS(data.d.results));
}
}
);
}
$(document).ready(function () {
ko.applyBindings(new EmployeeModal());
});
</script>
I made a template where each row has an edit button similar to the example but no fucntion of binding are done yet. Now what I would like to do is simply onlick pass the selected data to my modal and show my modal like so:
$('#myModal').modal('show');
This is the step im struggling the most with. Would any have any clear documentations for a noob or example, hints or any type of help I would take to get me in the right direction from here.
Assume you have them in a list like this:
<ul data-bind="foreach: Employees">
<li data-bind="text: fullName, click: showEmployee"/>
</ul>
What I'd recommend is to update your view model to look like this:
function EmployeeModal() {
var self = this;
self.Employees = ko.observableArray([]);
self.currentEmployee = ko.observable(null);
self.showEmployee = function(vm){
self.currentEmployee(vm);
$('#myModal').modal('show');
};
.... // rest of your view model here
}
The last piece will be using KO's with binding to wrap your modal
<div class="modal" id="myModal" data-bind="with: currentEmployee">
<h1 data-bind="text: fullName"></h1>
</div>
What this does is listen for the click event on an individual element and automatically pass the view model bound to that element to the click handler you defined. We're then taking that view model, storing it in its own observable and then showing the modal like normal. The key here is the with binding which only executes when there's data stored in the observable, and also sets the context of all nested code to be the employee stored in currentEmployee.
There's a lot there if you're new, but using a current style observable to track individual data from a list is a great paradigm to use with Knockout = )
P.S.. the http://learn.knockoutjs.com/ tutorials are really great to work through if you've yet to do so.
I am using Template.registerHelper to register a helper that would given some boolean value will output either class1 or class2 but also some initial class if and only if it was the first time it was called for this specific DOM element.
Template.registerHelper('chooseClassWithInitial', function(bool, class_name1, class_name2, initial) {
var ifFirstTime = wasICalledAlreadyForThisDOMElement?;
return (ifFirstTime)?initial:"" + (bool)?class_name1:class_name2;
});
I am having a hard time figuring out how to know if the helper was called already for this specific form element.
If I could somehow get a reference to it, I could store a flag in the data attribute.
Using Template.instance() one can get to the "template" instance we are now rendering and with Template.instance().view to the Blaze.view instance, however, what if we have more than one html element inside our template ?
Oh, you are doing it in the wrong direction.
If you want to manipulate the DOM, you should do it directly in the template, not the jquery way ;)
0. Helper
html
<template name="foo">
<div data-something="{{dataAttributeValue}}"></div>
</template>
js
Template.foo.helpers({
dataAttributeValue: function() {
return 'some-value';
}
})
If you cannot avoid accessing the DOM from outside, then there is Template.onRendered(callback), callback will be called only once, when the template is rendered for the first time.
1. Component style
<template name="fadeInFadeOut">
<div class="fade">{{message}}</div>
</template>
Template.onRendered(function() {
// this.data is the data context you provide
var self = this,
ms = self.data.ms || 500;
self.$('div').addClass('in'); // self.$('div') will only select the div in that instance!
setTimeout(function() {
self.$('div').removeClass('in')
self.$('div').addClass('out')
}, ms );
});
Then you can use it somewhere else:
<div>
{{>fadeInFadeOut message="This message will fade out in 1000ms" ms=1000 }}
</div>
So you would have a reusable Component..
The way I solved it for now was to manually provide some kind of global identifier, unique to that item, this is hardly the proper way, if anyone has suggestions let me know.
let chooseClassWithInitialDataStore = {};
Template.registerHelper('chooseClassWithInitial', function(bool, class_name1, class_name2, initial, id) {
if(!chooseClassWithInitialDataStore[id]){
chooseClassWithInitialDataStore[id] = true;
return initial;
}
return (bool)?class_name1:class_name2;
});
To be used like:
<div class="ui warning message lowerLeftToast
{{chooseClassWithInitial haveUnsavedChanges
'animated bounceInLeft'
'animated bounceOutLeft'
'hidden' 'profile_changes_global_id'}}
">
Unsaved changes.
</div>
Regarding this specific usage: I want to class it as 'animated bounceInLeft' haveUnsavedChanges is true, 'animated bounceOutLeft' when its false, and when it is first rendered, class it as 'hidden' (that is, before any changes happen, so that it doesnt even display when rendered, thus, the need for the third option, however this isnt a question about CSS, but rather about Meteor templateHelpers).
I have a set of Polymer paper-toggle-buttons that make elements in a graph (using c3) appear and disappear. I've had some success picking up the event change with the following code:
HTML:
<div class="graph-sets-element">
<core-label horizontal layout>
<paper-toggle-button checked id="graph1"></paper-toggle-button>
<h4>Graph 1</h4>
</core-label>
</div>
<div class="graph-sets-element">
<core-label horizontal layout>
<paper-toggle-button checked id="graph2"></paper-toggle-button>
<h4>Graph 2</h4>
</core-label>
</div>
Javascript:
[].forEach.call(
document.querySelectorAll('paper-toggle-button'),
function(toggle){
toggle.addEventListener('change',function() {
if(this.checked) {
chart.show('graph1');
}
else {
chart.hide('graph1');
}
});
}
);
The problem with my current approach is that any press of a paper-toggle-button does the same thing (hide/show the plotted graph1 line). Is there a way to grab the id of the paper-toggle-button pressed and pass it into chart.show()
Part of the challenge is that these toggle buttons are dynamically created when the user uploads new data into the graph. This means I don't necessarily know their names until the paper-toggle-button is pressed, and can't hard code each one's id.
You could access the caller element id with:
this.id
i had to do something close to this a while back maybe the approach i used will help you.
on the element you are clicking (in my case was a paper-item) assign a random attribute name (i used data) so that my element looked something like.
<paper-toggle-button on-change="{{doFunction}}" id="{{id}}"></paper-toggle-button>
then in my function i made sure to send the sender to it. that looked something like
doFunction: function (event, detail, sender) {
var id = sender.attributes.id.value;
// do something with the id
}
working with event listener is a little diff but similar. it would look like this
toggle.addEventListener('change', function (event) {
var id = event.path[0].id;
// do something with id
});
hope that helps.
i want to get the text of div using angularjs . I have this code
<div ng-click="update()" id="myform.value">Here </div>
where as my controller is something like this
var myapp= angular.module("myapp",[]);
myapp.controller("HelloController",function($scope,$http){
$scope.myform ={};
function update()
{
// If i have a textbox i can get its value from below alert
alert($scope.myform.value);
}
});
Can anyone also recommand me any good link for angularjs . I dont find angularjs reference as a learning source .
You should send the click event in the function, your html code should be :
<div ng-click="update($event)" id="myform.value">Here </div>
And your update function should have the event parameter which you'll get the div element from and then get the text from the element like this :
function update(event)
{
alert(event.target.innerHTML);
}
i just thought i put together a proper answer for everybody looking into this question later.
Whenever you do have the desire to change dom elements in angular you need to make a step back and think once more what exactly you want to achieve. Chances are you are doing something wring (unless you are in a link function there you should handle exactly that).
So where is the value comming, it should not come from the dom itself, it should be within your controller and brought into the dom with a ng-bind or {{ }} expression like:
<div>{{ likeText }}</div>
In the controller now you can change the text as needed by doing:
$scope.likeText = 'Like';
$scope.update = function() {
$scope.likeText = 'dislike';
}
For angular tutorials there is a good resource here -> http://angular.codeschool.com/
Redefine your function as
$scope.update = function() {
alert($scope.myform.value);
}
A better way to do it would be to use ng-model
https://docs.angularjs.org/api/ng/directive/ngModel
Check the example, these docs can be a bit wordy