8

I am trying to do a search engine interface with angular. The user selects some parameters in a form, click "search" and then it fills the url with the parameters using $location.search()

the search interface parameters which are used to build the form :

params = {
    milestones: [ "a", "b", "c", "d", etc. ], 
    properties: [ 
        { "name": "name A", type: "text" }, 
        { "name": "name B", type: "checkbox" }, 
        { etc. }
    ]
}

inside the controller :

$scope.query = $location.search();  // get the parameters from the url  
$scope.search = function (query) {  // set the parameters to the url
    $location.search(query);    
};

and the html of the form

<select ng-model="query.milestone_name" ng-options="ms for ms in params.milestones">
    <option value="">-select milestone-</option>
</select>
<select ng-model="property" ng-options="prop.name for prop in params.properties" ng-change="query.property_name=property.name">
<!-- if the object 'property' was passed in the url, it would look like this `%5Bobject%20Object%5D`, so its 'name' parameter is converted to a string -->
    <option value="">-select property-</option>
</select>
<span ng-switch="property.type">
    <label ng-switch-when="text">{{query.property_name}}: <input type="text" ng-model="query.property_value"></label>
    <label ng-switch-when="checkbox">{{query.property_name}}: <input type="checkbox" ng-model="query.property_value"></label>
</span>
<button ng-click="search(query)">search</button>

and somewhere else in the page is the list of results.

the user can also access to a search result page with an url like this:

http://myapp.com/search?milestone_name=a&property_name=name%20A

almost everything works fine : the list of results is displayed, the "milestone" parameter is pre-selected with the correct value in the select component, but not the "property" parameter because it's not a string, it's an object.

how can i set the default value (ng-model) of the select component to an object ?

or any other idea on how i should do this ?

François Romain
  • 13,617
  • 17
  • 89
  • 123

4 Answers4

27

When using an array of objects for the select to iterate over, the ng-options directive needs to have an attribute of the object to match against (and differentiate between arrays)

Use the track by section of the directive declaration eg

<select ng-model="property" ng-options="prop.name for prop in params.properties track by prop.name" ng-change="query.property_name=property.name">
<!-- if the object 'property' was passed in the url, it would look like this `%5Bobject%20Object%5D`, so its 'name' parameter is converted to a string -->
    <option value="">-select property-</option>
</select>
Zak Henry
  • 2,075
  • 2
  • 25
  • 36
  • Thank you!!! This is so exactly what I needed to do. – Ray Suelzer Jan 29 '14 at 01:12
  • 1
    worked for me too. `track by` did the job. If you want to filter the list it must be used at the end of the expression as this [answer](http://stackoverflow.com/a/21416725/2071612) suggests – Lekhnath Jan 16 '15 at 10:13
  • i've tried "track by" but the option values still look like this "object:875" and my ng-selected is not working – Sonic Soul Jul 21 '17 at 14:58
3

You can use this form of comprehension expression in ngOptions: label group by group for value in array. Html drop down list will display only name of selected object. Model will contain whole selected object. You can set selected object from controller.

  <select ng-model="property"
          ng-options="prop as prop.name for prop in params.properties">
  </select>

Check this plnkr example.

Vasiliy Kevroletin
  • 1,188
  • 13
  • 17
0

ng-options is generating some options to be used with ng-model. In your syntax (prop.name for prop in params.properties) you've told it to bind to the object found in the array (as oppose to a property on it - which is what you want to do) and use its name property as the value to display. So when you try and set the ng-model to be an object that's not in the ng-options array nothing happens - I'm guessing because it's using reference/shallow equality and not deep equality. So what you should do is either:

  • Convert the ng-options object to be an array of strings.

  • Use a syntax that involves keys, such as:

prop.name as prop.name for prop in params.properties

http://jsfiddle.net/C5ENK/

If that doesn't suit your needs let me know why and I'll see if I can help further.

Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
  • thanks for your answer. the problem is that i need the full object to be bind to `ng-model`, for exemple to retrieve its "type" property used in the following `ng-switch`. with your solutions i think it's not possible? – François Romain Sep 02 '13 at 08:28
  • If that's your only problem you make your properties a lookup instead of an array you could do You could just do ``. Let me know if that doesn't make sense or, if it does, if that doesn't solve your problem. – Jesus is Lord Sep 03 '13 at 02:56
0

i found a kind of solution…

when selecting a property, it saves the index of this object and then when the page loads, it sets the ng-model of the select to the value of this index. it uses this : example for setting the index and this example for getting the value of this index in the array of objects.

Community
  • 1
  • 1
François Romain
  • 13,617
  • 17
  • 89
  • 123