Is there an event when whole template is rendered in Vue3? - recursion

I have some recursion in the component template and I need add some functionality to the result html. So I need to wait till the whole html of the component and its nested components are available. What is the right time to add this functionality? Functionality means call jQuery plugin on the result html.
The code looks like:
<template>
<div>
<ul>
<li v-for="category in categories"
:key="category.id"
id="menuItem_{{category.id}}"
data-module="{{category.module?.id ? category.module?.id : 0}}"
>
<div>
{{category.name}}
</div>
<CategoriesList v-if="category.all_children?.length" :categories="category.all_children" />
</li>
</ul>
</div>
</template>

I had a similar situation for my previous project. I was using angular and NxJS and I used observables for doing this.
You can create a dynamic array of observables, init it in the main component, add a subscription and then update them when each component is rendered (in the mounted function for example). When all your array is done, you can then call the function / apply your functionality.
Seems like something like exists in Vuejs: https://jasonwatmore.com/post/2019/04/02/vuejs-rxjs-communicating-between-components-with-observable-subject
or https://www.thisdot.co/blog/introduction-to-vuejs-and-rxjs
I could check out the code tonight and send it to you if you are interested.

Related

Vuejs: inject & load vue component only when another component is emiting an event

I'm working with Symfony >4.2 and the latest vestion of vuejs.
So i have a component exposing different filters (price, size .. blablabla) and another component displaying product results. What i would like to do is first display all products by default (no filters selected) with symfony/twig (so that i can actually see the html in the source code of my navigator) in the #display-component div.
Then secondly, as soon as a user changes a filter, the vue display component should be injected in #display-component, and take over from that moment (it can update, delete, add, whatever..). But i really need that first render with twig.
Example:
<div id="#display-component">
<div class="product"> </div>
<div class="product"> </div>
<div class="product"> </div>
</div>
new Vue({
el: '#display-component',
components: {
'DisplayComponent': DisplayComponent
},
template: '<DisplayComponent/>'
});
I really don't know if its possible. It seems that if i hook the display component, i always loose the content of the div where it is being injected. I'm not showing code because it is quiet long. I would really appreciate a very simple POC, if someone has a solution !

What's a clean way to have a conditional container in handlebars?

