Meteor to not rerender when certain fields change - javascript

Meteor re renders a view when a document changes.
Template.story.data = function() {
var storyID = Session.get('storyID');
var story = Stories.findOne({
_id: storyID
})
if (!story)
return;
return story;
};
Here's a template helper for the story template, getting a story from the Stories document.
When fields like story.title changes I want the template to rerender. But when fields like story.viewingusers change I don't want to rerender the template. Anyway to make that happen?
The specific problem that triggered the question was solved by setting the publish function to not publish those fields. However that solution doesn't work for every usecase and a general solution is needed.

What you're looking for is the #constant template helper.
What i'd do in your case is wrap the popover markup in a constant block, then update the content in that block manually in the story.rendered function.
So something like this:
story.html:
<template name="story">
...
{{#constant}}
<!-- Popover markup here -->
{{/constant}}
...
</template>
client.js:
Template.story.rendered = function(){
//Update logic here
}

Related

How can I render template with context in an Angular component?

I'm using ng-template in Angular8 to make a plural translation for displaying a notification to the user from the component but I cannot get full generated inner HTML of the template before attaching to DOM because context not bound yet.
How can I render a template with its context and get its inner HTML for this purpose?
I tried to use ViewContainerRef to render the template and attach it DOM and it works fine but I don't want to attach something to DOM and read it later.
Template:
<ng-template #activeDeactiveSuccessMessage let-card="card">
<span i18n="##card.notification">Notification</span>
<span i18n>{card.lockStatus, select,
LOCKED {Card number ending with {{card.number}} has been deactivated.}
UNLOCKED {Card number ending with {{card.number}} has been activated.}
other {Card number status changed to {{card.lockStatus}} }}</span>
</ng-template>
Component Code:
#ViewChild('activeDeactiveSuccessMessage', { static: false }) private activeDeactiveSuccessMessage: TemplateRef<any>;
Bellow is part of code to attach the rendered template to DOM and works fine:
let el = this._viewContainerRef.createEmbeddedView(this.activeDeactiveSuccessMessage, { card });
But I don't want to attach to DOM, want to get rendered template inside component before attaching.
used bellow code to get text but for second node which needed context, returns comment!:
let el = this.activeDeactiveSuccessMessage.createEmbeddedView({ card });
console.log(el.rootNodes[0].innerHTML); // --> Notification
console.log(el.rootNodes[1].innerHTML); // --> <!----><!----><!----><!---->
I expect the output of Card number ending with 6236 has been deactivated. for the second node.
Problem SOLVED!
The problem I encountered was because of my request to translate alert messages in the script at runtime, and I did not want to use ngx-translate.
Fortunately, in angular 9, with the help of #angular/localize, this problem has been solved and it is easy to translate texts into the script in this way:
#Component({
template: '{{ title }}'
})
export class HomeComponent {
title = $localize`You have 10 users`;
}
to read more visit https://blog.ninja-squad.com/2019/12/10/angular-localize/

meteor comments-ui: comments appear then immediately disappear

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

Knockout JS - Geting a modal popup to edit items

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.

Using registerHelper how can I access the DOM element where this helper is used?

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

Exception in template helper: Error: Can't use $ on component with no DOM

I'm making a note-taking application in Meteor. I have a live-preview which updates as you type that shows what the note would look like after submitting it, it also renders the markdown. Now I want to add syntax highlighting to the preview note.
If what I'm trying to do is not completely clear to you, check out the demo here. And try creating a new note. Inside the preview, I'd like to add syntax highlighting.
Since the live-preview needs to rerun the syntax-highlighting every time something gets typed, I need a way to hook into a rerender of the content of the preview-note, to rerun the highlightjs code.
With previous versions of Meteor, this was very simple by simply using Template.render = function () {...}. But that's not possible anymore, since the API changed to only run render once, when the template renders for the first time.
What I want to try now is the following. Add a template helper next to the markdown render function, so it'll run every time the content gets updated. In this rerender function, I'll look up the current template instance, and run the highlighting code.
Template
<template name="previewNote">
{{#if content}}
<div class="note preview">
<h2 class="previewTitle">Preview</h2>
<hr>
<div class="middleNote">
<h1>{{title}}</h1>
<!-- THIS LINE -->
<p class='note-content'>{{#markdown}}{{content}}{{rerender}}{{/markdown}}</p>
<!-- THIS LINE -->
</div>
<hr>
<ul class="tags bottomNote">
{{#each tags}}
<li class="tag"><a>{{this}}</a></li>
{{/each}}
</ul>
</div>
{{/if}}
</template>
JS
Template.previewNote.rerender = function () {
// get the current template instance, and highlight all code blocks
var codes = UI._templateInstance().findAll("pre>code");
for (var i = 0; i < codes.length; i++) {
hljs.highlightBlock(codes[i]);
}
};
This seemed pretty solid, until I tried it out and got an exception.
Exception in template helper: Error: Can't use $ on component with no DOM.
I'm looking for either: Why this error happens, and how I can fix it. Or another method, to achieve the result I'm looking for.
Use Tracker.autorun like this:
Template.previewNote.rendered = function(){
this.autorun(function(){
// every time Template.currentData is changed then this function reruns
Template.currentData();
var codes = self.findAll("pre>code");
for (var i = 0; i < codes.length; i++) {
hljs.highlightBlock(codes[i]);
}
})
}

Categories