0

I am working on a little project that supports search filters. I have a default select dropdown from which a user can select e.g. Product Name, Status and Date. These options determine which type of form to display next or replace.

When the user selects Product Name another dropdown show with options like Equals and Not Equals as well as an input field.

If the user selects Date, another select dropdown shows with options like Less Than, Greater Than and Between. For the Greater Than and Less Than options, a datepicker shows. If the user selects Between then two datepickers shows, one for start date and the other for end date.

So far I have solved this problem by bloating my view with logic like e.g. ng-if and ||.

I know there has to be another way, the Angular way.

Here is the code that I currently have. I have a productsFormFilterBuilderCtrl controller that dynamically builds forms.

  app.controller('productsFormFilterBuilderCtrl', function($scope){
// Default form
$scope.default_form = {
  options:  [ 
    { id: "name",           name: "Product Name" },
    { id: "description",    name: "Product Description" },
    { id: "status",         name: "Status" },
    { id: "end_date",       name: "End Date" }
  ],
  selected_option: {id: "name", name: "Product Name"}//Sets the default value of the select in the ui
};

//Add form when product Name is selected
$scope.product_name = {
  operators:  [ 
    { id: "equals",       name: "Equals" },
    { id: "not_equals",   name: "Does Not Equal" },
    { id: "contains",     name: "Contains" },
    { id: "not_contains", name: "Does Not Contain" }
  ],
  selected_option: {id: "equals", name: "Equals"}//Sets the default value of the select in the ui
};

//Add for when product description is selected 
$scope.product_description = {
  operators:  [ 
    { id: "equals",       name: "Equals" },
    { id: "not_equals",   name: "Does Not Equal" },
    { id: "contains",     name: "Contains" },
    { id: "not_contains", name: "Does Not Contain" }
  ],
  selected_option: {id: "equals", name: "Equals"}//Sets the default value of the select in the ui
};

//Add form when product Status is selected
$scope.status_options = {
  operators:  [ 
    { id: "equals",       name: "Equals" },
    { id: "not_equals",   name: "Does Not Equal" },
    { id: "contains",     name: "Contains" },
    { id: "not_contains", name: "Does Not Contain" }
  ],
  selected_option: {id: "equals", name: "Equals"}//Sets the default value of the select in the ui 
};

//Add when End Date is selected 
$scope.end_date_options = {
  operators: [
  { id: "equals",             name: "Equal" }, //Start Date
  { id: "not_equals",         name: "Does Not Equal"},
  { id: "greater_than",       name: "Is Greater Than"}, 
  { id: "greater_or_equal",   name: "Is Greater Than Or Equal To"},
  { id: "less_than",          name: "Is Less Than"}, //End Date
  { id: "less_or_equal",      name: "Is Less Than Or Equal To"},
  { id: "between",            name: "Is Between"} //Start + End Date
  ],
  selected_option: { id: "equals", name: "Equal"}//Sets the default value of the select in the ui
};

});

