angular material: md-select not working with "controller as" syntax? - angularjs

All I did was modify their official Async Search demo (it fits my use case) to make use of "controller as", and getting weird behaviour as a result. Codepen is here. Am I missing anything?
Here's the relevant bits of code from the above link:
JS:
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('SelectAsyncController', function($timeout, $scope) {
var vm = this;
vm.user = null;
vm.users = null;
vm.loadUsers = function() {
return $timeout(function() {
vm.users = vm.users || [
{ id: 1, name: 'Scooby Doo' },
{ id: 2, name: 'Shaggy Rodgers' },
{ id: 3, name: 'Fred Jones' }
];
}, 650);
};
});
Markup:
<div ng-controller="SelectAsyncController as vm" layout="column" ng-app="MyApp">
<md-select placeholder="Assign to user"
ng-model="vm.user"
md-on-open="vm.loadUsers()">
<md-option ng-value="vm.user"
ng-repeat="user in vm.users">{{user.name}}</md-option>
</md-select>
<p>Assigned to: {{ vm.user ? vm.user.name : 'No one yet' }}</p>
</div>

You should change ng-value to "user".
<md-option ng-value="user" ng-repeat="user in vm.users">{{user.name}}</md-option>
Here is the working Codepen.

Related

AngularJS - Watch filtered list for changes

