leocaseiro / angular-chosen

AngularJS Chosen directive is an AngularJS Directive that brings the Chosen jQuery in a AngularJS way
http://leocaseiro.github.io/angular-chosen/
MIT License
682 stars 248 forks source link

Creating Select from JavaScript with $compile #260

Closed ghost closed 5 years ago

ghost commented 5 years ago

I am working on creating selectors on the fly based on ajax calls. I am trying something like this:

var html =
    "<div class=\"form-group mb\">" +
        "<label class=\"col-sm-2 control-label\">" + upperCaseFirst(name) + "</label>" +
            "<div class=\"col-sm-10\">" +
                    "<select chosen=\"" + name + "\" class=\"input-md chosen-select\"" +
                            "ng-model=\"inputs." + name + "\" " +
                            "ng-options=\"__value__ for __value__ in " + name + "\">" +
                        "<option value=\"\">Select a " +  upperCaseFirst(name) + "</option>" +
                "</select>" +
            "</div></div>"
angular.element(
    document.getElementById("my-form"))
        .append($compile(html)($scope))

However this fails to create a select control. It creates a small dropdown which does not render correctly and has no values. Is this possible using chosen and angular?

VanTanev commented 5 years ago

Sorry, this does not pertain to angular-chosen. Try the angular mailing list or #angularjs irc channel.

VanTanev commented 5 years ago

But, let me give you a pointer, just because:

It looks to me as if you're writing "jquery-in-angular" instead of writing angular code.

If you supply a complete specification of what you're building, I could give you a better pointer, but it sounds to me like you have an ajax that returns a data structure like:

{
  "inputs": [
    { "name": "my-input", "values": ["one", "two", "three"] }
  ]
}

( or at least, something to that effect).

A way to handle this in Angular would be:

// in controller or directive
$scope.models = {}
$http.get('/api/endpoint').then(res => res.data).then(data => {
  $scope.inputs = data.inputs
})
// in template
<div ng-repeat="input in inputs">
  <label>{{ ::input.name }}</label>
  <select chosen ng-model="models[input.name]" ng-options="value as value for value in input.values"></select>
</div>
ghost commented 5 years ago

Hi @VanTanev, thank you for the pointer. Regarding the code, the definition of the drop down itself is coming from the API, and cannot be written in the template in advance. It seems to me directly related to angular chosen (have you tried it?) I will check with the angular group. Thanks!

VanTanev commented 5 years ago

Hey @dwilliams999, it's fine if the definition comes from the API - the point of angular templates is to be programmable in and of themselves. You definitely do not need to manually compile and insert dom fragments to get this to work.

I have had to implement something similar in the past, it looked like this:

  # Options are a hash like::
  # {
  #   "option_name" => {
  #     "value" => "option value",
  #     "type" => "boolean/time/integer..etc",
  #     "label" => "Fancy label",
  #     (opt) "choices" => { "choice_val" => "Choice Label" }, # for type "selection"
  #     "help", "group", "position", "hidden", "allow_per_symbol", # field options, false if not set
  #   },
  #   (opt) "errors" => { "option_name" => ["array", "of", "error", "messages"] },
  # }
<div class="form-group" show-errors
     ng-repeat="(option_name, option) in options"
     ng-class="{'has-error': options.errors[option_name]}"
     ng-if="!option.hidden"
     >
    <label class="col-md-3 control-label" for="{{ ::option_name }}">{{ ::option.label }}:</label>
    <div class="col-md-8">
        <div ng-switch on="option.type">
            <select ng-switch-when="boolean"
                ng-options="opt.value as opt.label for opt in [{label: 'YES', value: true}, {label: 'NO', value: false}]"
                ng-model="option.value"
                id="{{ ::option_name}}"
                class="form-control"
                name="{{ ::option_name}}"
                required=required
            ></select>

            <input ng-switch-when="percent"
                ad-float
                type="text"
                ng-model="option.value"
                id="{{ ::option_name}}"
                class="form-control"
                name="{{ ::option_name }}"
                required=required
            />

            <input ng-switch-when="integer"
                ad-integer
                type="number"
                ng-model="option.value"
                id="{{ ::option_name}}"
                class="form-control"
                name="{{ ::option_name}}"
                required=required
            />

            <ad-time ng-switch-when="time"
                    ng-model="option.value"
                    ></ad-time>

            <select ng-switch-when="selection"
                chosen
                class="form-control"
                id="{{ ::option_name}}"
                name="{{ ::option_name}}"
                ng-model="option.value"
                ng-options="key as value for (key, value) in option.choices"
                required=required
                ></select>

            <input ng-switch-default
                type="text"
                ng-model="option.value"
                id="{{ ::option_name}}"
                class="form-control"
                name="{{ ::option_name}}"
                required=required
            />
        </div> <!-- ng-switch -->

        <span ng-if="::option.help"
                class="help-block"
                ng-bind="option.help"
                ></span>

        <span class="help-block"
                ng-show="options.errors[option_name]"
                ng-repeat="error in options.errors[option_name]"
                ng-bind="error"
                ></span>
    </div>
</div>
ghost commented 5 years ago

@VanTanev, thank you for the code sample. I understand the approach you are illustrating. Let me see if that works and I will get back with you here.

ghost commented 5 years ago

Works like a charm, thanks @VanTanev !