formData not changing with ngChange hook - angularjs

I have a very basic scenario where in which I have a module with one controller:
var myModule = angular.module('myModule', []);
myModule.controller('myModuleCtrl', function ($scope, $http) {
$scope.formData = {url:'',title:'',source:''};
$scope.init = function() {
$scope.formData.url = 'Test';
$scope.formData.title = '';
$scope.formData.source = '';
};
$scope.manageUrl = function() {
alert('update');
};
});
In my view I'm trying to hook the formData object properties to some form fields using ngModel. However my input doesn't update it's value after the init() method runs. If I add the ngChange directive and hook that up with the $scope.manageUrl() method, it only runs once, after my first keystroke/change of the input.
Am I missing something here? I've used both directives before without any problems. Only thing I can think of is something wrong with my module/controller setup?
This is what my view looks like:
<div ng-app="myApp" ng-controller="myModuleCtrl" ng-init="init()">
<div>
<form name="myForm">
<div>
<input type="url" ng-model="formData.url" ng-change="manageUrl()" />
</div>
</form>
</div>
</div>
And my application.js bootstrapper:
var app = angular.module('myApp', ['myModule']);

It happens because of the url validator, note how the whole url property is removed until you enter a valid url, that's when you'll start to get your alerts back. Basically, once removed, the url is never considered to be changed until a valid (and different) url is input.
url is set to 'Test'
you type anything into the box, it fails the validation and becomes undefined
it stays undefined until you enter a valid url (it's not changing)
Start typing http://anything and see what happens yourself:
var myModule = angular.module('myModule', []);
myModule.controller('myModuleCtrl', function ($scope, $http) {
$scope.formData = {url:'',title:'',source:''};
$scope.init = function() {
$scope.formData.url = 'Test';
$scope.formData.title = '';
$scope.formData.source = '';
};
$scope.manageUrl = function() {
alert('update');
};
});
var app = angular.module('myApp', ['myModule']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myModuleCtrl" ng-init="init()">
<div>
<form name="myForm">
<div>
<input type="url" ng-model="formData.url" ng-change="manageUrl()" />
<br>
{{formData}}
</div>
</form>
</div>
</div>
Am I missing something here? I've used both directives before without any problems. Only thing I can think of is something wrong with my module/controller setup?
The only logical solution that comes to my mind is that you've used a different input type before, so you were not a subject to validations. If you change the type to text, it works fine the whole time.

Related

AngularJS $watch not working properly

Recently I am learning Angularjs, my code seems not work as expected:
this is my div:
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="data.name" value=""/>
{{data.count}}
</div>
</div>
my controller is:
<script>
var app = angular.module('myApp',[]);
app.controller('myController', function($scope) {
$scope.data = {
name:"tom",
count = 0
}
$scope.$watch('data', function(oldValue,newValue) {
++$scope.data.count;
},true);
})
</script>
what I expect is when I type something in the <input> box, the {{data.count}} will increase by 1 each time. However the code is initially 11 and each time I make changes in the input field, the count is increased by 11, can someone help me find where have I done wrong? Thanks a lot in advance.
Why this happen?
Watcher calls multiple times because you created watcher for full object data. Flag true will create sub-watcher for every value in object.
Its a proper behavior. I believe you want something like:
$scope.$watch('data', function(oldValue,newValue) {
if(oldValue.name != newValue.name){
++$scope.data.count;
}
},true);
Demo Fiddle
The second solution is to watch on name only:
$scope.$watch(function(){
return $scope.data.name
}, function(oldValue,newValue) {
++$scope.data.count;
});
Here is a another way to do it. Use the ng-keydown directive and update the count only when a key is pressed inside the input element.
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
$scope.data = {
name: "tom",
count: 0
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app='myApp'>
<input type="text" ng-model="data.name" value="" ng-keydown="data.count = data.count+1" /> {{data.count}}
</div>

When does "$viewValue" become "NOT undefined"?

Here is my example to count value length in the input text:
let app = angular.module('myApp', []);
app.controller('myController', function ($scope) {
$scope.submit = function ($event, form) {
$event.preventDefault();
alert(form.myinput.$viewValue.length)
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form id="myForm" ng-model="myForm" name="myForm" novalidate ng-app="myApp" ng-controller="myController">
<input ng-model="myinput" name="myinput" />
Submit
</form>
The problem: If the value in the input is null or empty (the input contained nothing before), it would throw this error when clicking on submit:
TypeError: Cannot read property 'length' of undefined
at a.$$childScopeClass.$$childScopeClass.$scope.submit
Then, I've tried to type something in the input, delete it and click on submit again. It should work.
My question: for input[type=text], is there nothing like default value with property $viewValue?
I mean: if the value is null or empty, form.myinput.$viewValue should be ''. So, the length must be 0.
Try this :
It will check first for null and empty value of the text box and then perform operation according to that.
var app = angular.module('myApp', []);
app.controller('myController', function ($scope) {
$scope.submit = function () {
if($scope.myinput != null && $scope.myinput.length > 0) {
alert($scope.myinput.length);
} else {
alert("Please enter the text");
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form id="myForm" name="myForm" novalidate ng-app="myApp" ng-controller="myController">
<input ng-model="myinput" name="myinput" />
<button ng-click="submit()">Submit</button>
</form>
You need to access it via the scope. $scope.form.myinput.$viewValue.length
That being said I do not believe that controllers should know about form as forms are a view concept. Anything to do with the form variable should not make their way into your controllers. I am a big fan of not passing the $scope into your controllers at all and using the controller as syntax.
Here is how I would do it. This will only work with Angular 1.3 or greater as 1.2 doesn't support controller as.
let app = angular.module('myApp', []);
app.controller('myController', function () {
var vm = this;
vm.submit = function () {
alert(vm.myinput);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<form id="myForm" name="myForm" novalidate ng-app="myApp" ng-controller="myController as vm">
<input ng-model="vm.myinput" name="myinput" />
<button ng-click="vm.submit()">Submit</button>
</form>

Angular binding error

I have a curious error on my angular app.
I have a controller where I declare some variables. If I am going to write them in the html it works as expected. But if I try to change them, eg in a input-box nothing happens.
Even when I try to call a function from a button or something nothing happens.
The code below is a simplified version of my controller.
angular.module('myModule', [])
.controller('myCtrl', ['$rootScope', '$scope', 'SomeServices',
function ($rootScope, $scope, someServices) {
'use strict';
$scope.myVar = "foobar";
$scope.myFunction = function() {
// it never gets here
}
$scope.$watch("myVar", function() {
// it even never gets here
});
}]);
My HTML
<!-- change this and the watcher will not doing anything -->
<!-- the value will bee foobar, but after that nothing happens -->
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
If I tried it in codepen it works normaly.
Has anyone even got such a behaviour?
I don't see any problem why it should not work, may be this is more simplified demonstration,
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.myVar = "foobar";
$scope.myFunction = function() {
console.log($scope.myVar + ' myFunction called')
}
$scope.$watch("myVar", function() {
console.log($scope.myVar + ' value changed')
});
});
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9">
</script>
<div ng-app="myApp" ng-controller="MainCtrl">
<input type="text" ng-model="myVar" />
<button ng-click="myFunction()">click here and nothing will happen</button>
</div>
Open developer tool to see logs.
Elaborate more if you are facing some other problem.

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;

Very simple ng-model watch not working

Here is the jsfiddle.
http://jsfiddle.net/CLcfC/
code
var app = angular.module('app',['']);
app.controller('TestCtrl',function($scope){
$scope.text = 'Change Me';
$scope.$watch('text',function(){
alert('Changed !');
});
})
HTML
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="TestCtrl">
<input type="text" ng-model='text'/>
<span>{{text}}</span>
</div>
</div>
I am not able to see the change in $scope.text. Please help.
This is so easy but what am I missing?
Change the module creation to this, make sure you don't put a empty string in the []. (Obvious the empty string is not a module that can be injected.)
var app = angular.module('app', []);
Demo: http://jsfiddle.net/MWa66/
Your JavaScript file loads after the AngularJS initialization and that's why it fails to find your module. In order to fix it change the initialization to a manual initialization.
First change your HTML and remove the ng-app directive:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<div id="appRoot">
<div ng-controller="TestCtrl">
<input type="text" ng-model='text'/>
<span>{{text}}</span>
</div>
</div>
Then go to your JavaScript and use angular.bootstrap method to manually attach your module:
var app = angular.module('app',[]);
app.controller('TestCtrl',function($scope){
$scope.text = 'Change Me';
$scope.$watch('text',function(){
alert('Changed !');
});
});
angular.element(document).ready(function() {
angular.bootstrap(document.getElementById('appRoot'), ['app']);
});
You can find more help on manual AngularJS initialization here.
Thank you! I solved this annoying thing!
The solution that worked for me was that I use angular UI router and there I had used the following code
.state('app.monimes', {
url: "/monimes",
views: {
'menuContent' :{
templateUrl: "templates/monimes.html",
controller: 'sampleCtrl'
}
}
})
so then in the controller I had
/***
*
*Controller for tests..
*/
.controller('sampleCtrl',['$scope','sampleService', function($scope, $sampleService) {
$scope.username="em";
// Watch for changes on the username property.
// If there is a change, run the function
$scope.$watch('username', function(newUsername) {
// uses the $http service to call the GitHub API
// //log it
$scope.log(newUsername);
// and returns the resulting promise
$sampleService.events(newUsername)
.success(function(data, status, headers) {
// the success function wraps the response in data
// so we need to call data.data to fetch the raw data
$scope.events = data.data;
});
},true);
}
]);
and in the view I had
<div>
<label for="username">Type in a GitHub username</label>
<input type="text" ng-model="username" placeholder="Enter a GitHub username, like a user" />
<pre ng-show="username">{{ events }}</pre>
</div>
but that didn't work.
so I added ng-controller="sampleCtrl"
to the div and now it works :D
so that means that the view is loaded after the controller loads and the watcher doesn't get added to the watching variable.

Resources