Within angular I have a filtered list of people that takes the filter criteria from a predicate function. I want to watch a variable of the filtered list (called filteredPeople) every time the filtered list changes. But I am unable to see when that variable changes.
My code is below:
HTML:
<ul>
<li ng-repeat="person in ($ctrl.filteredPeople = ($ctrl.people | filter: $ctrl.filter))">
...
</li>
</ul>
JS:
controller: ['$scope',
function ($scope) {
var $ctrl = this;
$ctrl.people = {...}
$ctrl.filteredPeople = [];
$scope.$watch($ctrl.filteredPeople, function () {
console.log("called"); //not being called
});
$ctrl.filter = function (p) {
//custom filter function for each item in the array of people
}
}]
I can answer any questions of provide more code if needed
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.items = [
{ name: 'Sam' },
{ name: 'Max' },
{ name: 'Tom' },
{ name: 'Henry' },
{ name: 'Jack' },
{ name: 'Kate' }
]
var counter = 1;
$scope.$watchCollection('vm.filtered', function(){
console.log('Changed' + counter++);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<input type='text' ng-model='vm.filter' />
<ul>
<li ng-repeat='item in vm.filtered = (vm.items | filter : vm.filter)'>{{item}}</li>
</ul>
</div>

ng-Options is not refreshing the wired label when drop-down changed

Following Scott Allen's first code (simple) sample, after drop-down entry changes the wired ng-model is not refreshing this
{{engineer.currentActivity}}
Browser: FF 50.1.0
Angular: 1.5.9
jQuery: 1.7
HTML:
<div ng-controller="EngineeringController">
{{engineer.name}} is currently : {{engineer.currentActivity}}
<div>
choose an activity:
<select id="agDDLClient" ng-model="EngineeringController.currentActivity" ng-options ="act for act in activities">
</select>
</div>
<input type="button" ng-click="what()" value="check" />
</div>
JS:
var aIS = angular.module("app", []);
aIS.controller("EngineeringController", function($scope, $http)
{
$scope.engineer = {
name: "Dani",
currentActivity: "Fixing bugs"
};
$scope.activities =
[
"Writing code",
"Testing code",
"Fixing bugs",
"Dancing"
];
$scope.what = function(){ alert($scope.engineer.currentActivity);}
});
ng-model value should be engineer.currentActivity instead of EngineeringController.currentActivity as you are trying to alert the $scope.engineer.currentActivity inside what() function.
Working demo :
var myApp = angular.module('myApp',[]);
myApp.controller('EngineeringController',function($scope) {
$scope.engineer = {
name: "Dani",
currentActivity: "Fixing bugs"
};
$scope.activities = [
"Writing code",
"Testing code",
"Fixing bugs",
"Dancing"
];
$scope.what = function() {
alert($scope.engineer.currentActivity);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="EngineeringController">
{{engineer.name}} is currently : {{engineer.currentActivity}}
<div>
choose an activity:
<select id="agDDLClient" ng-model="engineer.currentActivity" ng-options ="act for act in activities">
</select>
</div>
<input type="button" ng-click="what()" value="check" />
</div>
Change to ng-model="engineer.currentActivity" instead of ng-model="EngineeringController.currentActivity"
and also your controller code should be follow this
var aIS = angular.module("app", []);
aIS.controller("EngineeringController", function($scope, $http)
{
$scope.engineer = {
name: "Dani",
currentActivity: "Fixing bugs"
$scope.activities =
[
"Writing code",
"Testing code",
"Fixing bugs",
"Dancing"
];
$scope.what = function(){ alert($scope.engineer.currentActivity);}
});

Angular Material md-select item is not saved at window reload

I have a drop down list from where the users are supposed to select the progress for a task. The progress has to be populated at all times, so it cannot be blank. The problem is that I cannot get the controller to get and set the progress information from the service. I admit that I have trouble understanding the logic behind populating the md-select drop down list with the initial progress and getting/setting the new status. I looked at md-select directive on the Angular Material site, but they give clear guidelines about md-on-open and md-on-close, not about initial/default value.
So here is my md-select code in my template:
<md-toolbar style="background-color: #e6e3f4">
<div style="display: flex; flex-direction: row">
<span flex></span>
<md-input-container style="margin: 1px;" ng-controller="SelectOptGroupController">
<md-select ng-model="progress">
<md-option ng-repeat="progress in progresses" value="{{progress}}">{{progress}}</md-option>
</md-select>
</md-input-container>
</div>
</md-toolbar>
It is included in a toolbar, I don't know if that makes any difference or not.
The controller looks like this:
angular
.module('myApp')
.controller('SelectOptGroupController', ['$scope', '$mdDialog', '$stateParams', 'ScorecardStatus', function($scope, $mdDialog, $stateParams, ScorecardStatus) {
$scope.employee.id = $stateParams.employeeid;
$scope.employee.startDate = $stateParams.startdate;
$scope.employee.endDate = $stateParams.enddate;
$scope.progresses = [
"Not started",
"In progress",
"Quality analysis finished",
"Sent for review",
"Completed"
];
$scope.getSelectedProgress = function(progress){
getProgress().then(function(progress){
$scope.progress = progress;
})
}
//console.log($scope.progress);
}])
And here's the service making the calls to and from the Database:
angular
.module('myApp')
.factory('ScorecardStatus', function($http,$rootScope,$q){
var progress;
function setProgress() {
$http({
method: 'POST',
url: 'php/setScorecardProgress.php',
data: {status: selectedStatus, id: employee.id, startdate: employee.startDate, enddate: employee.endDate}
});
}
function getProgress() {
if (!progress)
$http({
method: 'GET',
url: 'php/getScorecardProgress.php'
});
return progress;
}
return {
getProgress: getProgress
}
});
Thanks!
Try to define id for each progress you are using in md-select. As I shown in my example, because you have unique id releated to each progress you can identify any progress by its id. so define your progress model with the id you want to select when the page loads.
<md-toolbar>
<div layout="row" layout-aling="end center">
<span flex></span>
<md-input-container>
<md-select ng-model="progress">
<md-option ng-repeat="progress in progresses" value="{{progress.id}}">{{progress.name}}</md-option>
</md-select>
</md-input-container>
</div>
</md-toolbar>
JS
.controller('TempController', function($scope) {
$scope.progress = 2;
$scope.progresses = [
{
name: "Not started",
id: 0
},
{
name: "In progress",
id: 1
},
{
name: "Quality analysis finished",
id: 2
},
{
name: "Sent for review",
id: 3
},
{
name: "Completed",
id: 4
}
];
});
Here is the wokring example. http://codepen.io/next1/pen/zBNRMe

Autocomplete suggestions not shown after all chips removed (md-chips + md-autocomplete)

In my example (https://jsfiddle.net/vv18yjzo/7/) the autocomplete suggestions should be shown, as long as the md-chips input is focused. This works well when I enter a few items.
However, when all available items are added and then removed, the autocomplete suggestions are empty / hidden. I have to enter a new search text to see the autocomplete suggestions again.
HTML:
<md-input-container class="md-block">
<label>Categories</label>
<md-chips ng-model="vm.selectedCategories"
md-autocomplete-snap
md-require-match="true"
md-on-add="vm.onCategoryAdded($chip, $index)"
md-on-remove="vm.onCategoryRemoved($chip, $index)">
<md-autocomplete
md-selected-item="vm.autocomplete.selectedItem"
md-search-text="vm.autocomplete.searchText"
md-selected-item-change="vm.onSelectedItemChanged()"
md-search-text-change="vm.onSearchTextChanged()"
md-items="item in vm.searchCategories()"
md-item-text="item.name"
md-min-length="0"
md-select-on-match="true"
md-match-case-insensitive="true"
md-no-cache="true"
placeholder="Choose categories">
<span md-highlight-text="vm.autocomplete.searchText"
md-highlight-flags="i">{{item.name}}</span>
</md-autocomplete>
<md-chip-template>
<span>
<strong>{{$chip.name}}</strong>
</span>
</md-chip-template>
</md-chips>
</md-input-container>
JS:
(function () {
'use strict';
angular
.module('app', [
'ngMaterial',
'ngMessages'
])
.factory('CategoriesService', CategoriesService)
.controller('CategoriesCtrl', CategoriesCtrl);
function CategoriesService($q, $timeout) {
var that = {
find: find
},
categories = [
{ name: 'a' },
{ name: 'ab' },
{ name: 'abc' },
{ name: 'abcd' },
{ name: 'abcde' },
{ name: 'abcdef' }
];
function find(search, selectedCategories) {
var deferred = $q.defer(),
result = categories.filter(function(category) {
return (-1 === selectedCategories.indexOf(category) && -1 !== category.name.indexOf(search));
});
$timeout(function() {
deferred.resolve(result);
}, 100);
return deferred.promise;
}
return that;
}
function CategoriesCtrl($scope, CategoriesService) {
var vm = this;
vm.selectedCategories = [];
vm.autocomplete = {
selectedItem: null,
searchText: null
};
/* ... */
vm.searchCategories = function() {
return CategoriesService.find(vm.autocomplete.searchText.toLowerCase(), vm.selectedCategories);
};
}
})();
Example: https://jsfiddle.net/vv18yjzo/7/
There seems to be a weird bug with the autocomplete searchtext, if you remove an item it just unsets the searchtext variable, which then breaks the autocomplete events.
Check this working fiddle https://jsfiddle.net/mgsLkf89/3/
vm.onCategoryRemoved = function($chip, $index) {
if (!vm.autocomplete.searchText)
vm.autocomplete.searchText=null;
};

"Conroller as" with ng-repeat with array of objects (does not work with 'this')

I noticed that for me this shows nothing in ng-repeat:
<ul ng-controller="UserCtrl as ctrl">
<li ng-repeat="user in ctrl.users">
{{user.name}}
</li>
</ul>
But this, works ok:
<ul ng-controller="UserCtrl">
<li ng-repeat="user in users">
{{user.name}}
</li>
</ul>
Why is that?
My Controller code is simple as that:
angular.module('main.controllers').controller('UserCtrl', ['$scope', 'UserResource', function UserCtrl($scope, UserResource) {
$scope.users = UserResource.list(); // works with $resource service
// ideally I would like type "var users = ..." without using $scope
var ctrl = this;
this.users = function() {
return UserResource.list();
};
return this;
}
where users are json array of objects I'm eventually getting as resolved promise result:
[
{ id: "1", name: "name1" },
{ id: "2", name: "name2" },
{ id: "3", name: "name3" }
]
Update:
The whole point is that it does not work with this.users and controller-as notation when it is wrapped in promise that I'm getting from $resource service.
return $resource(' /rest/user-api/user/:id/:action',
{id: "#id"}, //parameters default
{
list: { method: "GET", params: {action : "list"}, isArray: true},
});
In you controller you've got
this.users = function() ..
so in you view you need to execute that function to get users
<li ng-repeat="user in ctrl.users()">
this.users should be rather array like in my demo this.users2 and in that case you can use it in repeater as
<li ng-repeat="user in ctrl.users2">
var app = angular.module('app', []);
angular.module('app').service("userService", function($timeout, $q) {
this.getData = function() {
var deffered = $q.defer();
$timeout(function() {
var arr = [{
id: 1,
name: "Jack from Service"
}];
deffered.resolve(arr);
}, 2000);
return deffered.promise;
};
return this;
});
angular.module('app').controller('UserCtrl', ['userService',
function UserCtrl(userService) {
// ideally I would like type "var users = ..." without using $scope
var ctrl = this;
ctrl.users = function() {
return [{
id: 1,
name: "Jack Function"
}];
};
ctrl.users2 = [{
id: 1,
name: "Jack Array"
}];
////// users from service
this.usersFromService = [];
function activate() {
userService.getData().then(function(data) {
angular.copy(data, ctrl.usersFromService);
});
}
activate();
return this;
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<ul ng-controller="UserCtrl as ctrl">
<li ng-repeat="user in ctrl.users2">
{{user.name}}
</li>
<li ng-repeat="user in ctrl.usersFromService">
{{user.name}}
</li>
</ul>
</body>
My answer is that:
When I do this:
<ul ng-controller="UserCtrl as ctrl">
<li ng-repeat="user in ctr.users">
It does not work.
When I do this:
<ul ng-controller="UserCtrl as userCtrl">
<li ng-repeat="user in userCtrl.users">
or UserCtrl as a or UserCtrl as b ... - it works.
I just does not like ctrl as a name.
Thanks to all who were not voting "close". I can NOT explain how it differs from that I got as replies. In my case I use end-to end angular - NodeJS application that actually does whole client-server communication. Not sure it matters.
Maybe it clashes with another scopes.. I'm not sure yet. But the general advice - try to change the name after "as" and see what happen If you have issue like that. I know it's weird.

Resources