AngularJS data binding in controller - angularjs

This works great:
<input type="text" class="search" data-ng-model="name"/>
<div class="rf-contact" data-ng-repeat="contact in contacts | filter: name">
<p class="rf-first">{{contact.first_name}} {{contact.last_name}}</p>
</div>
However I need to implement filter in the controller:
var contactsController = function ($scope, $filter){
$scope.contacts = contacts;
$scope.filteredContacts = $filter('filter')($scope.contacts, $scope.name);
}
<input type="text" class="search" data-ng-model="name"/>
<div class="rf-contact" data-ng-repeat="contact in filteredContacts">
<p class="rf-first">{{contact.first_name}} {{contact.last_name}}</p>
</div>
The problem with the code above is that the data binding is lost. When the data is changing in the text field, the filtering is not happening. Do I need to explicitly set event listeners for the input field in my controller? thanks.

You could try $watch-ing the name:
var contactsController = function ($scope, $filter){
$scope.contacts = contacts;
$scope.filteredContacts = $filter('filter')($scope.contacts, $scope.name);
$scope.$watch('name', function(newValue, oldValue) {
$scope.filteredContacts = $filter('filter')($scope.contacts, newValue);
});
}
For more info on $watch: http://docs.angularjs.org/api/ng/type/$rootScope.Scope. Anytime "something" happens through Angular (like the value of "name" changes because you type something in the text field), Angular will fire the watch you created and execute the function. This is necessary here because the initial code you wrote builds the filteredContacts scope variable when the controller is instantiated and there's nothing re-evaluating this expression.
While this solution with an explicit $watch will work, it's a little hacky. This kind of logic is better encapsulated in a custom filter. You can easily build one with arbitraty logic as described in http://docs.angularjs.org/tutorial/step_09.

Try the following
var contactsController = function ($scope, $filter){
$scope.filterContacts = function(){
$scope.contacts = contacts;
$scope.filteredContacts = $filter('filter')($scope.contacts, $scope.name);
}
}
<input type="text" class="search" data-ng-model="name" ng-change="filterContacts()"/>
<div class="rf-contact" data-ng-repeat="contact in filteredContacts">
<p class="rf-first">{{contact.first_name}} {{contact.last_name}}</p>
</div>
Here's a jsfiddle of this in action:
http://jsfiddle.net/CAuq9/

Related

ng-repeat doesn't update when item added from component directive

