md-select not binding to the object referenced by ng-model - angularjs

I have a form with some md-select dropdowns which im trying to bind with a scope object in my angular controller.
The html looks like that :
<div id="horizontal_form_category" class="row">
<div class="col-md-2 col-lg-2" data-ng-repeat="r in categories[general]" dir="rtl">
<md-input-container>
<label> {{ r.name }} </label>
<md-select ng-model="formObject[r.name]" class="md-no-underline">
<md-option ng-repeat="option in r.values" ng-value="option "> {{ option }} </md-option>
</md-select>
</md-input-container>
</div>
</div>
The controller has a definition of the object $scope.formObject = {}; (Although it should work without it)
But, unfortunately, the $scope.formObject object in my controller stays empty.
Any ideas what could cause such weird behavior ? I have some normal bootstrap components whom ng-model is written the same and are working just fine.
Thanks

Does this help in any way? - CodePen
The true as the last parameter for $watch checks for changes in the object formObject.
Markup
<div ng-controller="AppCtrl" ng-cloak="" ng-app="MyApp">
<md-input-container>
<label> Options </label>
<md-select ng-model="formObject['Options']" class="md-no-underline">
<md-option ng-repeat="option in options" ng-value="option "> {{ option }} </md-option>
</md-select>
</md-input-container>
</div>
JS
angular.module('MyApp',['ngMaterial'])
.controller('AppCtrl', function($scope) {
$scope.options = ["Earth", "Saturn", "Jupiter"];
$scope.$watch("formObject", function () {
console.log($scope.formObject);
}, true)
});

Apparently there wasnt any bug.
My form's md-select were empty and therefore the formObject was empty.
The minute I started puting values, the formObject got filled, one field at a time, containing only the fields that were set.
I added a default value and everything is now showing correctly, just to be sure this was the "bug".
In retrospect, this bevahiour actually saves me some lines of code so I adopted it :)
Gotta thank #camden_kid answer for the $watch tip, helped me figure it out in matter of seconds.

Related

How to set value from md-options on controller?

I have a simple md-select component. I want to set the value of number.number on the vm.telephonyInfo.phone.number property in the controller.
<md-input-container>
<md-select ng-model="vm.telephonyInfo.phone.number" placeholder="Selecteer een nummer" required>
<md-option ng-repeat="number in vm.phonenumbers"
ng-value="{{number.number}}" ng-click="vm.login()">{{number.number}}</md-option>
</md-select>
</md-input-container>
When I select a option I log the login() function but the property this.telephonyInfo.phone.number always is null.
Is my data-binding wrong? I thought I had to put the ng-value in the md-option and the ng-model in the md-select.
Use the ng-change directive instead of ng-click:
<md-input-container>
<md-select ng-model="vm.telephonyInfo.phone.number"
ng-change="vm.login()"
placeholder="Selecteer een nummer" required>
<md-option ng-repeat="number in vm.phonenumbers"
̶n̶g̶-̶v̶a̶l̶u̶e̶=̶"̶{̶{̶n̶u̶m̶b̶e̶r̶.̶n̶u̶m̶b̶e̶r̶}̶}̶"̶
ng-value="number.number"
̶n̶g̶-̶c̶l̶i̶c̶k̶=̶"̶v̶m̶.̶l̶o̶g̶i̶n̶(̶)̶"̶ >{{number.number}}</md-option>
</md-select>
</md-input-container>
Also avoid using double curly {{ }} interpolation in ng-value directives. See AngularJS Developer Guide - Why mixing interpolation and expressions is bad practice.

how to use ng-switch with ng-repeate in select

I am trying to show and hide one of two select options based on an expression
but it doesn't work well. How can I make it work? Thanks in advance.
<div class="col-md-3">
<div class="form-group has-error " ng-switch on="class">
<label class="title_lable">{{class}}</label>
<select class="form-control input-sm" ng-model="Tclass">
<option ng-switch-when="7" ng-repeat="sd in ScientificDegree" value="{{sd.sub_cod}}">{{sd.Degree}}</option>
<option ng-switch-when="8" ng-repeat="sp in Specialty" value="{{sp.Spec_Key}}">{{sp.Spec}}</option>
</select>
</div>
</div>
Would be better if you include the controller's content in your question, but I tried to complete an example based on some assumptions. Your ng-switch looks good, so I think something is wrong in the controller. This is what I put in the controller to get it working:
app.controller('MainCtrl', function($scope) {
$scope.ScientificDegree = [{sub_cod:1,Degree:'Degree 1'}];
$scope.Specialty = [{Spec_Key: 2, Spec: 'Specialty 2'}];
$scope.TClass = '';
$scope.class = 8;
});
Here's a plunker to see it working https://plnkr.co/edit/TjE2MvrHLfdI1umajtBx?p=preview

Angular Material mandatory form inputs pop up alert