Next I have a HTML template where the select dropdowns are built using ng-options. This view is very bloated with logic to display a particular form. e.g. a single datepicker vs two day pickers.

    <form id="new-search-filter-{{searchFilterForm.counter}}" ng-controller="productsFormFilterBuilderCtrl">
   <div class="row">
      <!-- Filter products on Product Name, Product Description etc  -->
      <div class="col-md-4">
         <div class="form-group">
            <select class="form-control" name="filter_field" id="filter_field"
               ng-options="option.name for option in default_form.options track by option.id"
               ng-model="default_form.selected_option">
            </select>
         </div>
      </div>
      <!-- Display only for Product Name, Description and Status-->
      <div ng-if="default_form.selected_option.name == 'Product Name' || 
         default_form.selected_option.name == 'Product Description' ||
         default_form.selected_option.name == 'Status'" >
         <!-- Filter on Product Name -->
         <div class="col-md-3" ng-if="default_form.selected_option.name == 'Product Name' ">
            <div class="form-group">
               <select class="form-control" name="product_name" id="product_name"
                  ng-options="option.name for option in product_name.operators track by option.id"
                  ng-model="product_name.selected_option">
               </select>
            </div>
         </div>
         <!-- Filter on Product Description -->
         <div class="col-md-3" ng-if="default_form.selected_option.name == 'Product Description' ">
            <div class="form-group">
               <select class="form-control" name="product_description " id="product_description "
                  ng-options="option.name for option in product_description.operators track by option.id"
                  ng-model="product_description.selected_option">
               </select>
            </div>
         </div>
         <!-- Filter on Product Status-->
         <div class="col-md-3" ng-if="default_form.selected_option.name == 'Status'">
            <div class="form-group">
               <select class="form-control" name="status_operators" id="status_operators"
                  ng-options="option.name for option in status_options.operators track by option.id"
                  ng-model="status_options.selected_option">
               </select>
            </div>
         </div>
         <div class="col-md-4">
            <div class="form-group" id="filter_input1">
               <input type="text" class="form-control" id="input_value" placeholder="Enter Value" name="input" ng-model="input.text">
            </div>
         </div>
         <!-- delete button-->
         <div class="col-md-1 text-center">
            <div class="form-group">
               <a ng-click="deleteSearchFilter($event)"><i class="fa fa-times fa-2x"></i></a>
            </div>
         </div>
      </div>
      <!-- Filters when End Date is selected. Attach datePicker controller here -->
      <div ng-if="default_form.selected_option.name == 'End Date'" ng-controller="datePickerCtrl">
         <div class="col-md-3">
            <div class="form-group">
               <select class="form-control" name="date_operators" id="date_operators"
                  ng-options="option.name for option in end_date_options.operators track by option.id"
                  ng-model="end_date_options.selected_option"></select>
            </div>
         </div>
         <!-- Filter when End Date is selected and Equal, Does Not Equal,
            Is Greater Than/or Equal To is selected-->
         <div ng-if="end_date_options.selected_option.name == 'Equal' || 
            end_date_options.selected_option.name == 'Does Not Equal' || 
            end_date_options.selected_option.name == 'Is Greater Than' ||
            end_date_options.selected_option.name == 'Is Greater Than Or Equal To' ">
            <div class="col-md-4">
               <div class="input-group">
                  <input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="status.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close"/>
                  <span class="input-group-btn">
                  <button type="button" class="btn btn-default" ng-click="open($event)"><i class="fa fa-calendar"></i></button>
                  </span>
               </div>
            </div>
         </div>
         <!-- Display end date datepicker for 'Is Less Than' or 'Is Less Than or Equal To'-->
         <div ng-if="end_date_options.selected_option.name == 'Is Less Than' 
            || end_date_options.selected_option.name == 'Is Less Than Or Equal To'">
            <div class="col-md-4">
               <div class="input-group">
                  <input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="status.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
                  <span class="input-group-btn">
                  <button type="button" class="btn btn-default" ng-click="open($event)"><i class="fa fa-calendar"></i></button>
                  </span>
               </div>
            </div>
         </div>
         <!-- Displays two day pickers if 'End Date' && 'Is Between' is selected -->
         <div ng-if="end_date_options.selected_option.name == 'Is Between'" ng-controller="datePickerCtrl">
            <div class="col-md-2">
               <div class="input-group">
                  <input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="status.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
                  <span class="input-group-btn">
                  <button type="button" class="btn btn-default" ng-click="open($event)"><i class="fa fa-calendar"></i></button>
                  </span>
               </div>
            </div>
            <div class="col-md-2">
               <div class="input-group">
                  <input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="status.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
                  <span class="input-group-btn">
                  <button type="button" class="btn btn-default" ng-click="open($event)"><i class="fa fa-calendar"></i></button>
                  </span>
               </div>
            </div>
         </div>
         <!-- delete button-->
         <div ng-if="end_date_options.selected_option.name == 'Equal' || 
            end_date_options.selected_option.name == 'Does Not Equal' || 
            end_date_options.selected_option.name == 'Is Greater Than' ||
            end_date_options.selected_option.name == 'Is Greater Than Or Equal To' || 
            end_date_options.selected_option.name == 'Is Less Than' 
            || end_date_options.selected_option.name == 'Is Less Than Or Equal To'">
            <div class="col-md-1 text-center">
               <div class="form-group">
                  <a ng-click="deleteSearchFilter($event)"><i class="fa fa-times fa-2x"></i></a>
               </div>
            </div>
         </div>

         <div ng-if="end_date_options.selected_option.name == 'Is Between'">
            <div class="col-md-1 text-center">
               <div class="form-group">
                  <a ng-click="deleteSearchFilter($event)"><i class="fa fa-times fa-2x"></i></a>
               </div>
            </div>
         </div>

      </div>
   </div>
</form>

This also supports adding/removing a form element or removing all added form elements as shown by the attached image. Any tips/suggestions will be greatly appreciated. my UI view

Elvyn Mejia
  • 307
  • 4
  • 18
  • It's not really easy to follow your HTML and identify which items should be shown when, but I would suggest starting off with creating boolean variables which represent the shown state for the various elements, and use `ng-change` to set the shown state of the various controls, rather than trying to do comparisons in the HTML. – Claies Jan 08 '16 at 23:43
  • @Claies thanks for the suggestion. I agree my HTML is very hard to read and understand. That's why I am looking for an efficient solution. If by any change you find an example please let me know. – Elvyn Mejia Jan 08 '16 at 23:52
  • Big forms are always a challenge, but I think you're headed in the right direction. Personally I'd truncate your var/property names a bit, and then get comfortable using those as conditional states in your html. ng-switch is useful for simple comparisons and if you're really averse to how big the HTML looks then split it out into partials and use ng-include. My biggest recommendation (after doing it myself and regretting it later) is to favor HTML structure in lieu of letting long elements wrap. Get them all on one line if at all possible since it really makes the HTML hard to read. – Scott Byers Jan 11 '16 at 14:56
  • @ScottByers thanks for your recommendations. I like the idea of using partials and `ng-include`. – Elvyn Mejia Jan 11 '16 at 18:28

0 Answers0