Laravel-Backpack / CRUD

Build custom admin panels. Fast!
https://backpackforlaravel.com
MIT License
3.08k stars 885 forks source link

[Bug] Ajax Inline Create not working correctly #4840

Closed emogeekface closed 1 year ago

emogeekface commented 1 year ago

Bug report

What I did

Following the examples on this ticket: https://github.com/Laravel-Backpack/CRUD/pull/4714

I created a new widget file and added this to my Parent Crud controller (Award) and to my child crud controller (Criteria) as Widget::add()->type('script')->inline()->content('assets/js/admin/forms/criteria.js');

with example content:

crud.field('measurement').onChange(function(field) {
    if (field.value == 'points') {
        crud.field('from_label').show();
        crud.field('of_label').hide();
    } else {
        crud.field('from_label').hide();
        crud.field('of_label').show();
    }
}).change();

What I expected to happen

  1. When changing the value of the measurement field on creation of a child record, the labels would show and hide based on the value of measurement.
  2. When changing the value of the measurement field on creation of a child record via InlineCreate on the parent record, the labels would show and hide based on the value of measurement.

What happened

  1. When changing the value of the measurement field on creation of a child record, the labels would show and hide based on the value of measurement. - Correct
  2. When creating/editing my parent record, I receive the following console errors and the fields are not hidden/shown as required:
CrudField error! Could not select WRAPPER for "measurement"
CrudField error! Could not select INPUT for "measurement"

What I've already tried to fix it

Is it a bug in the latest version of Backpack?

After I run composer update backpack/crud the bug... is it still there? - Yes

Backpack, Laravel, PHP, DB version

When I run php artisan backpack:version the output is:

### PHP VERSION:
PHP 8.1.7 (cli) (built: Jun 25 2022 08:13:46) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.7, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.7, Copyright (c), by Zend Technologies
    with Xdebug v3.1.4, Copyright (c) 2002-2022, by Derick Rethans

### LARAVEL VERSION:
v9.43.0@011f2e1d49a11c22519a7899b46ddf3bc5b0f40b

### BACKPACK VERSION:
5.4.11@ffbadcdb6478822646600b2cd763490caa927155
welcome[bot] commented 1 year ago

Hello there! Thanks for opening your first issue on this repo!

Just a heads-up: Here at Backpack we use Github Issues only for tracking bugs. Talk about new features is also acceptable. This helps a lot in keeping our focus on improving Backpack. If you issue is not a bug/feature, please help us out by closing the issue yourself and posting in the appropriate medium (see below). If you're not sure where it fits, it's ok, a community member will probably reply to help you with that.

Backpack communication channels:

Please keep in mind Backpack offers no official / paid support. Whatever help you receive here, on Gitter, Slack or Stackoverflow is thanks to our awesome awesome community members, who give up some of their time to help their peers. If you want to join our community, just start pitching in. We take pride in being a welcoming bunch.

Thank you!

-- Justin Case The Backpack Robot

pxpm commented 1 year ago

Hey @emogeekface can you share your field definition and what type of relation it is ?

Cheers

emogeekface commented 1 year ago

@pxpm Sure thing:

So these are the fields from above in my CriteriaCrudController (child)

$this->crud->addFields([
          [   // select2_from_array
                'name'        => 'measurement',
                'label'       => "",
                'type'        => 'select2_from_array',
                'options'     => $measurementOptions,
                'allows_null' => false,
                'allows_multiple' => false,
                'wrapper' => [
                    'class' => 'form-group col-md-3',
                ],
            ],
            [   // CustomHTML
                'name'  => 'of_label',
                'type'  => 'custom_html',
                'value' => '<p class="form-input-spacer">of</p>',
                'wrapper' => [
                    'class' => 'form-group col-md-1',
                ],
            ],
            [   // CustomHTML
                'name'  => 'from_label',
                'type'  => 'custom_html',
                'value' => '<p class="form-input-spacer">from</p>',
                'wrapper' => [
                    'class' => 'form-group col-md-1',
                ],
            ],
]);

And then the relationship on my AwardCrudController (parent):

$this->crud->addFields([
    [
        'name' => 'name',
        'label' => 'Grantable Award Level Name',
        'type' => 'text',
    ],
    [
        'name'  => 'criteria',
        'type'  => 'relationship',
        'ajax' => true,
        'inline_create' => [
            'include_main_form_fields' => ['id']
            ],
        'data_source' => backpack_url('award/fetch/criteria'),
    ]
]);