I'm creating a Material form and have a couple required fields that are rendering correctly after following the examples found on here: https://material.angularjs.org/latest/demo/input
However, in their example, when required inputs aren't filled out and a user presses Submit, a popup alert shows up and I'm not sure how this is done:
My code looks like this:
<div ng-switch-when="choice">
<md-input-container class="md-block">
<label style="font-size: 130%; white-space: normal;" for="{{element.section_element_name}}">{{element.section_element_label}}</label>
<md-select ng-if="element.mandatoryFlag==1" required id = {{element.section_element_name}} type="selectbasic" value={{element.q_value}} ng-model="element.answer">
<md-option ng-repeat="option in element.section_element_choices" value="{{option.element_choice_value}}" >
{{option.element_choice_label}}
</md-option>
</md-select>
<div ng-messages="element.answer.$error" role="alert">
<div ng-message="required" class="my-message">Please provide an answer.</div>
</div>
<md-select ng-if="element.mandatoryFlag==0" id = {{element.section_element_name}} type="selectbasic" value={{element.q_value}} ng-model="element.answer">
<md-option ng-repeat="option in element.section_element_choices" value="{{option.element_choice_value}}" >
{{option.element_choice_label}}
</md-option>
</md-select>
</md-input-container>
</div>
<md-button ng-click="submit()" class="md-fab md-mini">
<md-tooltip md-direction="top">Save or Submit Form</md-tooltip>
<i class="material-icons" style="vertical-align:middle;">send</i>
</md-button>
What do I have to do for that pop up to show up if things aren't filled out onSubmit?
There is a lot of discussion related to this in Chrome popup Please Fill Out this Field. However this isn't a duplicate as you are asking a bit of the opposite question.
The answer to this is that the HTML5 required attribute behavior for <input> elements is different than the required behavior for <select> elements.
This CodePen demonstrates the popup working for a <select> element, but not working for the <md-select> element.
One issue is that the Material Select doesn't have a blank entry for the first option. However, even adding that doesn't make the popup appear.
The second issue is that the required is not applied to the hidden <select> element in the DOM. So the browser can't add the popup.
<select class="md-visually-hidden" name="type" aria-hidden="true" tabindex="-1">
<option value="app">Application</option>
<option value="web">Website</option>
<option ng-value="project.type" selected="" aria-checked="false"></option>
</select>
After all of this, you may just want to handle validation messages in the Material Design style and disable the popup completely using the steps included in the first link in this answer.

md-select show already selected values

So I have the following:
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Documents</label>
<md-select multiple ng-model="ctrl.data.documents" placeholder="Documents" ng-controller="DocumentsCtrl"> // I want to show the submitted values.
<md-option ng-value="doc" ng-repeat="doc in allItems">{{ doc.name }}</md-option> // A list of other values
<div ng-hide="allItems.length">No items found</div>
</md-select>
</md-input-container>
</div>
ctrl.data.documents is an array of already selected values.
What I am trying to achieve is to present this array (ctrl.data.documents) in the field options and a list with the other values which is the ng-repeat in this case. The ng-repeat works just fine. I am able to select other values. But can't figure out how to show the already selected values.
Did some digging I thought ng-options might do it, but can't seem to get it working (not supported ??) and ng-selected did not do the trick as well.
Any help or ideas?
Update #1:
Added a picture of the ctr.data.documents array.
You have to use ng-model-options="{trackBy: '$value.id'}" and ng-repeat="doc in allItems track by doc.id". The id part should be your identifying marker of your document.
<md-select multiple ng-model="ctrl.data.documents"
ng-model-options="{trackBy: '$value.id'}"
placeholder="Documents" ng-controller="DocumentsCtrl">
<md-option ng-value="doc" ng-repeat="doc in allItems track by doc.id">{{ doc.name }}</md-option>
<div ng-hide="allItems.length">No items found</div>
</md-select>
Most of this is documented here: https://material.angularjs.org/latest/api/directive/mdSelect

Angular.JS: why can't the inputs be edited?

This is a strange problem. The code is simple:
HTML code:
<body ng-controller="MainCtrl">
<ul ng-repeat="name in names">
<input type="text" ng-model="name" />
</ul>
</body>
Angular code:
app.controller('MainCtrl', function($scope) {
$scope.names = ["aaa","bbb","ccc"];
});
The live demo url is: http://plnkr.co/edit/2QFgRooeFUTgJOo223k9?p=preview
I do not understand why the input controls can not be edited, I can't type new characters or delete characters.
This is a common issue due to scope inheritance . Each of your names is a primitive so ng-repeat makes it's own scope item that is not connected to original, however if each names is an object ng-repeat scope item will be a reference to original object
[{name:"aaa"},{name:"bbb"},{name:"ccc"}];
Always use a dot in ng-model is a helpful rule of thumb
<div ng-repeat="item in names">
<input type="text" ng-model="item.name"/>
</div>
Working Plunker
Read this article on angular github wiki for detailed explanaton:
https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
Angular 'fixed' this in 1.1 with the track by $index. No need to change your model.
<div ng-repeat="item in names track by $index">
<input type="text" ng-model="names[$index]" />
</div>
Plunker here
Late answer, but you should also be careful of typos, that angular will not warn you about:
<div ng-repeat="item in names track by $index" ng=if="names[$index] = 'John'">
<input type="text" ng-model="names[$index]" />
</div>
Note the single equals in the ng-if, that will not cause a warning or error, but the text will also be read only. Quite a hard one to spot if you are reading quickly.
It of course should be:
<div ng-repeat="item in names track by $index" ng-if="names[$index] == 'John'">
<input type="text" ng-model="names[$index]" />
</div>

Resources