onrender vs init in Ractive.js - javascript

I recently started trying out Ractive.js. I was particularly interested in its components. One of the things I noticed immediately is that many of the examples are using the init option. However when I try to use the init in my code I get a deprecation notice and it then recommends using onrender instead. onrender however had far fewer examples than init and some of the functions such as this.find weren't available inside onrender. I looked through the Github issues but couldn't find any reasoning behind this change or what the suggested path forward for selecting elements specific to a component was.
I created a test pen to try creating a recursive component with the new API but I had to resort to using jQuery and an undocumented fragment api to select specific DOM nodes I needed to manipulate. I feel this is against how Ractive expects you to do things, but I couldn't figure out what was expected of me with the existing documentation.
What's the major differences between the init and onrender options and how does onrender expect you to handle manipulating specific elements and their events within a component?

You can use this.find() inside onrender (if you can't for some reason, you've found a bug!).
We split init into oninit and onrender a while back for a number of reasons. Those examples you mention are out of date - are they somewhere on ractivejs.org? If so we should fix them. You can find more information about lifecycle events here on the docs, but the basic difference is this:
init was called on initial render (assuming the component was rendered, i.e. never in node.js, if you were doing server-side rendering)
oninit is called on creation, immediately before rendering. It is called once and only once for any Ractive instance, whether or not it gets rendered. It's therefore a good place to set up event handlers etc. The opposite of oninit is onteardown, so you can use that handler to do any cleanup if necessary (or use this.on('teardown'...) inside oninit).
onrender is called whenever a component is rendered. This could happen more than once (if you unrendered, then re-rendered, etc) or not at all. If you need to store DOM references etc, this is the place. The opposite of onrender is onunrender.
I did a fork of your codepen, replacing the jQuery stuff with more idiomatic Ractive code to show how you'd do it without storing DOM references.

Related

Vue methods to change data discouraged by docs - but why?

According to the Vue 3.0 guide:
Methods called from a template should not have any side effects, such
as changing data or triggering asynchronous processes. If you find
yourself tempted to do that you should probably use a lifecycle hook
instead.
I'm curious why this is? I feel like it's very likely you'll want to change data on an event listener that references your own method rather than a lifecycle hook.
I think what they mean is you should not call a method with side effects during the render, like this:
<div :title="changeSomeData()">Hello</div>
One such problem with this is calling changeSomeData() during the render can mutate reactive data which will trigger another re-render of the component, potentially infinitely. It's not good code.
It isn't always clear to the programmer when the component will be re-rendered and thus when changeSomeData() will be called. This makes it difficult to trace the cause of some data mutation.
An event handler method is not executed during the render, instead it is only registered as an event handler and will be called later in response to that particular event. This is OK.
They means the methods used for rendering purposes like :
<p>
{{showSomeCalcul()}}
</p>
because the methods are used as handler for events :
<button #clikc="send" >Send</button>

Angular 2+ Window attribute undefined

I'm using an external library that attaches itself to the global window object (window['lib']) once the library's javascript file is loaded by the browser. I'm trying to invoke code using this library whenever a component is loaded, however everytime I try to access the object it is undefined (due to the library not having been loaded). I have tried every lifecycle hook I can think of, however nothing appears to wait for the DOM to be fully ready. For example, I want to do something like this:
ngOnInit() {
window['lib'].doStuff(); // <-- window['lib'] is undefined
}
If I wrap it in a timeout, then it becomes available. However, this looks like code smell and do not want to approach it this way:
ngOnInit() {
setTimeout(function() {
window['lib'].doStuff(); // <-- this works
});
}
What is the best / suggested / "most angular way" to approach this problem? Thanks!
Angular Lifecycle Hook: ngOnInit()
Initialize the directive/component after Angular first displays the
data-bound properties and sets the directive/component's input
properties.
Called once, after the first ngOnChanges().
This is a common problem with Angular. Older methodologies like this one that uses a global variable on the window object will piss off the way Angular loads the application and also TypeScript (during development). The reason why you have to do window['lib'] and not window.lib is because TypeScript doesn't know anything about the types of window.lib so window['lib'] is a way to force it to work.
The other part is that depending on what type of compilation you're using (AOT vs JIT), that library you're loading may or may not be ready yet (also depends on how you're loading that script/module into the application). As Commercial Suicide mentioned, you could try some of the other Angular Lifecycle Hooks but more than likely, you're going to end up settling on setTimeout. You actually don't even need to define the timeout period or you can pass 0ms (as you've done in your code). Angular just wants you to hold off on calling that function until the DOM it's finished rendering.
I personally can't wait until all the jQuery-like libraries are converted into proper ES6 modules. The days of just throwing a script tag at the bottom of the body are long gone.
setTimeout
How to get a reference to the window object in
Angular
Relevant Thread

What is the correct usage for backbonejs view's initialize() and render()?

