I am doing a custom autocomplete with Angular Material and I would like to set the height of the autocomplete dropdown results box to something bigger.
To achieve this, I made a research and concluded that Angular Material does not support this, and that this issue will only be addressed in Angular Material 2 (to be released for AngularJS2):
After reading, I realized that some people found a way around this limitation forcing the CSS (Angular Material Design Layout custom sizes), but not matter what I try I can't see to make any of their suggestions work.
My sample app is composed of an index.html, an autocomplete.js file, and the server.js file with a mockData.json file.
Following are the index.html, autocomplete.js and style.css files. Since I am hosting this example in Cloud9 if the server is ON you can see it run live !
/*global angular*/
"use strict";
angular.module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache', 'ngMdIcons']).controller('DemoCtrl', DemoCtrl);
function DemoCtrl($q, $log, $http) {
this.searchText = null;
this.querySearch = function(query) {
let serverUrl = '//custom-material-autocomplete-fl4m3ph03n1x.c9users.io/getClients';
let deferred = $q.defer();
$http({
method: 'GET',
url: serverUrl,
params: {
word: query
}
}).then(function successCallback(response) {
deferred.resolve(response.data);
}, function errorCallback(response) {
$log.error(response);
});
return deferred.promise;
};
this.searchTextChange = function(text) {
$log.info('Text changed to ' + text);
}
this.selectedItemChange = function(item) {
$log.info('Item changed to ' + JSON.stringify(item));
}
}
.autocompletedemoCustomTemplate .autocomplete-custom-template li {
border-bottom: 1px solid #ccc;
height: auto;
padding-top: 8px;
padding-bottom: 8px;
white-space: normal;
}
.autocompletedemoCustomTemplate .autocomplete-custom-template li:last-child {
border-bottom-width: 0;
}
.autocompletedemoCustomTemplate .autocomplete-custom-template .item-title,
.autocompletedemoCustomTemplate .autocomplete-custom-template .item-metadata {
display: block;
line-height: 2;
}
.autocompletedemoCustomTemplate .autocomplete-custom-template .item-title md-icon {
height: 18px;
width: 18px;
}
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body ng-app="MyApp" ng-cloak>
<div ng-controller="DemoCtrl as ctrl" layout="column" ng-cloak="" class="autocompletedemoCustomTemplate" ng-app="MyApp">
<md-content layout-padding="" layout="column">
<form ng-submit="$event.preventDefault()">
<md-autocomplete md-selected-item="ctrl.selectedItem" md-search-text-change="ctrl.searchTextChange(ctrl.searchText)" md-search-text="ctrl.searchText" md-selected-item-change="ctrl.selectedItemChange(item)" md-items="item in ctrl.querySearch(ctrl.searchText)"
md-item-text="item.name" md-min-length="0" placeholder="Pick an Angular repository" md-menu-class="autocomplete-custom-template">
<md-item-template>
<span class="item-title">
<strong>Company name:</strong> {{item.Company_Name}}
</span>
<strong>Client Ids:</strong>
<span ng-repeat="clientId in item.Assets">
<span class="item-metadata">
<span class="item-metastat">
 <ng-md-icon icon="subdirectory_arrow_right" style="fill: gray" size="24"></ng-md-icon>
{{clientId}}
</span>
</span>
</span>
</md-item-template>
</md-autocomplete>
</form>
</md-content>
</div>
<!--CSS files-->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0-rc4/angular-material.min.css">
<link rel="stylesheet" href="https://material.angularjs.org/1.1.0-rc4/docs.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="/css/style.css">
<!-- Angular Material requires Angular.js Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-messages.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/svg-assets-cache.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0-rc4/angular-material.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-material-icons/0.7.0/angular-material-icons.min.js"></script>
<!-- Your application bootstrap -->
<script type="text/javascript" src="js/autocomplete.js"></script>
</body>
</html>
Here are the server.js file and the mockData.json file.
"use strict";
//Lets define a port we want to listen to
const PORT = 8080;
//Init Vars
var express = require('express');
var fs = require('fs');
var app = express();
//Init Functions
//we allow CORS: http://enable-cors.org/server_expressjs.html
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.listen(PORT, function() {
console.log('Example app listening on port ' + PORT + '!');
});
//GET methods
app.get('/getClients', function(req, res, next) {
let assetsQuery = function(array, word) {
let result = false;
array.forEach(function(element, index, array) {
if (element.toLowerCase().trim().includes(word))
result = true;
});
return result;
};
//get parameters from GET: https://scotch.io/tutorials/use-expressjs-to-get-url-and-post-parameters
var query = req.param('word');
let dbQuery = JSON.parse(fs.readFileSync('mockData.json', 'utf8'));
let result = [];
if (query == null || query == "" || typeof query == 'undefined')
result = dbQuery.mockupData;
else {
query = query.toLowerCase().trim();
dbQuery.mockupData.forEach(function(element, index, array) {
if (element.Company_Name.toLowerCase().trim().includes(query) || assetsQuery(element.Assets, query))
result.push(element);
});
}
res.send(result);
});
Fake Data file:
{
"mockupData": [{
"AccountID": "cweu4xy733z06mqv96",
"Company_Name": "Wal - Mart Stores, Inc",
"Assets": ["gme-wal1", "gme-wal2"]
}, {
"AccountID": "3tvjnjzud1jz2xy6ug",
"Company_Name": "Volkswagen Automotive",
"Assets": ["gme-vol1", "gme-aut2"]
}, {
"AccountID": "ht019dupmtb4jinzpo",
"Company_Name": "Vitol Commodities",
"Assets": ["gme-vit1", "gme-com2"]
}, {
"AccountID": "l2fd73rbpc48d1hvua",
"Company_Name": "Verizon Telecommunications",
"Assets": ["gme-ver1", "gme-tel2"]
}, {
"AccountID": "2iygtyj2do2pi8e4dv",
"Company_Name": "Valero",
"Assets": ["gme-vale1", "gme-valoil2"]
}, {
"AccountID": "1ojav89f9qka85vpwb",
"Company_Name": "United Health Health care",
"Assets": ["gme-usahc1", "gme-uhhc2"]
}, {
"AccountID": "y9ikfaj2qgf18d0vsw",
"Company_Name": "Trafigura Commodities",
"Assets": ["gme-traf1", "gme-trafcom2"]
}, {
"AccountID": "nxhpt5unxsjedui5sk",
"Company_Name": "Toyota Automotive",
"Assets": ["gme-toy1", "gme-oyota2"]
}, {
"AccountID": "hqu18f8wy43oc5kfde",
"Company_Name": "Total",
"Assets": ["gme-total1", "gme-tot2"]
}, {
"AccountID": "tsc9aures3yjpy2nrr",
"Company_Name": "Tata Group Conglomerate",
"Assets": ["gme-tata1", "gme-grcon2"]
}, {
"AccountID": "paoxb086omzi1uu5zr",
"Company_Name": "State Grid Electric utility",
"Assets": ["gme-sgeu1", "gme-elecs2"]
}, {
"AccountID": "7u2fhcatofgqjzv2tf",
"Company_Name": "Sinopec Group",
"Assets": ["gme-sin1", "gme-sinoil2"]
}, {
"AccountID": "hbg285h3nk206zmdqb",
"Company_Name": "Saudi Aramco",
"Assets": ["gme-sau1", "gme-aram2"]
}, {
"AccountID": "xyg1n25grvl74f69l6",
"Company_Name": "Samsung Conglomerate",
"Assets": ["gme-sam1", "gme-sacomg2"]
}, {
"AccountID": "aaoexfcqln8peec4dv",
"Company_Name": "Royal Dutch Shell",
"Assets": ["gme-royal1", "gme-forthequeen"]
}, {
"AccountID": "rubvrmy2ucrvh3elrj",
"Company_Name": "Phillips 66",
"Assets": ["gme-phil1", "gme-ips2"]
}, {
"AccountID": "5gmxscwazuokverzbd",
"Company_Name": "Petrobras",
"Assets": ["gme-pedro1", "gme-petro2"]
}, {
"AccountID": "nsx4y558obp62dwn47",
"Company_Name": "PDVSA",
"Assets": ["gme-pdvsa1", "gme-pgas"]
}, {
"AccountID": "80o7d5p4wrx1ueygmx",
"Company_Name": "Microsoft Conglomerate",
"Assets": ["gme-microsoft1", "gme-evil2"]
}, {
"AccountID": "1v14j9i5w8sy4iipuv",
"Company_Name": "McKesson Pharmaceuticals",
"Assets": ["gme-mckesson1", "gme-mckeuticals2"]
}, {
"AccountID": "92ac8fl1dk1nh5v408",
"Company_Name": "Lukoil",
"Assets": ["gme-luko1", "gme-loil2"]
}, {
"AccountID": "pn3w8dxqrzytlmanhe",
"Company_Name": "Kuwait Petroleum Corporation",
"Assets": ["gme-kuwait1", "gme-war2"]
}, {
"AccountID": "35mtlyc6bnbxhuav1d",
"Company_Name": "Koch Industries Conglomerate",
"Assets": ["gme-koch1", "gme-kindus2"]
}, {
"AccountID": "n4gu863njqnndvmk3d",
"Company_Name": "Japan Post Conglomerate",
"Assets": ["gme-jap1", "gme-konichiwa2"]
}, {
"AccountID": "gkrllbxd56r9gi4q7t",
"Company_Name": "Industrial and Commercial Bank of China Financial services",
"Assets": ["gme-china1", "gme-LongLiveTsungLee2"]
}, {
"AccountID": "qgb1gudy460seqgzo3",
"Company_Name": "Honda Automotive",
"Assets": ["gme-honhon1", "gme-autohon2"]
}]
}
How can I make my dropdown box for the results bigger?
You could use the non-documented parameter md-dropdown-items:
<md-autocomplete
md-selected-item="selectedItem"
md-search-text="searchText"
md-items="item in getMatches(searchText)"
md-item-text="item.display"
md-dropdown-items="10">
<span md-highlight-text="searchText">{{item.display}}</span>
</md-autocomplete>
The default value is 5 (as of version angular-material#ad0581ddd3).
You might also want to adjust this value according to height of the window:
<md-autocomplete
md-selected-item="selectedItem"
md-search-text="searchText"
md-items="item in getMatches(searchText)"
md-item-text="item.display"
md-dropdown-items="nbItems">
<span md-highlight-text="searchText">{{item.display}}</span>
</md-autocomplete>
and then set nbItems in your controller. For example:
$scope.nbItems = 5 + $mdMedia('(min-height: 400px)') * 5 + $mdMedia('(min-height: 600px)') * 10;
Thus you will have 5 items if height is lower than 400px, 10 items if less than 600px, and 20 items shown above 600px.
Hope it helps
Note: the md-dropdown-items parameter does not exist for md-select, only for md-autocomplete.
Too bad ;)
After much experimentation and reading, I finally found a workaround on how to do it.
My solution is not really a solution to the problem, but more of a hack. The truth is that there is no official support for this, and there will never be, at least in Angular Material 1, which has now been deprecated in order to make Angular Material 2, and has lost official support.
Thus my workaround focuses on one suggestion found in github, and it effectively overrides the default CSS:
.md-virtual-repeat-container.md-autocomplete-suggestions-container {
height: 24vh;
min-height: 12vh;
max-height: 24vh !important;
}
This code overrides the default CSS by using !important and setting custom heights. In this case I am using the vh measurement, but you can use the px or any other.
This workaround will work fine if you only have one autocomplete with a dropdownbox, or if all the autocomplete with dropdownboxes you have are equal.
If however, you have two autocompletes different from each other, you will start fighting with your own code because of the override (which will affect all autocompletes).
This is, in the end, not a perfect solution, but as long as you don't go too much into autocomplete, it should be fine.
I hope this can help someone in the future!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With