I have a table template that is leaking I have pinpointed the code causing the issue to a single line.
<tbody>
<tr repeat.for="row of workListData.rows" click.delegate='resultItemClick($event, row)'>
<td repeat.for="col of workListData.columns">
<template if.bind='col.name === "isChecked"'>
${col.label}
</template>
<template if.bind='col.name !== "isChecked"'>
**${row.Properties[col.name]}**
</template>
</td>
</tr>
</tbody>
${row.Properties[col.name]}
This is the line of code causing the issue this line takes the current row and uses the column name to access the value of the column
enter image description hereOk it seems aurelia is leaking when I have nested repeat.for
Leaks
<tr repeat.for="row of workListData.rows" click.delegate='resultItemClick($event, row)'>
<td repeat.for="col of workListData.columns">
</td>
</tr>
Does not leak
<tr repeat.for="row of workListData.rows" click.delegate='resultItemClick($event, row)'>
</tr>
Its definitely coming from nested repeat problem is if I simplify the code IE remove web services etc its work as expected
One of my colleagues has noticed something interesting commenting out a different template controller also stops the leak seems its not fussy which controller gets commented out.
Finally located the leak it seems I had a couple however one thing for sure is aurelia seems to have a very bad tendency to leak if you do this in a template
${row.Properties[key]}
Not sure if that is supported though
I now instead pass the object and key to a function and return the value which solves the issue
Related
I'm currently making bitcoin trading web app (personal project. Not a business one)
I have a bunch of cryptocurrencies prices from API, and showing it all by using v-for loop.
here's part of my code:
<tbody class="text-sm">
<tr v-for="coin in props.coins" :key="coin.code" class="hover:bg-zinc-600">
<td class="p-1">
<div class="font-semibold">{{ coin.name }}</div>
<div class="text-xs text-gray-400">{{ coin.code }}</div>
</td>
<td
class="text-right font-bold align-top p-1"
>{{ Number(coin.trade_price).toLocaleString() }}</td>
<td
:class="{ 'text-blue-400': isNegative(coin.signed_change_rate), 'text-red-400': isPositive(coin.signed_change_rate) }"
class="text-right p-1"
>
<div>{{ Number(coin.signed_change_rate).toFixed(2) }}%</div>
<div class="text-xs">{{ coin.signed_change_price }}</div>
</td>
<td class="text-right align-top p-1">{{ convertTp24h(coin.acc_trade_price_24h) }}</td>
<td class="text-right p-1">
<button class="bg-red-700 rounded-lg hover:bg-red-600 p-1">매수</button>
</td>
</tr>
</tbody>
As you can see I have many methods and expressions that converts raw value from API to human-readables.
I'm wondering if this is a bad practice. I heard that methods are called everytime so, rather use computed().
But as you can see I have my values from API in Object(props.coins) and I'm looping this object in v-for. Can I still use computed() methods to convert a value inside an Object that is also looped by v-for?
You can't use computed for items in a loop, since they don't take in arguments. Computed is used for .. computed properties and methods take in arguments e.g. formatChangeRate()
You are right that methods are called everytime, but that's totally fine and normal practice.
So creating a component with methods like formatChangeRate, formatTradePrice is totally fine.
There are ways to make it work with computed properties but in my opinion its not worth it.
1)
You coould make another component that takes in the item as a prop, uses computed property and displays it in componenent's template but that's a total overhead.
2)
You could map the array and reference it by index. Something like:
computed: {
changeRate() {
this.coins.map(coin => {
return Number(coin.signed_change_rate).toFixed(2) + '%'
})
}
}
Now changeRate is an array of formatted change rates so you could in your v-for do something like
v-for="(coin, index) in coins)">
<td>changeRate[index]</td>
So now you're looping through the array multiple times, and the code is less testable and less readable.
In my opinion using methods is totally fine and the right way to go in this example. The performance of calling a simple formatting method multiple times is negligible.
Also the cache you're refering to is that computed properties are cached based on their reactive dependency. E.g. you have in your data, firstName and lastName. You could then make a computed property called fullName that concats those two. The caching would work so that fullName doesn't change unless firstName or lastName changes.
Since you're looping through an array the value that would be computed is always changing.
you are right, methods are called everytime and in theory thats not really good, but in practice if your method just concat strings or other simple operations, that's really not a big deal. Your computer can do millions of operations like that per seconds.
In the other hand if you do complex operations then the solution is to precalculate before.
After you receive your data construct an Array looking something like bellow and use this one in your template
(pseudo code)
[{
name,
code,
changeRate: Number(signed_change_rate).toFixed(2),
...
},
...
]
I want to use hx-swap-oob to replace a table row of the existing page "out of band".
in browser:
<table>
<tr id="offer_1">....</tr>
<tr id="offer_2">....</tr> (old)
<tr id="offer_3">....</tr>
</table>
From Server to client:
<table hx-swap-oob="outerHTML:#offer_2" hx-select="#offer_2">
<tr id="offer_2"> .... </tr> (new)
</table>
But up to now this is the result:
<table>
<tr id="offer_1">....</tr>
<table hx-swap-oob="outerHTML:#offer_2" hx-select="#offer_2">
<tr id="offer_2"> .... </tr> (new)
</table>
<tr id="offer_3">....</tr>
</table>
I guess hx-select does not get evaluated when htmx get this snippet from the server.
How can I swap a row out-of-band?
Take a look at the new extension multi-swap.
https://htmx.org/extensions/multi-swap/
It allows swapping multiple elements marked with the id attribute.
For each element it is possible to choose which swap method should be used.
This does work:
<tr hx-swap-oob="true" id="offer_2"> .... </tr> (new)
But it has a drawback:
You need to modify the method which creates this row. Depending on your context, you might already have a method for this. Why modify this method, just because the result of this method should get used out-of-band?
If you use Django, this snippet could get used to add the hx-swap-oob attribute after the HTML got created:
def add_oob_attribute(html):
"""
I would like to avoid this ugly hack
https://github.com/bigskysoftware/htmx/issues/423
"""
assert isinstance(html, SafeString)
new, count = re.subn(r'(<\S+)', r'\1 hx-swap-oob="true"', html, count=1)
if not count == 1:
raise ValueError(f'Could not add hx-swap-oob: {html}')
return mark_safe(new)
I created an issue to find a better solution in the future:
https://github.com/bigskysoftware/htmx/issues/423
This is using Vue 2.5.16 in IE 11. Assume a dataset array in app.data, the following works fine in Chrome (and the code is simplified):
...
<tbody>
<template v-for="(datarow, index) in dataset">
<tr><td> {{ datarow }} {{ index }} </td></tr>
<tr v-if="!(index % 50)"><td> -repeating header row- </td></tr>
</template>
</tbody>
...
However, in IE 11, it does not work and furthermore there is no line and character number in console error (took me some time to figure out). It just says in red:
[object Error] {description: "'datarow' is undefined" ..
It works if I remove the template tag and just put the v-for repeat in first tr and remove the 2nd one.. but I really would like to have the second one.
I assume this is a DOM issue difference in IE 11 and that IE 11 is hoisting the template tag outside the table, but don't know IF any non-standard tag will work, or if so which one will work. How can I solve this?
The solution I found to this problem was to have multiple tbody elements in place vs. template. Multiple tbody tags are allowed in IE 11 without IE moving it out of the table and thus making the tr tag unaware of the referenced loop variables.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody
There are two possible side-effects of this:
Your tbody may have been styled by CSS - mine was in bootstrap - so the appearance will be different than expected, normally with extra borders. You'll need to probably use !important or at least your own CSS to overcome this.
At least for IE 11, load time appeared slower, but I have not tested this.
Resulting code:
<table>
<tbody v-for="(datarow, index) in dataset">
<tr><td> {{ datarow }} {{ index }} </td></tr>
<tr v-if="!(index % 50)"><td> -repeating header row- </td></tr>
</tbody>
<tbody>
<!-- not posted above but I used another template/tr for the case of no records found; substituted with just another tbody -->
</tbody>
</table>
I am trying to populate table with an object which contains an array. I am able to successfully do that but I want each task name to have its own row right now they are coming in a single row.
{level_image :"image"level_name:"1"task_name: ["game","taskgame","jenga"]}
<tr v-for="tel in result" :key="tel.level_image" :pey="tel.level_name">
<td>{{tel.level_image}}</td>
<td>{{tel.level_name}}</td>
<td v-for="task in tel.task_name">{{task}}</td>
</tr>
You're missing the obvious: if you want each one to have its own row, you need to put the v-for in a <tr> tag (like you did for result). Exactly how you deal with the <td>s is up in the air, but it might go like this:
<tr v-for="tel in result" :key="tel.level_image" :pey="tel.level_name">
<tr v-for="task in tel.task_name">
<td>{{tel.level_image}}</td>
<td>{{tel.level_name}}</td>
<td>{{task}}</td>
</tr>
</tr>
Or if you mean you want each one to be on a separate line within a table cell, it could be
<tr v-for="tel in result" :key="tel.level_image" :pey="tel.level_name">
<td>{{tel.level_image}}</td>
<td>{{tel.level_name}}</td>
<td><div v-for="task in tel.task_name">{{task}}</div></td>
</tr>
The main idea is that you want the v-for to be associated with the type of tag that creates the entity you want each task in.
The problem
In Vue, I'm passing an array called issues. The array contains (at present) two objects, but can contain infinite amounts of objects. Every object then has another array named issues, nested inside of it.
The issue is that when I need to display the data, I find that I can't seem to reach the inner "issues" section of it.
I can loop through the first array like so:
<tr v-for="issue in issues" track-by="id">
But that only lets me see the first two objects. I then tried:
<tr v-for="issue in issues" track-by="id">
<td>
<div class="btn-table-align" v-for="issue_title in issue.issues">
#{{ issue_title.title }}
</div>
</td>
</tr>
Which lets me access the sub-elements, but doesn't generate enough rows. I then tried looping over it AGAIN, like so:
<div v-for="first in issues" track-by="id">
<tr v-for="issue in first" track-by="id">
<td>
<div class="btn-table-align">
#{{ issue.id }}
</div>
</td>
</tr>
</div>
But, alas - it generates no rows at all when I do that.
I'd basically need a way to run a "issue in issues", then another for the results and THEIR direct children. The only issue is - I can't figure out how to do it, and Vue won't respond to any of the above attempts! I find a severe lack of documentation on two-dimensional arrays in Vue as well, which has me confused further.
Can anyone shed some light on this? Is it possible, or do I need to adjust the data sent to Vue differently?
To help, I shot an image of an example structure: http://i.imgur.com/6Oz67R9.png
This was a typical 5am question, where I now realize that the data I'm passing makes no sense - it should be the other way around. The actual issues should be in the first array, and the subarray should contain affected servers.