I find the usage of the different elements of backbonejs quite ambiguous. From my undestanding, this was the intention as backbone didn't want to be a framework, but more of a set of tools/objects.
I understand most parts of backbonejs, but i'm still questioning what is the correct usage for a view's initialize() and render() calls. In other words, what logic should be placed within each.
Could someone explain what is best practice or what is considered correct usage of these calls within a view?
This is a pretty far-ranging question, but I'll give it a try. The key takeway here is that there is no such thing as "the correct usage" for these calls. Backbone is flexible on purpose and meant to adapt to your needs. That's why it still has its place in a world of more sophisticated, but "opinionated" frameworks (which tell you that it's "my way or the highway").
Backbone does not use any magic with respect to rendering. The render() method is a no-op and not even invoked automatically for you. Its existence is nothing more than a hint how things are usually done.
The initialize() method, however, is invoked automatically when the view is instantiated. Backbone guarantees that at that point, the top-level DOM element of the view (the el) has already been created and is ready to have stuff attached to it.
But again, that's pretty minimal: the top-level element is there, but not yet attached to the DOM (unless you passed options.el to the constructor, setting the el to an existing element). Inserting the el into the DOM is your job, too.
So you are free to decide how to wire things up. What I typically do is this:
Most of my views have a template, which is commonly assigned to the template property of the view. (Again, just a convention, no magic involved. You could name it foo.) That template is compiled in initialize().
In initialize(), I set up the relationship with a model or a collection which the view is supposed to represent. Ie, the view observes model/collection events and is made to call render when the data has changed. (You don't have to use a Backbone entity as your data source, of course ... could be anything).
In render(), the data is fed into the template, and the result becomes the innerHTML of the el. Afterwards, I make sure the el is inserted into the DOM if it isn't already.
The first call to render() should happen when the data is ready. The right moment depends on the app. Perhaps render() is the last call in initialize(), or some event will kick it off later on.
If a lot of that sounds like repetitive boilerplate to you, yes, that's exactly what it is. Frameworks like Marionette exist on top of Backbone to take care of that.
But there is a sigificant performance penalty for that framework magic. So when I have to generate a lot of views, I stick to the generic Backbone stuff (and use one of my own components for speeding up the template handling.)

Meteor: how can I avoid checking for the existence of a variable or property everywhere?

When programming in Meteor I often find myself having to sprinkle typechecks or existence checks a bunch when writing Template helpers (at least under a couple very common conditions).
A helper for one template depends on a collection loaded by a different template
Any time a template helper operates on a piece of the DOM that another template is responsible for rendering into existence
For example (in the first case):
Template.example.rendered
rev1 = getRev(revId1)
revText1 = html2plain(rev1.text)
where getRev is doing an operation on the Revisions collection that may or may not be loaded by the time the example template is first rendered. So rev1.text will sometimes throw an exception because getRev ends up returning null or undefined if called before Revisions is loaded.
I then end up having to check a ton of variables/objects throughout my code for existence before using any of their properties just to be safe.
I could imagine using a router to not render my example template until after a different collection is ready (but for nested templates, and Session variable changes this doesn't work so hot).
I could imaging wrapping the helper code in a if (isCollectionReady) which might help but doesn't seem best practice.
The question is: Is there a canonical or best practice way to, identify these situations, code for them, or avoid this altogether?
Meteor is designed such that its templates are reactive, so that in most cases you shouldn't need to do DOM manipulations on them. As the underlying data changes, the templates automatically rerender so that they're always showing the latest data. Take a look at the examples in the Meteor docs: they don't use any DOM manipulation code. The templates put the data into the right places, and that's all they need.
In my experience there are two common reasons to need to put code into rendered:
You're loading a widget that needs to be initialized after the template is rendered and ready, like a <select> replacement.
You're doing animations. (In this case, I usually tell my template to put everything in its proper place but with a CSS class that hides the elements, and then all rendered does is animate the reveals.)
It's usually fine for a template to render before its subscription has loaded; at worst, the template will just render blank and then rerender as the data streams in. Also remember that you can subscribe from client-side code other than a template helper, for example Meteor.startup on the client side. Finally don't forget about the created helper; if you really want to wait until a template is loaded before subscribing, that would be a better place to subscribe than rendered, as it gets called sooner.
What DOM manipulations are you doing and why? Assuming you're not using widgets or animations, chances are that you can achieve what you want by using templates on their own without any additional manipulation code.

Is publishing/subscribing to events after UI rendering a best practice regardless of framework?

I've inherited a rather large Javascript/ExtJS3 code base, and there are many instances of invoking events inside of the overridden initComponent method, after the call to "...superclass.initComponent.apply(this, arguments)". Specific events are being invoked on specific objects in a manner such as the following:
this.filter.on('filterUpdated', function(filter, params)
I've started converting the code to use a pub/sub paradigm instead, to reduce the coupling between objects and their specific event names, but quickly ran into issues when publishing and/or subscribing to events within initComponent (which in ExtJS executes before rendering). I need to fire an "INIT" event from the highest level component when the screen first loads, and I was either getting an error (due to ExtJS "templates" not having been rendered as it turns out), or events not firing at all.
Then I read the following in the ExtJS source for Ext.Component (from which all components extend) and I had an "aha" moment:
if (this.tpl) {
if (!this.tpl.compile) {
this.tpl = new Ext.XTemplate(this.tpl);
}
if (this.data) {
this.tpl[this.tplWriteMode](contentTarget, this.data);
delete this.data;
}
}
this.afterRender(this.container);
When I switched to both publishing the "INIT" event from my topmost component's afterRender method, and subscribing to all events from all other components from their afterRender methods, everything worked as I expected. And now I am just wondering, largely to validate my design....
Is this a generally accepted way of implementing pub/sub in an event driven UI? Regardless of framework even? Namely are the following 2 good principles, or are their other ways?
"Initialization events" should get published after all sub-components have rendered
All sub-components should subscribe to all events (to be on the safe side) after they have rendered
Thanks in advance
You have to balance the overhead of event handling vs. the possibility of missing significant events. In js/DOM land state is mutable.
For your #1 if you can identify a point in time when all your sub-components have rendered and subscribed, firing an init event makes sense.
For #2, it seems safe everyone to listen for events; however it could slow things down. If performance issues are apparent you may have to decide what events you don't care about and avoid subscribing.

Categories

Resources