indrimuska / angular-selector

A native AngularJS directive that transform a simple <select> box into a full html select with typeahead.
http://indrimuska.github.io/angular-selector
MIT License
96 stars 36 forks source link

Validate input after manual update of model (`value`) using remote fetching #40

Closed indrimuska closed 8 years ago

indrimuska commented 8 years ago
This is to choose the behavior of Angular Selector in a remote configuration after model update performed "manually" by the user.

Scenario

Let's take this example as scenario. The only requirement is to set a remote configuration to fetch the results.

<select selector
        multi="true"
        model="browsers"
        value-attr="value"
        remote="getBrowsers(search)">
</select>
$scope.browsers = [];
$scope.getBrowsers = function (search) {
   if (!search) return $q.resolve([]); // no text gets no results
   else return $q.resolve([
      { value: "GC", label: "Chrome" },
      { value: "FF", label: "Firefox" },
      { value: "AS", label: "Safari" },
      { value: "IE", label: "Internet Explorer" }
   ]);
};

Update

Using the example above, let's consider a user that updates $scope.browsers manually.

<button ng-click=" browsers = ['FF'] ">
   Set to <code>[FF]</code>
</button>
<button ng-click=" browsers = null ">
   Set to <code>null</code>
</button>

The problem

How should behave Angular Selector after that browsers have been changed?

  1. It should ignore the update and keep the previous value.
  2. It should check if the value is allowable (requires a remote request every time the model changes) and update the item view
  3. Anything else I'm not considering...

    The plunker

http://plnkr.co/edit/IFvuGySSfwoOKvgkcVX1?p=preview

andreialecu commented 8 years ago

There is an error in the plunker. It's not loading properly. Try it in incognito more, maybe you're seeing a different version.

indrimuska commented 8 years ago

Yes, you're right, I forgot $q.resolve([]) in script.js. I've updated the plunker now, check it out.

andreialecu commented 8 years ago

Regarding option 1, keeping the previous value does not seem like proper behavior. It would simply disable loading persisted data for most forms, so you wouldn't be able to edit anything but only create things.

I don't see any other option here than the second one.

indrimuska commented 8 years ago

Option 2 generates another big problem: How dow you check if the new value is correct? Obviously you need to launch a remote request in the same way you check if an option exists when users types something. But what if the value of each options is completely different from the corresponding label? What if I look for "FF" in the server side and no labels match this string?

To be more clear, let's take this other example. If your (remote) option array is like this:

[
   { "value": 4, "label": "bar" },
   { "value": 6, "label": "foo" }
]

if I type "ba" then I will get just one option in the dropdown list, but if I set the model to 4 manually, then it will be set back to [] because the server does not have any option with label that matches 4. So, you can't handle value validation in the same way you handle searches.

andreialecu commented 8 years ago

That is why I suggested in https://github.com/indrimuska/angular-selector/issues/34#issuecomment-202880315 that a new attribute is introduced which takes a function which defines what happens in this scenario (I named it remote-set in that example, but it can be whatever..., remote-load?)

The server will know what 4 is supposed to be, but it will come from a different call than from the free-text search one.

indrimuska commented 8 years ago

Since search and validation must return the full (filtered) objects array, I was thinking about using the same remote attribute for both requests, adding a second parameter:

remote(search, value)

This way, we can use value to return the objects that matches the needed requirements. Obviously, the second parameter will be passed to the function only for validation purpose, so that backend filtering queries won't be affected.

What are your thoughts?

indrimuska commented 8 years ago

This is the plunker showing the above behavior: http://plnkr.co/edit/u8zgR4I2vAIGaIlLQzgb?p=preview

This allows to validate every value set by manual update using a custom validation function.

<select selector [...] remote="getBrowsers(search, value)"></select>
$scope.getBrowsers = function (search, value) {
    // value not set -> search items (as usual)
    if (!value) return $browsers.search(search);

    // otherwise -> perform validation
    else return $browsers.validate(value);
};
.service('$browsers', ['$q', '$filter', function ($q, $filter) {
    var query = $filter('filter');
    var myDatabase = [...];

    function Browsers(){}
    Browsers.prototype.search = function (search) { ... };
    Browsers.prototype.validate = function (value) { ... };

    return new Browsers();
}])
indrimuska commented 8 years ago

In the end, I think that the solution proposed by @andreialecu would be the best, so that's why I will add some new attributes to Angular Selector in the next release: remote-validation and remote-validation-param. They will work in the same way remote and remote-param do. Any advice is welcome. :)

This will also let users define a default value in any remote scenario. Plunker: http://plnkr.co/edit/cvlh2djPAR74fsdCLStN?p=preview

indrimuska commented 8 years ago

Release v1.3.10 contains new attributes to solve this issue (also #34, #38).

Plunker: http://plnkr.co/edit/sJk7uHJ3J3THjc5ilA43?p=preview Please, check out ["Remote fetching and validation" example]() to understand how it works.