dependent() not setting new value

dependent() not setting new value

pisislerpisisler Posts: 91Questions: 19Answers: 1
edited February 2022 in Editor

Hi.

Per the example of the documentation in dependent(), I wrote something like:

editor.dependent('field1', function (val, data, callback, e ) {
    // Check if field data has changed.
    // (Is it intended to check this? Because otherwise this block works at all times even if field1 is not modified.)
    if (data.row.field1 != data.values.field1)
        editor.field('field2').set('some value');
    callback(true);
});

field2 is never set. I confirmed that it steps into the if condition by logging the "some value" into the console in the if block, but never sets the value for the field. I also tried assigning data.values.field2 manually to "some value" to still no use.

By the way I saw that some other people asked the same question before but none are answered. I resolved the problem with another approach like this for now:

editor.on('preSubmit', function (e, json, action) {
    // Detect which row is being edited.
    let row_id = Object.keys(json.data)[0];

    // Detect if the edited cell is we want to write a dependent for.
    if (action == 'edit' && editor.display() == 'inline' && editor.displayed()[0] == 'field1') {
        // Do some calculations here..
        // And alter the json data object directly before sending to server.
        json.data[row_id].field2 = 'some value';
    }
    return !editor.inError();
});

Answers

  • rf1234rf1234 Posts: 2,524Questions: 79Answers: 369
    edited February 2022

    Something else must be wrong with your code, I guess. I use "dependent" all the time to set variables, message texts retrieved from the server etc. Here is an example that handles an array of two variables at the same time:

    editor
        .dependent(['ctr.end_date', 'ctr.expired'], function (val, data, callback) {
            return ctrEndDateStandardDependencies(this);
        })
    

    And the functions used:

    function ctrEndDateStandardDependencies(that) {
        if ( that.val('ctr.end_date') > '' && that.val('ctr.expired') < 1 ) {
            that.show(['ctr.automatic_prolongation', 'ctr.follow_up_days']);
        } else {
            that.set({'ctr.automatic_prolongation': 0,
                      'ctr.follow_up_days': ''})
                .hide(['ctr.automatic_prolongation', 'ctr.follow_up_days']);
        }
        if (that.val('ctr.end_date') > '' && that.val('ctr.automatic_prolongation') == 1) {
            that.show(['ctr.prolongation_months', 'ctr.last_day_month']);
        } else {
            that.set({'ctr.prolongation_months': 0, 'ctr.last_day_month': 0})
                .hide(['ctr.prolongation_months', 'ctr.last_day_month']);
        }
        
        if ( that.val('ctr.end_date') > '' &&
             that.val('ctr.automatic_prolongation') > 0 &&
             that.val('ctr.prolongation_months')    > 0      ) {
            return getAutomaticNextCtrEndDate(that);
        }    
    }
    
    function getAutomaticNextCtrEndDate(that) {    
        $.ajax({
            type: "POST",
            url: 'actions.php?action=automaticNextCtrEndDate',
            async: true,
            data: { endDate:            that.val('ctr.end_date'),
                    prolongationMonths: that.val('ctr.prolongation_months'),
                    lastDayMonth:       that.val('ctr.last_day_month')
            },
            dataType: "json",
            success: function (data) {
                return that.field("ctr.prolongation_months").message(data.message);
            }
        });
    }
    
  • pisislerpisisler Posts: 91Questions: 19Answers: 1
    edited February 2022

    Thank you for taking time to reply. I think there are two fundamental points I would like to talk about in your post.

    1. Your code should not work per the tech note here: https://datatables.net/manual/tech-notes/16 It says editor needs your dependency code to return an object which the editor can interpret, which your code lacks. And by which, otherwise, editor will be stuck in "processing" state which exactly happens in my instance when I use your approach. Because I see that you don't return anything in your custom callback in some conditions. This gets editor stuck which is perfectly normal per the tech note. (In which it recommends something like callback(true); for the dependency to let the editor continue.)
    2. field().set() is a very core API method of the editor. For this not to work, I must have broke a very vital part of it; which shouldn't be the case as there are many customizations and the core work pretty good.

    Now interestingly, I tried it without any conditioning (which I am sure was working, because I could log the val in that if block to console), now it works but with a possible bug.

    editor.dependent('field1', function (val, data, callback ) {
        // Set field2 value
        editor.field('field2').set('some value based on <val>');
        // Continue editor's normal operation
        callback(true);
    });
    

    Now the possible bug I mention here is, field2 is set whether field1 succeeds or not. I mean there are some custom validators on both fields. Even if field1 fails in one of the validators, field2 is set successfully. Should it really? Why doesn't it regard the result of its dependency? Maybe it is by design, but we should reconsider the meaning of the word "dependent" here. Maybe I should file a bug report?

    And another note here; I still need those conditions to see if the value of field1 is changed. Because I have some validators integrated to 3rd parties set for field2. So if nothing has changed in field1, the validators for field2 shouldn't fire. But it does.

  • allanallan Posts: 57,730Questions: 1Answers: 9,203 Site admin

    I was curious about this so I just tried it out and it seems to work as expected: http://live.datatables.net/jorutoji/25/edit .

    Click a row and edit it. Then change the "Name" field value and tab or click out (to trigger a change event) and the Salary field will correctly update.

    If my little example and @rf1234's code doesn't help, could you link to a test case showing the issue or modify the example to demonstrate the issue please?

    Thanks,
    Allan

  • pisislerpisisler Posts: 91Questions: 19Answers: 1
    edited February 2022

    Thanks Allan. In your test instance, I edited the name but the salary field didn't set. I don't get how it is working? See your example, I edited the name field but salary still has the old value and not the new value you set through dependent()

    Edit: Alright, now I understand what you mean but I think this is a very unreliable way. If you edit the name and hit "Save" button, it doesn't work. You have to click somewhere else to trigger the change as you said. But I think this is very wrong, don't you think? I mean clicking on save button doesn't trigger the change and so it doesn't set the value.

  • kthorngrenkthorngren Posts: 17,754Questions: 25Answers: 4,215

    Edit the name then go to another field by clicking in it or tabbing. You should see the Salary change to 55555. Does it not change for you?

    Kevin

  • pisislerpisisler Posts: 91Questions: 19Answers: 1
    edited February 2022

    It changes, but I think it shouldn't work this way. You shouldn't be telling all of your users "to click somewhere else before saving", should you?

    Plus, as I said before, dependent() sets the new value even if the the field which it depends on doesn't pass validation. I humbly think dependent() needs a complete rework with a different logic.

    Edit: I mean:
    1- It should regard whether if the field which we depend on passes the validation and is successfully sent to server. It doesn't regard whether that field fails or passes now.
    2- It should work whether you click somewhere else or not. It works only when you trigger the change intentionally. This is not viable and you can't expect end-users to know and apply these technical expectations.
    3- It shouldn't trigger if there is no change. It triggers now.

    Check the code I gave in my first post, that I use instead of dependent(). It regards the field's validation before it sets the value of the other cell. It works without expecting to trigger the change manually. And it doesn't trigger if there is no change in the field's data.

    Second edit: My code doesn't regard the field's validation either. I thought it did becuase I use the same validator for both of the fields. So if one of them can't pass, the other likely fails too. So this problem remains unsolved.

  • kthorngrenkthorngren Posts: 17,754Questions: 25Answers: 4,215

    It changes, but I think it shouldn't work this way. You shouldn't be telling all of your users "to click somewhere else before saving", should you?

    I see. It seems there is an issue if a change is made to the name and enter is hit. The dependent code runs but the field value doesn't seem to be update. All other cases, click update or move to another field, the field is updated. I updated the test case (with console.log) to show this issue, you can see it executes the editor.field('salary').val(55555); statement but its not set. @allan will need to comment on this.

    Plus, as I said before, dependent() sets the new value even if the the field which it depends on doesn't pass validation.

    In Allan's test case I can edit a row not make a change and click update, move to another field or hit enter and no changes are made. Can you provide the steps to show this problem?

    Kevin

  • colincolin Posts: 14,618Questions: 1Answers: 2,497
    edited February 2022

    You can add additional events here, too, such as keyup. This is Allan's example, modified to use both keyup and change - http://live.datatables.net/bugavolo/1/edit

    That seems to be nearer to what you're looking for.

    Colin

  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    Now here is another news, as you noticed, on the test case, if you change the name and click "Update" button with the mouse, then dependent sets the value for the other field, but instead of clicking the button, if you press enter key to save, now dependent doesn't work.

    For the other problem, we can't add server side script to the test case, so I can't show you on it. But to test it simple add a minNum() validator on a field and write dependent() to depend on this field. Now type an alphanumeric value to this field and try to save. It will fail giving you this message; "input not valid". But in the mean time, dependent will still work. Of course you can say that the developer should do the checks before saving anything in dependent() BUT the developer would never know if that field which we depend of failed or passed the validation.

  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    @collin yeah I had done it that way before. But as I said, I have my dependency connecting to a 3rd party integration site. I just can't send post incomplete data to 3rd party every time you fire keyup event.

    In my honest opinion again, dependent() logic needs to change. Because this way, it is not really depending.

  • allanallan Posts: 57,730Questions: 1Answers: 9,203 Site admin

    I'm not quite sure what you are looking for in that case. If you don't want it to trigger on every keystroke, but you need it to happen more frequently than the change event. Do you just want to have it lookup the value on submit? In which case your original solution of using preSubmit would be the correct way to do it.

    I might have misunderstood - if you could clarify please, that would be great.

    Thanks,
    Allan

  • kthorngrenkthorngren Posts: 17,754Questions: 25Answers: 4,215

    @allan I do have a question. In your test case the Salary field is not updated if I update the name and just hit enter. I added a console.log statement to show that the
    editor.field('salary').val(55555); statement executes but the update is not there. Is this expected?

    http://live.datatables.net/jorutoji/26/edit

    Kevin

  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    I might have misunderstood - if you could clarify please, that would be great.

    In fact as I told before, in my opinion dependent() as it name suggests should really be "dependent". Like;

    1- It should regard the validation of the field it depends on. Right now, it doesn't regard it and it runs the code block even if that field fails to validate. This way, it actually wouldn't have depended on that field. I mean if it is going to run that code block at all times anyway, without depending on that field, then why need it and call it "dependent"? As far as I can see, there is also no way that developer could know if the validation of the field we are depending on failed or passed.

    2- As I said before (like in @kthorngren's live instance), submitting the editor with enter button doesn't trigger dependent(). (Hence it is not reliable in inline editing mode either.) We shouldn't be expecting the end-user to understand and regard all this tech stuff and trigger change consciously. So dependent() should be triggering in a way like preSubmit does, without the need of end-user's conscious behavior.

    3- Developer shouldn't need to check if there is a change of value. Rather dependent() should run only when something has changed. Because if I am depending on a field, that already means I would like to do some operation only when something has changed in the data I am depending on. But right now, dependent() runs the code block even if nothing has changed.

  • rf1234rf1234 Posts: 2,524Questions: 79Answers: 369

    I am also following this discussion with great interest. This is my opinion on the points made above.

    1 - Field validation: I don't see any client side field validation in your code. What is your suggestion on how this should work in conjunction with "dependent" (Never an issue for me: I only do field validation server side. In addition: I only use "dependent" in conjunction with dropdowns and checkboxes. I don't have a single use case where "dependent" makes sense in conjunction with a free text field.)

    2 - Using "dependent" with dropdowns and checkboxes exclusively avoids this issue. No difference between either way of submission. And no need to click "outside" the changed field after changing it. That is done implicitly if you will after choosing from a dropdown or clicking a checkbox.

    3 - this point has been bothering me as well - but I got used to it and learned to live with it. And now: If it was changed to a more "reasonable" fashion I might even run into issues: I am relying on the fact that "dependent" is being executed on "open" as well ...

  • allanallan Posts: 57,730Questions: 1Answers: 9,203 Site admin

    @kthorngren

    statement executes but the update is not there. Is this expected?

    Kind of... The reason for it is that the blur event is async - so it is actually happening after the form submission, which happens on the click event (which then triggers the blur event after that!).

    "Fixing" that isn't going to be trivial. We might need to trigger a change event on all fields that have a dependent value, before submission, but this has two problems - it makes the submit async (possibly not an issue for the button since that itself is an async action triggered by the end user), and it might also cause unneed processing such as an ajax call, depending on what the dependent() does.

    @pisisler I think the issue is knowing when the value has "changed". By default we listen for the change event (which the browser will trigger on blur when the value has changed) - for text inputs that can be changed to keyup (which is obviously no use for select elements).

    I absolutely see the issue here - thanks for the clarification. I need to have a think about how we can implement a solution for this.

    Allan

  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    @rf1234 , no I don't have client-side validators, I am using server-side validators just like you do.

    @allan , I think it is still not depending on a change, because the code block in dependent() always run, whether there is a change or not. And this means it runs even if there is no change during the submission of the form.

    Maybe I should explain my use case to give better idea.

    I have a price field (let it be price1) which is integrated to a third party. My (server-side) validator tries to update the third party web service with the given field value. If the web service returns true, then the validator also returns true to allow the editor to save the new value. If the third party web service returns false, then my validator returns the error message it received from the third party; allowing the user to know what happened and allowing the editor to discard the change.

    Now there is another price field (let it be price2) which is integrated to another third party by almost the same approach with the first price field.

    The thing is, price2 depends on price1. So it actually works this way:

    -> User enters a price value to the first price field. (price1)
    -> I calculate another value for price2. (Using preSubmit, see the first post.)
    -> User saves the form
    -> Server-side validator runs for price1, which sends the data to a 3rd party.
    -> Server-side validator runs also for price2, which sends the data to another 3rd party.
    -> If everything goes well, editor saves the new values for both price1 and price2.

    If either of the values fail to succeed on 3rd parties or anything goes wrong with the submission, editor doesn't save the new value for neither of the fields.

    Now the problem with this use case is;

    -> One of the fields could actually succeed but if the other one failed, then editor couldn't have saved the new value for the field which succeeded. But rather it discards changes of both fields. For example price1 succeeded but price2 failed. Now the third party has the new value, but editor haven't saved it, as one of validators returned false. (This is of course not a bug, this is how it is designed currently.)

    -> Dependent code block runs even if there is no change in the field which it depended on. (price1 in this case.)

    What could be the solution? Theoretically;

    If dependent() regards the validator of price1 and runs only when it succeeds (since it is depending on it), then it runs only if there is a change of value in price1 and operates on price2 only if price1 succeeds.

    Why I call it "theoretically"? Because the problem with this solution is that it requires dependent() to have a server-side equivalent :smile: I think there is no other way that it could work in this way on client-side. Otherwise it will require to submit the form twice. (Like submit to see if field1 succeeded and now submit field2 which depended on field1)

  • rf1234rf1234 Posts: 2,524Questions: 79Answers: 369

    The thing is, price2 depends on price1.

    -> One of the fields could actually succeed but if the other one failed, then editor couldn't have saved the new value for the field which succeeded. But rather it discards changes of both fields. For example price1 succeeded but price2 failed. Now the third party has the new value, but editor haven't saved it, as one of validators returned false. (This is of course not a bug, this is how it is designed currently.)

    If prices 1 and 2 are interdependent you should do a complete rollback if one of the fields succeed and the other one doesn't. In that regard it is good that Editor discards all changes. (I wrote a lot of banking software some decades ago. Rollbacks were extremely important when updating various ledgers and one of the updates failed. At that time I had to do this stuff "manually" using indexed and sequential files - no database ...).

  • pisislerpisisler Posts: 91Questions: 19Answers: 1
    edited February 2022

    By the way, I think this is what it would look like if we solve it server-side:

    ...
    Field::inst('price1')->validator(Validate::minNum(0))->setFormatter(Format::ifEmpty(0))
        ->validator(function ($price, $product) { return update_platform1_price($fiyat, $product); }),
    Field::inst('price2')->validator(Validate::minNum(0))->setFormatter(Format::ifEmpty(0))
        ->validator(function ($price, $product) { return update_platform2_price($price, $product); }),
    ...
    
    function update_platform1_price($price, $product) {
        // Do the updating of platform1 web service, then:
        if (platform1 returns true from web service)
            return update_platform2_price();
    }
    
    function update_platform2_price($price, $product) {
        return what platform2 update procedure returns;
    }
    
    

    This way, there is still a risk in the case when platform2 fails to update; in which the editor will discard changes on both fields, showing the error of platform2. Now editor and platform1 3rd party is not synchronous.

    As per how editor is designed, something has to change fundamentally if we want to "solve" this. Right now, editor is designed to discard changes unless server-side returns true explicitly. Because if it is other than an explicit "true", editor considers "it is false and what it has in return must be the error message". (Which is perfectly normal by design.) But the way I am telling would require editor to check not only an explicit true but also differentiate "false" and "message" from each other.

    Like for example in the case I mentioned, saving the price1 field and displaying the error message for price2 field. This is actually not only about dependent(). There could be this case too; the new value passes validation but instead of returning an explicit true, it could return true with an additional message like:

    "Values are saved but something else happened that you need to consider/check whatever bla bla.. " (Ok)

  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    @rf1234 , absolutely. A rollback is also what I was thinking but forgot to mention that it has other drawbacks like;

    1- In my case, the regarding web service requires you to wait at least 60 seconds prior to sending another request. (There is another service which even requires 20 minutes :smiley:)
    2- Rollback could fail too. (I know we can't solve everything on one side, but we need to consider every probability.)

  • rf1234rf1234 Posts: 2,524Questions: 79Answers: 369
    edited February 2022

    ok, one more time :smile:

    3- Developer shouldn't need to check if there is a change of value. Rather dependent() should run only when something has changed. Because if I am depending on a field, that already means I would like to do some operation only when something has changed in the data I am depending on. But right now, dependent() runs the code block even if nothing has changed.

    After debating this with Kevin I came to the conclusion that "dependent" should indeed run even if nothing has changed INITIALLY. Otherwise the dynamic hiding and showing of fields "dependent" on the value of other fields wouldn't work when opening the form. (The issue only arises initially because after opening the form I don't see "dependent" being executed if nothing changes.)

    If you don't want the "dependent" code to be executed on opening of the Editor form (before anything can get changed) you can replace it by rather simple jQuery like this for example:

    editor
        .on('open', function(e, mode, action) {
            $( editor.field('yourField').node() ).change( function() {
                 ... do something "dependent" ...
            });
             // omit the following line if you don't want the code above
             // to be executed when opening the form:
             $( editor.field('yourField').node() ).change();
        })
        .on('close', function() {
            $( editor.field('yourField').node() ).off( 'change' );
        });
    
  • pisislerpisisler Posts: 91Questions: 19Answers: 1

    Otherwise the dynamic hiding and showing of fields "dependent" on the value of other fields wouldn't work when opening the form.

    Fair point. Then maybe dependent() could have another parameter to watch if there is a change. Or it stays this way and the developer has to check if there is a change, like I exemplified in the first post. But in this case, there is still a more robust way of checking needed because I think the way I did may not be multi rows editing aware.

    On the other hand, I am still confident with what I proposed about the development of the Editor. Right now, editor is evaluating the result like:

    true: Value saved, update the table view.
    else: It has failed, discard changes and display whatever the server returned.

    What I am saying that it might be something more handy like:

    // Everything is successful like an explicit true:
    { result: "success" }
    // Or it is successful but there is a notice:
    {
        result: "success",
        message: "Price saved but don't you think it is too expensive?"
    }
    // Or it has failed:
    {
        result: "failure",
        message: "Price is out of allowed range"
    }
    // Or even more, like field based:
    {
        field1: {
            result: "success"
        },
        field2: {
            result: "success",
            message: "This was the last time you are allowed to edit this field."
        }
        field3: {
            result: "failure",
            message: "Invalid value"
        }
    }
    

    @allan consider this :blush:

  • allanallan Posts: 57,730Questions: 1Answers: 9,203 Site admin

    Hi,

    Circling back to this as I've been looking at it for the changes to be made for Editor 2.1. Firstly, the issue with pressing return bypassing the dependent action has been resolved (by blurring the focus on submit if it is in a field's input).

    Secondly, regarding the different messages for errors in @pisisler's last post. All but the second will work out of the box with Editor as you can return success, a general error message or individual field error messages (details of the data format for that are available in the manual).

    The one thing that isn't built in, is the "success, but with a message" state. For that you could listen for submitComplete and check for that state and show the message in a notification box. It can't really be shown in Editor since the form is removed at that point due to the successful submission.

    Allan

Sign In or Register to comment.