Skip to content

Commit

Permalink
Fix select pristine/dirty error and many styles.
Browse files Browse the repository at this point in the history
_NOT READY FOR MERGE - Need to share with others to see improved
UI functionality/styles and get sign-off._

Fix styles and code to follow pristine/dirty styling of other
input elements and provide CSS class for stand-alone usage.

 - Select now behaves like a normal input, appearing as invalid
   if the user focuses/blurs the element, or submits the form,
   without selecting an option.
 - Fix issues with floating labels not working on focus.
 - Add new `md-no-underline` CSS class to allow for stand-alone
   usage (non-form).
 - Update demos to show new stand-alone usage.

Fixes angular#8529. Fixes angular#7988.
  • Loading branch information
topherfangio committed Jun 5, 2016
1 parent 51f8a2a commit 2161b72
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 72 deletions.
28 changes: 21 additions & 7 deletions src/components/input/demoErrors/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@
</div>
</md-input-container>

<md-input-container class="md-block">
<label>Client Name</label>
<input required name="clientName" ng-model="project.clientName">
<div ng-messages="projectForm.clientName.$error">
<div ng-message="required">This is required.</div>
</div>
</md-input-container>
<div layout="row">
<md-input-container flex="50">
<label>Client Name</label>
<input required name="clientName" ng-model="project.clientName">
<div ng-messages="projectForm.clientName.$error">
<div ng-message="required">This is required.</div>
</div>
</md-input-container>

<md-input-container flex="50">
<label>Project Type</label>
<md-select name="type" ng-model="project.type" required>
<md-option value="app">Application</md-option>
<md-option value="web">Website</md-option>
</md-select>
</md-input-container>
</div>

<md-input-container class="md-block">
<label>Client Email</label>
Expand Down Expand Up @@ -59,6 +69,10 @@
</div>
</md-input-container>

<div>
<md-button type="submit">Submit</md-button>
</div>

<p style="font-size:.8em; width: 100%; text-align: center;">
Make sure to include <a href="https://docs.angularjs.org/api/ngMessages" target="_blank">ngMessages</a> module when using ng-message markup.
</p>
Expand Down
6 changes: 4 additions & 2 deletions src/components/input/input-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ md-input-container.md-THEME_NAME-theme {
color: '{{foreground-3}}';
}

label.md-required:after {
color: '{{warn-A700}}'
&.md-input-invalid {
label.md-required:after {
color: '{{warn-A700}}'
}
}

&:not(.md-input-focused):not(.md-input-invalid) label.md-required:after {
Expand Down
9 changes: 1 addition & 8 deletions src/components/input/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,14 +334,7 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout, $mdGesture)
}

var isErrorGetter = containerCtrl.isErrorGetter || function() {
return ngModelCtrl.$invalid && (ngModelCtrl.$touched || isParentFormSubmitted());
};

var isParentFormSubmitted = function () {
var parent = $mdUtil.getClosest(element, 'form');
var form = parent ? angular.element(parent).controller('form') : null;

return form ? form.$submitted : false;
return ngModelCtrl.$invalid && (ngModelCtrl.$touched || $mdUtil.isParentFormSubmitted(element));
};

scope.$watch(isErrorGetter, containerCtrl.setInvalid);
Expand Down
105 changes: 81 additions & 24 deletions src/components/select/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
@@ -1,26 +1,83 @@
<div ng-controller="AppCtrl as ctrl" class="md-padding" ng-cloak>
<div>
<h1 class="md-title">Enter an address</h1>
<div layout="row">

<md-input-container>
<label>Street Name</label>
<input>
</md-input-container>

<md-input-container>
<label>City</label>
<input>
</md-input-container>

<md-input-container>
<label>State</label>
<md-select ng-model="ctrl.userState">
<md-option ng-repeat="state in ctrl.states" value="{{state.abbrev}}" ng-disabled="$index === 1">
{{state.abbrev}}
</md-option>
<div ng-controller="AppCtrl as ctrl" class="md-padding" ng-cloak layout="column">

