理解 AlpineJS 的 x-init 和 x-for

huangapple go评论72阅读模式
英文:

Understanding AlpineJS x-init and x-for

问题

以下是您的代码的翻译部分:

我有一些东西,我一直在努力解决,我已经解决了,但我不太确定原因,希望您可以帮助理解。

我的想法是创建一个控制台框,我会通过Laravel Echo接收的日志来更新它。

然后,在我的`<script>`标签中,我有以下内容:

```javascript
getLogs() {
    Echo.channel('logs').listen('.logs', (log) => {
        // 根据事件名称和数据执行所需的操作
        console.log(log);
        this.logs.push(log.message.function + ': ' + log.message.task + ' - ' + log.message.status);
        
        window.dispatchEvent(new CustomEvent("scrollDown"));
    });
}

这花了我相当长的时间才能让它工作,主要是x-for更新logs变量的新添加时遇到了问题。不过,通过将getLogs()添加到我的容器<div>x-init中,我成功解决了这个问题。

我的理解是,x-init只在元素渲染时执行。我不太明白为什么将函数放在x-init中会允许x-for正确更新。有人能提供一些见解来帮助我理解吗?

这是代码的一个简化示例:

const appState = {
    events: [],
    
    async loadEvent(event) {
        // 向服务器发送请求并处理响应
    },
    
    async retrieveLoadedEventsTable() {
        // 从服务器获取数据
    }
}
<div x-data="appState" x-init="retrieveLoadedEventsTable()">
    <template x-for="event, index in events" :key="event.id">
        <!-- 渲染事件属性的HTML - 第一次加载时正常工作 -->
    </template>
</div>

<div x-data="appState" x-init="retrieveIncompleteEventsTable()">
    <x-button class="ml-3" x-bind:disabled="event.disabled" x-on:click="loadEvent(unloadedEvents[index])">
        加载事件
    </x-button>
    <!-- 这能够将事件加载到SQL表中,但包含在具有retrieveLoadedEventsTable的div中的模板中的事件不会更新 -->
</div>

请注意,以上翻译只包含代码的主要部分,不包括其他信息。如果您需要进一步的解释或帮助,请随时提出具体问题。

英文:

I have something that i've been working on which I got working but I'm not really sure why, hoping you can help understand.

The idea was to essentially have a console box which I update with logs that I receive through laravel echo.

         &lt;div x-data=&quot;appState&quot; class=&quot;w-10/12 m-auto&quot; x-init=&quot;getLogs()&quot; x-show=&quot;consoleShow&quot;&gt;
            &lt;div
                class=&quot;coding inverse-toggle h-60 overflow-y-scroll px-5 pt-4 shadow-lg text-gray-100 text-xs font-mono subpixel-antialiased 
              bg-gray-800  pb-6 pt-4 rounded-lg leading-normal overflow-hidden&quot; id=&quot;consoleBox&quot;&gt;
                &lt;div class=&quot;top mb-2 flex flow-root static&quot;&gt;
                    &lt;div class=&quot;h-4 w-4 bg-red-500 rounded-full float-right cursor-pointer&quot; x-on:click=&quot;consoleShow = false&quot;&gt;&lt;/div&gt;
                &lt;/div&gt;
                &lt;div class=&quot;mt-4 flex&quot;&gt;
                    {{-- &lt;span class=&quot;text-green-400&quot;&gt;computer:~$&lt;/span&gt; --}}
                    &lt;p class=&quot;flex-1 typing items-center pl-2&quot;&gt;
                        &lt;template x-for=&quot;(log, index) in logs&quot; :key=&quot;index&quot;&gt;
                            &lt;li class=&quot;list-none&quot; x-text=&quot;log&quot;&gt;&lt;/li&gt;
                        &lt;/template&gt;
                        &lt;br&gt;
                    &lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

Then I within my appState in <script> tags

getLogs() {
                Echo.channel(&#39;logs&#39;).listen(&#39;.logs&#39;, (log) =&gt; {
                    // do what you need to do based on the event name and data
                    console.log(log);
                    this.logs.push(log.message.function+&#39;: &#39;+log.message.task+&#39; - &#39;+log.message.status);
                
                    window.dispatchEvent(new CustomEvent(&quot;scrollDown&quot;));



                });

            }

It took my quite a while to get working, essentially i had issues with the x-for updating with new additions to the logs var. However i managed to resolve it by adding getLogs() to the x-init of my container div.

My understanding was that the x-init was only there to execute when the element is rendered. I don't really understand why having the function in x-init would allow the x-for to update correctly. Could anyone provide some insights to help wrap my head around this?

Adding in a cutback example of the code:

const appState = {
events: [],


async loadEvent(event) {
                fetch(&quot;/admin/scrapeEvent&quot;, {
                        method: &quot;POST&quot;,
                        headers: {
                            &quot;Content-Type&quot;: &quot;application/json&quot;,
                            &#39;X-CSRF-TOKEN&#39;: document.head.querySelector(&#39;meta[name=csrf-token]&#39;).content,
                            Accept: &quot;application/json&quot;,
                        },
                        body: JSON.stringify(event)
                    })
                    .then(() =&gt; {
      
                    })
                    .catch(() =&gt; {
 
                    })
                    .finally(() =&gt; {
                        
                        this.retrieveLoadedEventsTable();  
                        
                    });
            },
 async retrieveLoadedEventsTable() {


                try {
                    let response = await fetch(&#39;/admin/findNewEvents/loaded&#39;);
                    let data = await response.json();

                    for (let item of data) item.disabled = true;
                    this.events = data;
                } catch (itsTheErrorObject) {
                    console.log(&quot;something went wrong&quot;);
                }

                

            }

}


&lt;div x-data=&quot;appState&quot; x-init=&quot;retrieveLoadedEventsTable()&quot;&gt;
&lt;template x-for=&quot;event, index in events&quot; :key=&quot;event.id&quot;&gt;
/** HTML that renders event.atrributes - Works fine on first load **/

&lt;/template&gt;
&lt;/div&gt;


&lt;div x-data=&quot;appState&quot; x-init=&quot;retrieveIncompleteEventsTable()&quot;&gt;
 &lt;x-button class=&quot;ml-3&quot; x-bind:disabled=&quot;event.disabled&quot; x-on:click=&quot;loadEvent(unloadedEvents[index])&quot;&gt;
Load Event
&lt;/x-button&gt;

/** This works fine to load my event into the sql table, i can confirm that it runs through and executes the finally in loadEvent, but the events in the template contained in the div with x-init with retrieveLoadedEventsTable does not update **/

&lt;/div&gt;

答案1

得分: 1

我的理解是,x-init 仅用于在元素渲染时执行。

不,x-init 指令 在元素初始化阶段被调用。在这一点上,Alpine.js 已经从 x-data 中创建了数据变量,但尚未渲染任何内容(尚未修改 DOM)。x-init 是推荐的位置,用于放置一个函数,例如调用一个 API,然后附加一些在 x-data 中定义的变量,或者创建一个事件监听器,以后会更改数据,就像在你的示例中一样。

在执行 x-init 中的函数后,Alpine.js 使用实际的 logs 变量渲染组件。当你的 getLogs() 函数中的事件监听器捕获到新事件并附加 logs 变量时,Alpine.js 检测到它并重新渲染 DOM 的相应部分。

英文:

> My understanding was that the x-init was only there to execute when the element is rendered.

No, x-init directive is called at initialization phase of an element. At that point Alpine.js has already created the data variables from x-data but it has not rendered anything (has not modified the DOM) yet. The x-init is the recommended place to put a function that e.g. calls an API then appends some variables defined in x-data or to create an event listener that (later) mutates the data like in your example.

After executing the functions from x-init, Alpine.js renders the component with the actual logs variable. When the event listener in your getLogs() function catches a new event and appends the logs variable, Alpine.js detects it and re-renders the respective parts of the DOM.

huangapple
  • 本文由 发表于 2023年3月12日 19:58:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75712946.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定