Here is my root component in angular-meteor app:
export class RootComponent implements OnInit {
playlists:Mongo.Cursor<any>;
ngOnInit():any {
this.playlists = Playlists.find();
}
}
When I update Playlist collection from another angular(2) component, I see that my view where I am rendering the playlists updates.
That particular view does not have access to any other variable in the other component so it seems obvious that the view is updating because of collection playlist updating.
I was wondering that how is this happening without having that code in ngOnInit update the view without being in Tracker.autorun?
Tracker.autorun() is a way to explicitly create a computation object around some data that you want to be reactive. However, certain things are automatically reactive in Meteor:
Session variables
Template helpers
Publish/subcribe statements
Collection cursors
See here for more info: https://www.discovermeteor.com/blog/reactivity-basics-meteors-magic-demystified/
Related
I am trying to wrap FullCalendar React component with my own functional React component with useContext hook to access MobX store (I might use the store in other components eventually) and observer() to make it react to changes in the store. My component reacts as I would expect, but I have trouble making the FullCalendar component render after a change.
I've tried finding a solution, wrapping <FullCalendar> in <Observer>, playing with autorun() and reaction() but nothing worked. I have to be missing something
This sandbox https://codesandbox.io/s/mobx-fullcalendar-xejn9 shows a simplified version of my solution so far.
Clicking the button at the top adds an event to the observable store, which gets shown in a list below it. But the calendar doesn't show the new event.
Weirdly enough if I make a change in the code, save it, the CodeSandbox causes the render and the events show up in the calendar.
Thanks to #ADyson, I found a solution. Instead of passing a static list of events to the FullCalendar component
events={store.events}
I can pass an event fetching function. So in the most trivial case, I can just pass the events from the store to the successCallback function and it works.
events={(fetchInfo, successCallback, failureCallback) => {
successCallback(store.events);
}}
I've updated the CodeSandbox https://codesandbox.io/s/mobx-fullcalendar-xejn9 as well.
I'm currently getting used to using FlowRouter after a while using Iron Router and trying to set up some best practices. I'm subscribing to my collection at a template level.
Previously I've waited for a template to render using onRendered and then targeted my input field and applied focus(), however I am now trying to only show my template in Blaze when the subscriptions are ready using the following (please excuse the Jade but I think it's pretty clear in this case)
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
form
input(type="text" name="name")
So the basic idea is that until the subscriptions are ready the spinner shows. The issue I'm having is that now even when the template renders, the focus won't apply. I've tried various methods of wrapping it in an autorun call but not sure the best way of trying to target the first field when combined with this approach?
Template.subjectNew.onRendered(function() {
console.log('rendered');
$('input').first().focus();
});
Is it possible?
Many thanks for any ideas.
Your subjectNew is considered rendered even when it is only showing the spinner. Just stick your:
form
input(type="text" name="name")
Into a separate template and then attach your focus code to the onRendered handler of that other template.
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
+myForm
template(name="myForm")
form
input(type="text" name="name")
js:
Template.myForm.onRendered(function(){
$('input').focus()
});
I think using an autorun would be a good approach but then you would have to employ Tracker.afterFlush() to wait to set the focus after the form is rendered.
Something like:
Template.subjectNew.onRendered(function() {
this.autorun(() => {
if (this.subscriptionsReady()) {
Tracker.afterFlush(() => $('input').first().focus());
}
});
});
I'm currently developing a Meteor application where I use the video.js-Library.
I have the following template:
template(name='foo')
.video.embed-responsive.embed-responsive-16by9
with richMediaContent
video#video.video-js.vjs-default-skin.vjs-big-play-centered(controls='' preload='auto')
source(src='{{video.videoUrl}}' type='video/mp4')
p.vjs-no-js {{i18n 'videoTagNotSupported'}}
Initializing the video.js-Library after the template is rendered works fine.
Template.foo.onRendered ->
videojs document.getElementsByClassName('video-js')[0], {}
But the videojs-Library is not reinitialized if the same template is rendered with a different video (with a different richMediaContent).
I've already tried to move the video-Part in an own template and included it in the foo-Template so that the onRendered-Call should get called every time a new video is loaded. But this doesn't seem to work.
Do you have any idea how I can reinitialize the library if the video changes?
Thanks in advance!
New answer
Indeed, when your route changes but uses the same template, the said template does not get rendered again, therefore your js plugin call will not trigger a second time. What you can do instead is call your js plugin in an onAfterAction call, within your route definition:
Router.route('/video/:_id', {
name: 'video_page',
template: 'foo',
// ...
onAfterAction: function () {
videojs document.getElementsByClassName('video-js')[0], {}
}
});
Previous answer
I think you are looking for the almighty this.autorun. At the end of your onRendered function, it should look like this (I type it in pure javascript)
this.autorun(function () {
var video = Session.get("video"); // reactive data
videojs document.getElementsByClassName('video-js')[0], {}
});
The idea is that the first line must include, within the autorun function, a way to get your reactive data. In that case, I use the Session which is reactive. Collections are also reactive, so another way would be something like Videos.findOne();. This will depend on how you get that video element.
What this does is that any time the reactive data changes, the callback for this.autorun will run again, and your video plugin will be reset.
Currently meteor supports a limited number of events that we can react to from our template definitions. I would like a way to react to events beyond this predefined list. I want the freedom to add any event, even custom events, to the list of possible events in a template.
One idea I had would be to set up a jquery event handler somewhere that listens for the unsupported event and have it set a session variable:
$(form).submit( ->
Session.set('formSubmitted', true)
And then use that session variable when rendering a template:
Template.confirmation.submitted = ->
return Session.get('formSubmitted')
<template name="confirmation">
{{#if submitted}}
<!-- do whatever -->
{{/if}}
</template>
But this is just a workaround and doesn't really address the issue. Is there a real Meteor-way of doing this? Is this something I can do with the new Spark implementations?
NOTE: Please ignore the fact that I'm using the submit event here. I know I can just bind a click event to the submit button, but that's beside the point.
NOTE 2: The accepted answer to this question is also just a workaround.
The rendered callback is what I use to do this.
http://docs.meteor.com/#template_rendered
The callback gives you template instance you should use to find the dom elements you need: http://docs.meteor.com/#template_inst
Untested example below ;)
Template.foo.rendered = ->
$(this.find("form")).submit ->
Session.set 'formSubmitted', true
Using a Session variable than to switch the view is a matter of taste I think.
I have an app State stored in the Session, that toggles Templates. Additionally the backbone package is very useful to provide some meaningful urls.
I have a TitleWindow mxml class wich has several components, and listeners.
On its creationComplete and init state i add some listeners which listen for events on its gui.
This TitleWindow is only shown when the user click on a "button", i made TitleWindow a singleton with the following code:
public static function getInstance():MyWindow
{
if ( MyWindow.singleton )
{
return MyWindow.singleton;
}
else{
MyWindow.singleton = new MyWindow();
return MyWindow.singleton;
}
}
I needed a singleton because the user will call this window several times as much as he wants and i only need one.
The problem is the following on some special external events i need to "modify" some listeners (remove listeners and add new ones) on a button from MyWindow, before it was even shown once.
I still have MyWindow.getInstance() in memory when my application starts up.
However adding /removing listeners does not seem to have any effect if he actual rendering of the components did not happen, event when using the following code on app startup.
myWindow= MyWindow.getInstance();
myWindow.initialize();
Not suprisingly if i "show" ('render') the myWindow at least once then the events modifications on the myWindow instance works perfectly.
How can i fake the complete initialisation of this component without showing it on startup ?
Thanks !
Which sort of a container holds your button? If you are using a Multiple View Container you can try setting the creationPolicy to all. Single View Containers create all their children in one go and you shouldn't face this problem.
From Flex 3.0 docs I could retrieve this:
The default creation policy for all containers, except the Application container, is the policy of the parent container. The default policy for the Application container is auto.
This looks like the cause for all your troubles.
Update: I did not mention this earlier, since I thought this was to be expected :) Setting the creationPolicy to all makes your application load more slowly. So, read up on Ordered Creation -- this technique helps you to choose if the controls are displayed all in one go (which is the default behavior, after all of the controls have been created) or step-by-step, as and when they are created.