<p>
The <code>&lt;md-select&gt;</code> component can be used within a
<code>&lt;md-input-container&gt;</code> or as a stand alone component by using the
<code>md-no-underline</code> class.
</p>

<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline">Account Preferences</span>
<span class="md-subhead">Tell us a little about you.</span>
</md-card-title-text>
</md-card-title>

<md-card-content>
<div layout="row">
<md-input-container>
<label>Street Name</label>
<input>
</md-input-container>

<md-input-container>
<label>City</label>
<input>
</md-input-container>

<md-input-container>
<label>State</label>
<md-select ng-model="ctrl.userState">
<md-option ng-repeat="state in ctrl.states" value="{{state.abbrev}}" ng-disabled="$index === 1">
{{state.abbrev}}
</md-option>
</md-select>
</md-input-container>
</div>
</md-card-content>
</md-card>

<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline">Battle Preferences</span>
<span class="md-subhead">Choose wisely if you want to win.</span>
</md-card-title-text>
</md-card-title>

<md-card-content>
<div layout="row" layout-align="space-between center">
<span>What is your favorite weapon?</span>
<md-select ng-model="weapon" placeholder="Choose..." class="md-no-underline">
<md-option value="axe">Axe</md-option>
<md-option value="sword">Sword</md-option>
<md-option value="wand">Wand</md-option>
<md-option value="pen">Pen?</md-option>
</md-select>
</div>

<div layout="row" layout-align="space-between center">
<span>What armor do you wear?</span>
<md-select ng-model="armor" placeholder="Choose..." class="md-no-underline">
<md-option value="cloth">Cloth</md-option>
<md-option value="leather">Leather</md-option>
<md-option value="chain">Chainmail</md-option>
<md-option value="plate">Plate</md-option>
</md-select>
</md-input-container>
</div>
</div>
</div>

<div layout="row" layout-align="space-between center">
<span>How do you refresh your magic?</span>
<md-select ng-model="drink" placeholder="Choose..." class="md-no-underline">
<md-option value="water">Water</md-option>
<md-option value="juice">Juice</md-option>
<md-option value="milk">Milk</md-option>
<md-option value="wine">Wine</md-option>
<md-option value="mead">Mead</md-option>
</md-select>
</div>
</md-card-content>
</md-card>

</div>
49 changes: 34 additions & 15 deletions src/components/select/demoValidations/index.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
<div ng-controller="AppCtrl" layout="column" layout-align="center center" style="min-height: 300px;" ng-cloak>
<div ng-controller="AppCtrl" ng-cloak layout="column" layout-align="center center" layout-padding>
<form name="myForm">
<p>Note that invalid styling only applies if invalid and dirty</p>
<md-input-container class="md-block">
<label>Favorite Number</label>
<md-select name="myModel" ng-model="myModel" required>
<md-option value="1">One</md-option>
<md-option value="2">Two</md-option>
</md-select>
<div class="errors" ng-messages="myForm.myModel.$error" ng-if="myForm.$dirty">
<div ng-message="required">Required</div>
</div>
</md-input-container>
<div layout="row">
<md-button ng-click="clearValue()" ng-disabled="!myModel" style="margin-right: 20px;">Clear</md-button>
<md-button ng-click="save()" ng-disabled="myForm.$invalid" class="md-primary" layout layout-align="center end">Save</md-button>
<p>
Note that, similar to regular inputs, the invalid styling only applies if select is both
invalid <em>and</em> touched.
</p>

<div layout="row" layout-align="start" flex>
<md-input-container flex="50">
<label>Quest</label>
<input type="text" name="quest" ng-model="quest" />
</md-input-container>

<md-input-container flex="50">
<label>Favorite Number</label>
<md-select name="favoriteColor" ng-model="favoriteColor" required>
<md-option value="red">Red</md-option>
<md-option value="blue">Blue</md-option>
<md-option value="green">Green</md-option>
</md-select>
<div class="errors" ng-messages="myForm.favoriteColor.$error">
<div ng-message="required">Required</div>
</div>
</md-input-container>
</div>