I have problem of my code but couldn't find that,I updated an array but it doesn't update in my main view
here my code
app.controller("MainCtrl", function ($scope, $http) {
$scope.user = [];
$scope.adduser = function (fuser, luser) {
var name = { fname: fuser, lname: luser }
// debugger;
$scope.user.push(name);
};
<div ng-app="mainApp">
<div ng-controller="MainCtrl">
<h3>User Info</h3>
<userinfo></userinfo>
<div ng-repeat="name in user">
{{name.fname}}
</div>
</div>
<form name="myForm" ng-controller="MainCtrl">
First Name : <input type="text" ng-model="fname">
<br> Last Name : <input type="text" ng-model="lname">
<button type="button" class="btn btn-primary" ng-click="adduser(fname,lname)">Add</button>
</form>
You need to pass the variable in the directive,
<userinfo user='user'></userinfo>
and the directive as,
scope: {
user: '='
}
controllerAs syntax makes available your controller function this(context) accessible via its alias, here it is c. As you're using controllerAs syntax, Your binding values should be bounded to controller function context (this). You should be avoid using $scope in controller in controllerAs approach. Even while calling adduser method call it by controller alias c.adduser
Code
app.controller("MainCtrl", function ($http) {
var c= this;
c.user = [];
c.adduser = adduser;
function adduser(fuser, luser) {
var name = { fname: fuser, lname: luser }
// debugger;
c.user.push(name)
};
})

Angularjs calculate based on formula given

Im new in Angularjs
and I would like to do something like the following in directive
<!--get numb1&numb2 from user input-->
<div>
<input ng-model="numb1" type=number/>
</div>
<div>
<input ng-model="numb2" type=number/>
</div>
<!--result display on the following input box, not allow to edit-->
<div>
<input ng-model="result" formula="some formula here, can be anything" readonly/>
</div>
numb1 & numb2 can be change anytime, use $watch instead on ngChange.
Can anyone guide me on this?
You can achieve your requirement with the following code snippet.
The HTML
<div ng-app="demo">
<div ng-controller="DefaultController as vm">
<inputs formula="vm.formula"></inputs>
</div>
</div>
<script type="text/template" id="inputsTemplate">
<div>
<input type="number" ng-model="vm.a"/>
</div>
<div>
<input type="number" ng-model="vm.b"/>
</div>
<div>
<input type="number" ng-model="vm.result" readonly/>
</div>
</script>
The AngularJS code
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('InputsController', InputsController)
.directive('inputs', inputs);
function DefaultController() {
var vm = this;
vm.formula = 'a + b';
}
function inputs() {
var directive = {
restrict: 'E',
scope: {
formula: '='
},
template: function () {
return angular.element(document.querySelector('#inputsTemplate')).html();
},
controller: InputsController,
controllerAs: 'vm',
bindToController: true
};
return directive;
}
InputsController.$inject = ['$scope', '$parse'];
function InputsController($scope, $parse) {
var vm = this;
var expressionFunc = $parse(vm.formula);
$scope.$watchGroup(['vm.a', 'vm.b'], function (newValue, oldValue, scope) {
if (angular.isDefined(vm.a) && angular.isDefined(vm.b)) {
var result = expressionFunc(vm);
vm.result = result;
}
});
}
The jsfiddle link here
The coding style is followed from angular-styleguide by John Papa and from Pro AngularJS by Adam Freeman
Explanation:
There is a main controller which is having a formula set on its scope such as a + b and which is passed to a directive called inputs through an attribute called formula, this an element directive meaning that it is restricted to be applied only as an individual html element.
The inputs directive gets its template from a script tag having html content
by using the template property of the directive definition. The inputs directive has its own controller bound using the controller aliasing syntax.
The inputs directive has three objects on its controller scope they're a, b and result and the magic happens in the InputsController using the $parse service which returns a function when an expression is parsed which can indeed be evaluated on an object to execute that expression and get the result i.e.
var expressionFunc = $parse(vm.formula);
var result = expressionFunc(vm);
vm.result = result;
Also as we need to watch both a and b objects on the scope, we can use the $watchGroup function on the $scope object to handle the change events and update the view accordingly.
Note: we are only parsing once var expressionFunc = $parse(vm.formula); and executing the expression many times var result = expressionFunc(vm); this is an important factor and should be considered for quality of the code and for performance.
You are in the same scope, so, you can use ng-value in order to set a new value given numb1 and numb2. For example, you formula is numb1 + numb2, just try something like this:
<input ng-model="result" ng-value="{{numb1 + numb2}}" readonly/>

Angular watch form input doesnt work

I have a form in html page :
<div ng-controller="ctrl as c">
<form name="myForm">
<input type="text" name="myInput" require/>
</form>
</div>
I want to watch changes of input in my controller, so I did like this :
angular.module('app').controller('ctrl', ctrl);
ctrl.$inject = ['$scope'];
function ctrl($scope) {
var vm = this;
$scope.$watch('myForm.myInput', function (value) {
//check validity
});
}
But when I change input value, nothing happen in controller.
Any idea?
<div ng-controller="ctrl as c">
<form name="myForm">
<input type="text" name="myInput" ng-model="c.myInput" require/>
</form>
</div>
Controller
function ctrl($scope) {
var vm = this;
vm.myInput = 'hello';
$scope.$watch(function(){
return vm.myInput;
}, function(newValue, oldValue) {
console.log(newValue)
});
}
The $scope.watch() function creates a watch of some variable.
So you need to bind a scope to your input and creates watch for that variable.
View:
<input type="text" ng-model="myInput" name="myInput" require/>
Controller:
function ctrl($scope) {
var vm = this;
$scope.myInput = "";
scope.$watch('myInput', function(newValue, oldValue) {
// Your logic
});
}
I believe watching the entire form may create a performance hit, still they are ways to watch multiple variable using watch
1. Create a $scope object like
$scope.form = {
name:"My Name",
Id:"My Id:,
....
}
Now you can use $watch with a third variable 'true'
$scope.$watch('form', function(newVal, oldVal){
console.log('changed');
}, true);
Html:
<input type="text" ng-model="form.name" name="myName" require/>
<input type="text" ng-model="form.id" name="myId" require/>
2. Use $scope.$watchCollection
$scope.$watchCollection('[item1, item2]', function(newValues, oldValues){
// You logic here
// newValues and oldValues contain the new and old value of the array
});
Also check this post for different ways of listening changes to multiple elements using $watch

angularJS radio buttons not functioning

I'm having trouble communicating with my angularJS radio buttons. I'm using the material design framework. I'm fairly new to angular.
HTML
<div ng-controller="MainCtrl as ctrl">
<md-radio-group class="user-type">
<div layout="row" layout-sm="column" layout-align="space-between" layout-align-sm="space-around center">
<md-radio-button ng-model="userType" value="prospective" name="user_type" ng-change='newValue(value)'>Prospective Patient</md-radio-button>
<md-radio-button ng-model="userType" value="patient" name="user_type" ng-change='newValue(value)'>Patient</md-radio-button>
<md-radio-button ng-model="userType" value="caregiver" name="user_type" ng-change='newValue(value)'> Caregiver </md-radio-button>
<md-radio-button ng-model="userType" value="doctor" name="user_type" ng-change='newValue(value)'>Doctor</md-radio-button>
</div>
</md-radio-group>
</div>
JS
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
$scope.newValue = function(value) {
console.log(value);
};
$scope.$watch('userType', function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
My ng-change isn't firing and my $watch isn't working either.
Can anyone find where I'm going wrong? I can't communicate between my controller and view!
When you use the controller as syntax, you should bind to that instead of the scope. I think the md-radio-button directive was creating a child scope that was messing things up but hard to reproduce without that directive.
Here's a plunker with the model and click bound to ctrl instead of $scope: http://plnkr.co/edit/fSTBDAMZLFKJgRD4br9K?p=preview
Radios changed to input, but referencing the ctrl:
<input type="radio" ng-model="ctrl.userType" value="prospective" name="user_type" class="user-type-rdo md-warn md-hue-2" ng-change='ctrl.newValue(value)'>Prospective Patient
And the controller updated to move the newValue function off $scope:
.controller('MainCtrl', ['$scope', function($scope) {
var self = this;
this.newValue = function(value) {
console.log(value);
};
$scope.$watch(function(){return self.userType;}, function(value){
if(value == "patient"){
console.log(value);
self.showPatientStepTwo = true;
}else{
console.log(value);
self.showPatientStepTwo = false;
}
});
}])
The newValue function logs undefined - not sure what you were trying to do there, but you can use self.userType in the newValue function if you want the value.
First thing: You don't need to declare an ng-model on ea. angular-material radio button when using radio groups, as per the angular-material docs for radio buttons.
The second thing is, the standard $scope events behave a bit differently when you build your controllers using controllerAs syntax See controllerAs Reference.
function MainController($scope, $log) {
var vm = this;
vm.title = 'Some Title';
vm.showPatientStepTwo = false;
// does not work
$scope.$watch('userType', function(newVal, oldVal){
// do work
});
// works
$scope.$watch('vm.userType', function(newValue, oldValue) {
// do work with newValue
});
// also works
$scope.$watch(function() {
return vm.userType;
}, function(newValue, oldValue) {
vm.showPatientStepTwo = newValue === 'patient';
});
}
Working plunkr: http://plnkr.co/edit/Dth67cQJKarwt3NiE9yp
<div class="form-group">
<label>Type of ad <b class="text-danger">*</b></label><br>
<input type="radio" name="typeofAd" value="sell" ng-model="product.typeofAd"> I want to sell
<input type="radio" name="typeofAd" value="buy" ng-model="product.typeofAd"> I want to buy
</div>
radio button this way works fine for me
refer link
https://scotch.io/tutorials/handling-checkboxes-and-radio-buttons-in-angular-forms
you need to add ng-model and ng-change event like below.
<md-radio-group ng-model="selectedVal" ng-change="showSelected()">
and in your controller you can defined function as following.
$scope.showSelected= function(){
console.log($scope.selectedVal);
}

One value won't save in AngularJS

For some reason the comment.content part turns out to be null every time I try to save it, but the comment.pid works just fine, why is that? It's really not making sense to me.
The controller:
var CreateCommentController = function ($scope, $location, $routeParams, Comment) {
$scope.action = "Create";
var id = $routeParams.postId;
var content = $scope.content;
var comment = new Comment();
comment.pid = id;
comment.content = content;
$scope.save = function () {
Comment.save(comment, function () {
$location = '/';
});
}
};
The HTML:
<div class="control-group" ng-class="{error: form.content.$invalid}">
<label class="control-label lbl" for="content">Content:</label>
<div class="controls">
<textarea ng-model="content" id="content" class="textareaContent">content</textarea>
</div>
Try ng-model="comment.content" directive on the textarea. Also you should remove the optional content you provided between the opening and closing textarea tags. If you want to initialize it with some default content that should be done on the model:
<textarea ng-model="comment.content" id="content" class="textareaContent" ></textarea>
Also you might need to pass the comment instance in the scope:
$scope.comment = comment;

Resources