alpinejs / alpine

A rugged, minimal framework for composing JavaScript behavior in your markup.
https://alpinejs.dev
MIT License
28.34k stars 1.23k forks source link

Gor error with method for rows added programatically #1127

Closed sergeynilov closed 3 years ago

sergeynilov commented 3 years ago

Hello, in my Laravel 8 / tailwindcss 2 / Alpinejs 2.8 app on the page I load listing of data with axios and fill my template in alpinejs template circle. Listing is rendered ok, but any item has icon with function revokeAdCategory assigned to this icon. Problem when I click on this icon to run revokeAdCategory function :

        <div class="editor_form_wrapper w-10/12" x-data="adminAdEditorComponent()">
           ...
              <span x-text="getEditorTitle()"></span>
           ...
                    <div x-show="activeTab === 2" class="p-2">
                        Tab #2 : Categories
                        <div id="div_ad_categories">
                            <table x-show="adCategories.length">
                                <tbody>

                                <template x-for="nextCategory in adCategories" :key="nextCategory.id">
                                    <tr>
                                        <td class="editor_listing_cell" x-text="nextCategory.name">
                                        </td>

                                        <td class="editor_listing_cell">
                                            <span x-show="nextCategory.isSelected">
                                            <a href="#" @click.prevent="revokeAdCategory( nextCategory.id, nextCategory.name)">
                                                <x-icon-svg :icon="'clear'" title="Exclude category from this ad "></x-icon-svg>
                                                <b>Assigned</b>&nbsp;
                                            </a>
                                            </span>

        ...
        function adminAdEditorComponent() {
            console.log('adminAdEditorComponent::')

            return {
                adCategories: [],
                showDatepicker: false,
                ...
                getEditorTitle: function () {
                    if (parseInt('{{$isInsert}}') > 0) {
                        return "Add ad"
                    } else {
                        return "Edit ad"
                    }
                }, // getEditorTitle: function (component) {

                loadAdCategories: function () {
                    window.axios.get('/admin/ads/{{ $ad->id }}/ad_categories', {}).then((response) => {
                        console.log('response.data::')
                        console.log(response.data)
                        adCategories = response.data.categories
                    }).catch((error) => {
                        console.error(error)
                    })
                ...
                revokeAdCategory :function (adCategoryId, category_name) {
                   if (confirm('Do you want to revoke "' + category_name + '" category from this ad ?' ) ) {
                       window.axios.delete('/admin/ads/{{ $ad->id }}/ad_categories/' + adCategoryId, {}).then((response) => {

And in the console I got error :

alpine.min.js:7 Alpine Error: "ReferenceError: revokeAdCategory is not defined"

Expression: "revokeAdCategory( nextCategory.id, nextCategory.name)"
Element: <path stroke-linecap=​"round" stroke-linejoin=​"round" stroke-width=​"2" d=​"M9 13h6m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">​</path>​
u @ alpine.min.js:7
(anonymous) @ alpine.min.js:7
Promise.catch (async)
d @ alpine.min.js:7
(anonymous) @ alpine.min.js:7
evaluateCommandExpression @ alpine.min.js:7
z @ alpine.min.js:7
l @ alpine.min.js:7
VM5335:3 Uncaught (in promise) ReferenceError: revokeAdCategory is not defined
    at eval (eval at d.el (alpine.min.js:7), <anonymous>:3:21)

This page has several methods like getEditorTitle, loadAdCategories inside of adminAdEditorComponent and they work ok. Error with revokeAdCategory for rows added programatically. In the browser I see : https://prnt.sc/104lbtj

Why error and how can it be fixed?

Thanks!

KevinBatdorf commented 3 years ago

Does it work if you switch out your table elements for divs?

sergeynilov commented 3 years ago

Not sure what do you mean. Could you please detailze ?

KevinBatdorf commented 3 years ago

Change <table>, <tbody>, <tr>, and <td>, and all of their closing tags for <div> and </div> respectively. Then see if it works.

sergeynilov commented 3 years ago

I tried to remake this code without table tages, as :

                    <div x-show="activeTab === 2" class="p-2">
                        Tab #2 : Categories
                        <div id="div_ad_categories">
                            <div x-show="typeof adCategories != 'undefined' &&  adCategories.length">

                                <template x-for="nextCategory in adCategories" :key="nextCategory.id">

                                    <div class="p-2 pt-6 flex">
                                        <div class="w-3/12 justify-start align-top">
                                            <span class="editor_listing_cell" x-text="nextCategory.id">
                                            </span>
                                        </div>
                                        <div class="w-3/12 m-1 flex justify-start items-center align-top">
                                            <span class="editor_listing_cell" x-text="nextCategory.name">
                                            </span>
                                        </div>
                                        <div class="w-3/12 m-1 flex justify-start items-center align-top">
                                            <span class="editor_listing_cell" x-text="nextCategory.isSelected">
                                            </span>
                                        </div>
                                        <div class="w-3/12 m-1 flex justify-start items-center align-top">
                                            <div x-show="nextCategory.isSelected">
                                            <a @click.prevent="revokeAdCategory(nextCategory)">
                                                <x-icon-svg :icon="'clear'" title="Exclude category from this ad "></x-icon-svg>
                                                <b>Assigned</b>&nbsp;
                                            </a>
                                            </div>
                                            <div  x-show="!nextCategory.isSelected">
                                                <a @click="assignAdCategory( nextCategory.id, nextCategory.name)">
                                                    <x-icon-svg :icon="'category'" title="Assign category to this ad"></x-icon-svg>
                                                </a>
                                            </div>
                                        </div>
                                    </div>

                                </template>
                            </div>

                        </div> <!-- div_ad_categories -->

                    </div>

But clicking on the icon I got : alpine.min.js:7 Alpine Error: "ReferenceError: revokeAdCategory is not defined" What I see in the console : https://imgur.com/a/MUmacDS

I tried to modify line as : <a @click="revokeAdCategory(nextCategory)"> Or : <a onclick="revokeAdCategory(nextCategory)">
Or : <a onclick="javascript:revokeAdCategory(nextCategory)"> Also I tried to get rid rid of <a :

   <div x-show="nextCategory.isSelected" @click="revokeAdCategory(nextCategory)">
       <x-icon-svg :icon="'clear'" title="Exclude category from this ad "></x-icon-svg>
       <b>Assigned</b>&nbsp;
   </div>

Also I upgrated to v2.8.1, but the samer error.

Any ideas?

KevinBatdorf commented 3 years ago

I don't have any ideas. Sometimes with table elements, if not structured properly the browser will move them around putting some items out of scope. But if you switch to div elements and still get then it's not that.

Can you put it online? On codepen, for example?

Otherwise I suggest you simplify the code heavily until it works, then slowly add pieces back until it breaks.

sergeynilov commented 3 years ago

Thanks for you feedback! I tried to simplify my data listing and found that my code works ok if it is a simle listing of data and javascript functions are called ok.

But as I have a tab element with several tabs, like

                    <div x-show="activeTab === 1" class="p-2">
                    </div>
                    <div x-show="activeTab === 2" class="p-2">
                        Tab #2 : Categories
                        <div id="div_ad_categories">
                            <div x-show="typeof adCategories != 'undefined' &&  adCategories.length">
                             ... adCategories Listing here

                    <div x-show="activeTab === 3" class="p-2">
                        Tab #3 : Locations

                        </div> <!-- <div id="div_ad_locations">-->

                    </div>
                    <div x-show="activeTab === 4" class="p-2">
                        Tab #4 : Images
                    </div>

My listing is rendered ok but error calling Javascript function I wrote above in this case. Maybe reason is that this listing is hidden when page is inited or something like that?

Also did you work/test Alpinejs with tailwindcss 2 ?

SimoTod commented 3 years ago

Without seeing the complete markup and being able to replicate the error, it's hard to tell. Any chance you can create a broken example on codepen?

sergeynilov commented 3 years ago

Please take a look at https://codepen.io/sergeynilov/pen/yLVvdzW

SimoTod commented 3 years ago

@sergeynilov thanks for creating that codepen. So you defined a first component, which has access to your functions, but internally you defined a second component (x-data) with activeTab = 2. In Alpine (at least v2), nested components don't have access to the scope of the parent component out of the box. You need to rethink your code or you can include https://github.com/alpine-collective/alpine-magic-helpers and use $parent.revokeAdCategory. See https://codepen.io/SimoTod/pen/vYyRYBX The alpine magic helpers library is a community project so if you decide to use it and you find a bug, please make sure you open the issue on the relevant repositor.

sergeynilov commented 3 years ago

Great! Thanks!