My Model relationship on my Award Model is:

public function criteria() {
        return $this->belongsToMany(Criteria::class);
}

And on my Criteria Model:

public function award() {
        return $this->belongsTo(Award::class, 'award_id', 'id');
}
pxpm commented 1 year ago

Hello @emogeekface thanks for such good details. 🙏

I've reproduced your use-case, and I think there is some misunderstanding on the usage of Crud JS API. I will try to explain what I mean: Crud JS Api is not designed to manipulate html elements in the page, but to manipulate crud inputs.

Translated it means that the Crud JS api is only capable of manipulating elements that follow the crud field basic structure, that is including a wrapper etc. You can have a look here on what I mean by field structure: https://github.com/Laravel-Backpack/Generators/blob/main/src/Console/stubs/field.stub

The JS parts etc are optional, but this is pretty much mandatory to work with Crud JS API.

@include('crud::fields.inc.wrapper_start')
    <input type="text"
        name="{{ $field['name'] }}"
        value="{{ $field['value'] }}"
        @include('crud::fields.inc.attributes')>
@include('crud::fields.inc.wrapper_end')

You are returning just <p></p> from your custom html, and the library cannot interact with those elements alone, like I explained above.

Also, another note for reference, you don't need to add the JS Widget to both cruds. When you specify ->inline() in your child crud, it means that the JS Widget will be included when that entity is created inline, but also in the regular create/update operations. You may have other scripts that should only load in that entity create/update pages, for that you omit the ->inline() and that script would not be available during the inline creation of the entry.

I've just tested the InlineCreate with the same relations and similar scenario to yours (but with real fields) and it worked, so I expect that if you adapt your html to work with the JS api, it would work for you too.

Please notice that some fields need to be handled by custom events on their own files, you will find examples of them in alot of our fields and in the JS api file where they trigger: https://github.com/Laravel-Backpack/CRUD/blob/d97c8e65c4944d5c798437cfc6548e4ba5adbf4f/src/resources/views/crud/inc/form_fields_script.blade.php#L112

I will have a look at the docs to check if they are misleading somehow, and probably make it more noticeable of what I described above to avoid this confusions.

Sorry it took so much time to detect it, I didn't spot it with naked eye too 👀

I will be closing this, I don't think there is any bug here, rather a bad implementation (probably on our side, in docs). I will fix it in a few moments.

Wish you the best @emogeekface 🎅

emogeekface commented 1 year ago

Hi @pxpm

Thanks for coming back to me.

Also, another note for reference, you don't need to add the JS Widget to both cruds. When you specify ->inline() in your child crud, it means that the JS Widget will be included when that entity is created inline, but also in the regular create/update operations.

When I remove the JS widget from my parent crud controller (Award) and include it only in my child controller (Criteria) then it is not loaded in at all.

You are returning just <p></p> from your custom html, and the library cannot interact with those elements alone, like I explained above.

If I remove any input manipulation completely and just try to log out a value on change, the value is not logged as the input change is not being picked up.

crud.field('measurement').onChange(function(field) {
    if (field.value == 'points') {
        console.log('Points');
    } else {
        console.log('Not Points');
    }
}).change();

I've just tested the InlineCreate with the same relations and similar scenario to yours (but with real fields) and it worked, so I expect that if you adapt your html to work with the JS api, it would work for you too.

Would it be possible for you to share this with me so that I can take a look at what you're doing differently to me?

pxpm commented 1 year ago

Sure, I will describe how I did:

So my Parent crud is Monster -> BelongsToMany -> Product (child). This is the field definition on Monster (parent):

https://github.com/Laravel-Backpack/demo/blob/020947d5ddc7b3f8000a838de4c6f28b4530d89c/app/Http/Controllers/Admin/MonsterCrudController.php#L913

In the Product (child), I added in the setupCreateOperation :

Widget::add()->type('script')->inline()->content('assets/js/products/product.js');

When I go to Monster (parent) and click the + Add Item in the child input, the Widget would be loaded, and the JS API works normally.

If you are having issues, make sure you don't have any overriden backpack files (specifically form_content.blade.php) in your resources folder that are preventing you from getting any updates on this file.

Also a php artisan backpack:publish-assets may be needed if you didn't do it for some time.

You can reproduce this scenario in our Demo just by adding the bits I described above.

Let me know if I can help you with something else. 👍

Wish you the best 🎅