[英]Elixir Phoenix LiveView Echart just vanishes (disappears)
[英]Elixir phoenix LiveView collapsible collapses on update
LiveView 折疊我打開的元素。
我有一個在頁面加載時折疊的元素:
<a class="collapse_trigger">...</a>
<div class="is-collapsible">
# content updated by liveview
</div>
如果用戶點擊可折疊,則可折疊有一個類.is-active
。
<a class="collapse_trigger">...</a>
<div class="is-collapsible is-active">
# content
</div>
但是 liveview 刪除了那個類。 知道如何確保 liveview 忽略父元素<div class="is-collapsible is-active">
但會照顧孩子嗎? 我的第一個想法是phx-update="ignore"
。 但是現在我想我需要將可折疊的邏輯放入后端。 :/
我將bulma-collapsible與一個 css 更改一起使用:
// the following is necessary because liveview does not work well with the bulma-collapsible. Otherwise elements would stay open but can be closed by clicking them twice.
.is-collapsible{
height: 0;
&.is-active{
height: auto;
}
}
為了使用僅限前端的選項,我建議如下。
為簡單起見,我將使用純 javascript。
我們需要修改按鈕,並編寫函數來存儲狀態(我使用了簡單的 localStorage)
<a class="collapse_trigger" onclick="memoizeCollapsibleState()">...</a>
<script type="text/javascript">
function memoizeCollapsibleState() {
if (!localStorage.getItem('collapsibleState')) {
localStorage.setItem('collapsibleState', true)
} else {
localStorage.removeItem('collapsibleState')
}
}
</script>
之后我們需要編寫從本地存儲恢復該狀態的函數
function restoreCollapsibleState() {
var collapsibleEl = document.getElementById('collapseExample');
if (localStorage.getItem('collapsibleState')) {
collapsibleEl.classList.add('is-active')
}
}
最后一刻,我們需要將該函數綁定到phoenix_live_view
套接字更新,我們需要在窗口加載后立即執行此操作。
window.onload = init;
function init() {
liveSocket.getSocket().channels[0].onMessage = function (e, t, n) {
setTimeout(restoreCollapsibleState, 10)
return t
}
}
setTimeout
函數的目的是 socket 更新是異步操作,我們需要添加一些延遲才能恢復可折疊狀態。 10ms 似乎沒問題,但我們可以將其更改為任何其他去抖動函數,我只是為了簡單起見使用它,認為這是概念證明
我剛剛用默認的活鳳凰結構做了一個例子mix phx.new my_app --live
向它添加了引導程序(但我認為 bulma 遵循基本相同的規則)並將 phoenix live 模板修改為以下內容
<div class="collapse <%= if (@results && String.trim(@query) != ""), do: "show", else: "" %>" id="collapseExample">
<div class="card card-body">
<%= for {app, _vsn} <- @results do %>
<p value="<%= app %>"><%= app %></p>
<% end %>
</div>
</div>
因此,如果有任何結果並且查詢不為空,則永遠不會折疊。
對於您的情況,我認為它會略有相同
<a class="collapse_trigger">...</a>
<div class="is-collapsible <% if (@results && String.trim(@query) != "") do: "is-active", else: "" %>">
# content
</div>
根據@zhisme 的回答,我創建了一個可行的解決方案。 唯一的要求是可折疊物品有 id。
function initCollapsibles() {
const bulmaCollapsibleInstances = bulmaCollapsible.attach('.is-collapsible');
bulmaCollapsibleInstances.forEach(bulmaCollapsibleInstance => {
const key = 'collapsible_' + bulmaCollapsibleInstance.element.id;
// some id's from other pages might leak over if we don't clean the localstorage on page load
localStorage.removeItem(key)
bulmaCollapsibleInstance.on('before:expand', (e) => {
localStorage.setItem(key, true)
})
bulmaCollapsibleInstance.on('after:collapse', (e) => {
localStorage.removeItem(key)
})
});
}
function restoreCollapsiblesState() {
const collapsibles = Array.prototype.slice.call(document.querySelectorAll('.is-collapsible'), 0);
collapsibles.forEach(el => {
if (localStorage.getItem('collapsible_' + el.id)) {
// I'd love to call the collapsible open method, but haven't figured out how to get that instance without creating a new one or storing it to the window globally.
el.classList.add('is-active')
}
});
}
document.addEventListener('phx:update', restoreCollapsiblesState);
document.addEventListener('DOMContentLoaded', initCollapsibles);
一個眾所周知的,如果不是優雅的解決方案是在mount()
上的那個元素上addEventListener
並將它放回update()
。 這是想法:
Utils.onDetailsTagState = {
mount(storeEl) {
let detailsTag = storeEl.el.closest("details")
if (detailsTag) {
storeEl.expand = detailsTag.open
detailsTag.addEventListener("toggle", event => {
storeEl.expand = event.target.open
})
}
},
update(storeEl) {
let detailsTag = storeEl.el.closest("details")
if (detailsTag) { detailsTag.open = storeEl.expand }
}
}
Hooks.callaspable = {
mounted() { Utils.onDetailsTagState.mount(this) },
updated() { Utils.onDetailsTagState.update(this) },
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.