When there is a link present, we want something like this HTML:
<img src="{{src}}"></img>
When there is no link present, we want something like this HTML:
<img src="{{src}}"></img>
Is there a clean way to do this? I consider the following solution bad, because it's dangerous to have to separately remember to open and close the <a> tag:
{{#if url}}<a href="{{url}}" title="{{title}}" target="_blank">{{/if}}
<img src="{{src}}">
{{#if url}}</a>{{/if}}
I considered using a block helper, but can't think of how to do so without adding more complexity. Maybe something like:
{{#linkWrap url}}<img src="{{src}}">{{/linkWrap}}
But then it's hard to see how we set the title and target and everything gets awkward.
I think you are on the right track, but I would recommend using a Handlebars Partial Block instead of a Block Helper. This will allow to pass one piece of template (the block) to another piece of template by which it will be wrapped (the partial).
Handlebars provides us with {{> #partial-block }} as a way to render a block of template within a partial. We can use this to create our "linkWrap" partial:
{{#if link}}
<a href="{{link.url}}" target="{{link.target}}" title="{{link.title}}">
{{> #partial-block}}
</a>
{{else}}
{{> #partial-block}}
{{/if}}
This gives us a clean and simple partial that will allow us to wrap any section of our template with a link as long as we have a link object to pass to our partial. Note that I have opted to use an object to represent the link so that I can pass a single parameter to the partial instead of passing the url, title, etc. properties individually.
For anywhere we wish to render a link around some markup in our template, we can do so in the following way:
{{#> linkWrap link=link}}
<img src="{{image.src}}">
{{/linkWrap}}
If the link object is null or undefined, the img element will be rendered without a parent anchor element.
I have created a supplementary fiddle for reference.

Meteor: Passing array parameter to template

I'm trying to pass an array to another template in Meteor.
Why? Because I would like to create a small template for each Bootstrap element, allowing me to reuse components much more easily.
{{> dropdown id="dropdown1" textDropdown="My dropdown!" listItems=["item1", "item2"] }}
This does not seem to work unfortunately.
Any clue? Does what I'm doing even make sense? I'm new to Meteor.
Thanks!
Spacebars is currently pretty limited in what it can accept - you'll need to add a helper to accomplish this:
Template.myTemplate.helpers({
listItems: ['item1', 'item2']
});
And them modify your template:
{{> dropdown id="dropdown1" textDropdown="My dropdown!" listItems="{{listItems}}"}}
Make sure to update myTemplate to the parent template's name.

Template.body vs Template.myTemplate

I am currently going through the Meteor tutorial (https://www.meteor.com/try), and have come across something about Templates that puzzles me.
In the tutorial, a simple "Todo List" application gets created. In this app, the following HTML is placed into the simple-todos.html file:
<!-- simple-todos.html -->
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
Then, the following JavaScript is placed into the simple-todos.js file:
// simple-todos.js
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
// This code only runs on the client
Template.body.helpers({
tasks: function () {
return Tasks.find({});
}
});
}
At this point, the example works exactly as intended. However, as I poke around in the documentation, as well as look at other examples on the web, I have noticed slightly different syntax: using Template.myTemplate instead of Template.body.
So, out of curiosity, I altered my JavaScript file to read:
Template.task.helpers({ ...
instead of:
Template.body.helpers({ ...
However, when I run the application now, the client does not display the data from the collection. I don't get any errors about undefined types, like I do if I misspell the template name in the JavaScript, so it seems that it is resolving the template correctly. But why isn't it getting used or rendered?
And to go a little further: when is it appropriate to use Template.myTemplate and when is it appropriate to use Template.body?
The helpers code only works for the template it's attached too.
So, code that works for Template.task will only apply to templates named "task".
Template.body is like the one-off that exists because it would be weird if it didn't. It's a way for you to specifically target the <body>, even though technically, there's no template named "body".
So, what is going on:
Parent template = body
Child template = task
Your logic says:
In the parent template, for each task that we find, render an instance of the child template "task".
If you change your helper from body to task, you won't get any output at all, unless you mimic the pattern that's already happening:
<template name="task">
{{#each tasks}}
do something
{{/each}}
</template>
That's because <body> is the parent template, and you should treat it as such:
<template="body>stuff that normally goes in an HTML body here</template>
When you remove the helpers for the body, then no data gets displayed at all because helpers pass data into the template. And with no helper for the template i.e. the body, you get no data.

How to update partials

Hi there RactiveJS users, I'm new here and I'm very excited using RactiveJS but, I came up to wonder if I can update the template / data inside a Ractive instance .
I have the following modal window:
<div id="modal" intro-outro="fade:100">
<div id="content">{{>content}}</div>
</div>
And >content is a ranking template. Imaging that clicking on a player will need to overwrite the ranking with the playerProfile template and it's requested data. Is there another way this could be done aside teardown and then reinitialise the modal window with the playerProfile template ?
UPDATE
I forgot to mention that the window will wrap around 20 templates at the moment (which will grow in the future as the game gets more complex). The window I am talking about is the main modal window that shows every template that user triggers, like rankings, mails, reports, tribes, map etc.
Regards.
WORKAROUND
Marty's answer is correct but I'd rather not complicate myself and just create new instances of Ractive inside the #content div than create different instances of the whole modal window which will be shown on init and hidden on teardown.
If you just have two states (as opposed to open-ended dynamism), you can block them conditionally:
<div id="modal" intro-outro="fade:100">
<div id="content">
{{^selected}}{{>partial1}}{{/}}
{{#selected}}{{>partial2}}{{/}}
</div>
</div>
You can augment the data model after initial render. Supposed you had a player click handler:
ractive.on('playerSelected', function(e){
//however your data works...
$.get('/player?id=' + e.context.id, function(data){
ractive.set(e.keypath + '.player', data.player)
ractive.set(e.keypath + '.selected', true)
})
})
If you need a more dynamic approach, you can use a component to set the partial dynamically. Here's an example (http://jsfiddle.net/9Vja5/2/) of how you can do it in 4.0 (4.1 will offer a few improvements in this area). Use a component in the main template:
<div>
{{#reset}}
<dynamicPartial partial='{{partial}}'/>
{{/}}
</div>
Then set the template to the supplied partial in the component beforeInit:
Ractive.components.dynamicPartial = Ractive.extend({
beforeInit: function(o){
o.template = '{{>' + o.data.partial + '}}'
}
})
The partials need to either be defined globally, or on the dynamic component.
I'm using a partial because that's what you had in your question. You could set the template directly if you had a list of which template to assign. Or you could also set the template to a component via '<' + component + '/>'.
The reset is a bit of a hack to force Ractive to re-render the component:
r.observe('partial', function(){
r.set('reset', false)
r.set('reset', true)
})
Otherwise it would just change the data in same component instance, which wouldn't get it to recreate and assign a new template.

Resources