<div layout="row" layout-align="start">
<md-checkbox ng-model="myForm.$invalid" disabled>Form Invalid</md-checkbox>
<md-checkbox ng-model="myForm.$dirty" disabled>Form Dirty</md-checkbox>
<md-checkbox ng-model="myForm.favoriteColor.$touched" disabled>Select Touched</md-checkbox>
</div>

<div layout="row" layout-align="end" flex>
<md-button ng-click="clearValue()" ng-disabled="!(quest || favoriteColor)" style="margin-right: 20px;">Clear</md-button>
<md-button ng-click="save()" class="md-primary" layout layout-align="center end">Save</md-button>
</div>
</form>
</div>
9 changes: 7 additions & 2 deletions src/components/select/demoValidations/script.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
angular.module('selectDemoValidation', ['ngMaterial', 'ngMessages'])
.controller('AppCtrl', function($scope) {
$scope.clearValue = function() {
$scope.myModel = undefined;
$scope.quest = undefined;
$scope.favoriteColor = undefined;
};
$scope.save = function() {
alert('Form was valid!');
if ($scope.myForm.$valid) {
alert('Form was valid.');
} else {
alert('Form was invalid!');
}
};
});
26 changes: 25 additions & 1 deletion src/components/select/select-theme.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
md-input-container {
&.md-input-focused {
&:not(.md-input-has-value) {
md-select.md-THEME_NAME-theme {
._md-select-value {
color: '{{primary-color}}';
&._md-select-placeholder {
color: '{{primary-color}}';
}
}
}
}
}
}

md-select.md-THEME_NAME-theme {
&[disabled] ._md-select-value {
border-bottom-color: transparent;
Expand All @@ -10,11 +25,17 @@ md-select.md-THEME_NAME-theme {
}
border-bottom-color: '{{foreground-4}}';
}
&.ng-invalid.ng-dirty {
&.md-no-underline ._md-select-value {
border-bottom-color: transparent !important;
}
&.ng-invalid.ng-touched {
._md-select-value {
color: '{{warn-A700}}' !important;
border-bottom-color: '{{warn-A700}}' !important;
}
&.md-no-underline ._md-select-value {
border-bottom-color: transparent !important;
}
}
&:not([disabled]):focus {
._md-select-value {
Expand All @@ -24,6 +45,9 @@ md-select.md-THEME_NAME-theme {
color: '{{ foreground-1 }}';
}
}
&.md-no-underline ._md-select-value {
border-bottom-color: transparent !important;
}
&.md-accent ._md-select-value {
border-bottom-color: '{{accent-color}}';
}
Expand Down
13 changes: 6 additions & 7 deletions src/components/select/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ angular.module('material.components.select', [
* @description Displays a select box, bound to an ng-model.
*
* When the select is required and uses a floating label, then the label will automatically contain
* an asterisk (`*`).<br/>
* This behavior can be disabled by using the `md-no-asterisk` attribute.
* an asterisk (`*`). This behavior can be disabled by using the `md-no-asterisk` attribute.
*
* By default, the select will display with an underline to match other form elements. This can be
* disabled by applying the `md-no-underline` CSS class.
*
* @param {expression} ng-model The model!
* @param {boolean=} multiple Whether it's multiple.
Expand Down Expand Up @@ -331,11 +333,8 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $compile, $par
if (!isReadonly) {
element
.on('focus', function(ev) {
// only set focus on if we don't currently have a selected value. This avoids the "bounce"
// on the label transition because the focus will immediately switch to the open menu.
if (containerCtrl && containerCtrl.element.hasClass('md-input-has-value')) {
containerCtrl.setFocused(true);
}
// Always focus the container so floating labels and other styles are applied properly
containerCtrl.setFocused(true);
});

// Attach before ngModel's blur listener to stop propagation of blur event
Expand Down
Loading

0 comments on commit 2161b72

Please sign in to comment.