I have an edit form for updating answers in a forum:
<div ng-repeat="answer in question.answers">
...
<div ng-show="answer.editMode" class="card pam">
<edit-answer-form
answer="answer"
></edit-answer-form>
</div>
</div>
The edit answer form is as below:
{{answer.body}} <!--This appears on screen fine-->
<form ng-submit=editAnswer() method="POST">
<input type="text" ng-model="answer.body" />
<button type="submit">Edit</button>
</form>
The Javascript for the directive is:
app.directive('editAnswerForm', function() {
var editAnswerController = ['$scope', 'flashMessageService', 'answerService', '$log', function($scope, flashMessageService, answerService, $log) {
var logger = $log.getInstance('editAnswerFormDirective');
$scope.editAnswer = function() {
logger.info("Edited answer to send to service ", $scope.answer);
answerService.editAnswer($scope.answer).then(function success(response) {
flashMessageService.flashMessageSuccess("Your answer has been edited");
}, function error(response) {
flashMessageService.flashMessageError("Error editing answer");
logger.error("Error editing answer: ", response);
})
};
}];
return {
// restrict: 'E',
templateUrl: '/angular/app/answer/editAnswer.html',
controller: editAnswerController,
scope: {
answer: '='
}
}
});
The $scope.answer is being passed to the directive fine (the answer will appear on screen when putting {{answer.xxx}} on the screen). However in the form, the answer.body value is not present, nor does it update the model when writing into the input field, or logging out the values when submitting the form.
I have a similar directive/form for updating questions and that's working fine. The only difference is the answer is in a ng-repeat. Could it be something to do with this?
Any ideas why this is not working?
Thanks a lot.
Related
This question already has answers here:
Why does Chrome debugger think closed local variable is undefined?
(7 answers)
Closed 5 years ago.
Consider the following controller
angular.module('app')
.controller('formCtrl', ['$scope', '$http', function($scope, $http) {
$scope.var = 1;
$scope.updateData = function () {
debugger; // <-- $scope is undefined here!
}
}]);
dirrective is as follows...
angular.module('app')
.directive('formL', function() {
return {
restrict: 'E',
scope: {
items: '=info'
},
controller: 'formCtrl',
templateUrl: 'js/form/form.html'
};
});
Template is the following
<form class="form-horizontal" ng-controller="formCtrl as controller">
<input type="button" value="BTN" class="btn btn-success" ng-click="updateData()">
</form>
That does not seem to be the common problem (at least I did not found something simmilar in Google and on SO), when I hit button and get into controller $scope is undefined. And at the same time this is equal to $scope as it should be.
What should I do to make $scope be visible inside updateData?
PS. angular version is 1.6.5
UPDATE. I've change directive name from form to formL into above template. form is definetelly not the best name for a dirrective, but it's not the name I have in project, it's a bad simplification of the name for this question. So the problem is not caused by the name of dirrective
The main problem is your directive element matching. It's an infinite loop because your directive template also includes the directive element form. So your directive getting binded again and again and again.
Please check this runnable DEMO FIDDLE and rename your directive element. Do not use form or modify your template and outsource the form element. You also do not need to define a ng-controller inside your form element, while your controller is defined by the directive near controller: 'formCtrl'.
View
<div>
<my-form></my-form>
</div>
AngularJS application
var myApp = angular.module('myApp',[]);
myApp.controller('formCtrl', function ($scope) {
$scope.btn = 'BTN';
$scope.updateData = function () {
$scope.btn = 'BTN clicked';
}
});
myApp.directive('myForm', function () {
return {
restrict: 'E',
replace: true,
template: '<form class="form-horizontal"><input type="button" ng-value="btn" class="btn btn-success" ng-click="updateData()"></form>',
controller: 'formCtrl'
}
});
Update due to question update:
$scope is available inside your $scope function updateData(). Please compare your solution which mine above.
`
I'm experiencing some problems while inputing user data into ordered fields displayed with Angular ng-repeat.
Say that you want some values to display on a list, and those values might be editable. At the same time, you are ordering that data. Due to how ng-model works and Angular reflow cycle, if the value of one input surpases another one while still editing, you'll find yourself typing on the wrong field. Look at this example:
var app = angular.module('app', []);
app.directive('myrow', Row);
app.controller('controller', Controller);
function Controller () {
this.order = '-value';
this.inputs = [
{value: 1, tag: "Peas"},
{value: 2, tag: "Apples"},
{value: 3, tag: "Potatos"}
];
}
function Row($compile, $sce){
var linker = function($scope, $element, $attrs){
var template = '<div>- <input type="number" ng-model="data.value"><span ng-bind="data.tag"></span></div>';
a = $element.html(template);
$element.html(template);
$compile($element.contents())($scope);
}
return {
restrict: 'AE',
replace: true,
scope: {
data: "="
},
link: linker
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<div ng-app="app" ng-controller="controller as ctrl">
List:
<div ng-repeat="item in ctrl.inputs | orderBy: ctrl.order">
<div myrow data="item"></div>
</div>
</div>
I've made this simplified example as the original component has thousands of lines and some dependencies. Here this problem is not reproduced exactly, yet, when you write, sometimes the input loses focus, thing that, for example, doesn't happen when not compiling on the directive (which is completly necessary in my real code). Any ideas on how to solve this? Is it possible to activate ng-model update on change instead of on user typing.
You can use ng-model-options and its updateOn property so that your model is updated only when user leaves the field.
You can see how it works here: https://docs.angularjs.org/api/ng/directive/ngModelOptions (There is a sample in the 'Triggering and debouncing model updates' section)
example:
<input ng-model-options="{ updateOn: 'blur'}" />
I am trying to pop up a modal to get some input, but the angular binding via ng-model seems to be read only. This is my modal markup:
<script type="text/ng-template" id="signatureWindow.html">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Signature Capture</h4>
</div>
<input type="text" width="100px" ng-model="theName" />
<div class="modal-footer">
<button ng-click="accept()" class="btn btn-primary">Accept</button>
<button ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</script>
Then, I invoke this modal as follows:
$scope.getSignatureModal = function(signatureBase64) {
var modalInstance = $modal.open({
templateUrl: 'signatureWindow.html',
controller: 'SignatureModalController',
size: 'lg',
resolve: {
signatureData: function() {
return signatureBase64;
}
}
});
modalInstance.result.then(function(signatureData) {
alert('Signed');
signatureBase64 = signatureData;
}, function() {
alert('Canceled');
});
};
And the following controller code for the modal:
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, signatureData) {
$scope.base64 = signatureData;
$scope.thename = "NamesRus";
$scope.accept = function() {
debugger;
$modalInstance.close($scope.thename);
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}
});
The modal pops up as expected, and the input has the initial value "NamesRus", but when I close the modal, invoking accept in the modal controller, $scope.thename still has it's initial value, not any new value I type when the modal is active. What could be wrong here?
NOTE: At the debugger breakpoint in accept, no matter what I type in the modal's input, theName still has the initial assigned value.
MORE: My Plunker for this works fine. It's when in-place, in an ASP.NET MVC5 project, that I get the strange behaviour.
I think that you mix up two differents scopes.
If you want several variables to be passed to and retrieved from the modal you have to mention them:
in the resolve attribute
resolve: {modalData: function (){return {signatureData:$scope.base64,name:$scope.theName}}}
pass modalData as dependencie to your controller
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, modalData)
update the modal controller $scope with modalData
$scope.signatureData = modalData.signatureData;
$scope.name=modalData.name;
invoke
$modalInstance.close({signatureData:$scope.base64,name:$scope.theName})
reassign the original $scope with the result of promise
modalInstance.result.then(function (data) {
$scope.base64 = data.signatureData;
$scope.thename=data.name;
}
take a look at this plunker forked from ui-boostrap modal orginal example: http://plnkr.co/edit/whLSYt?p=info
I'm a beginner with AngularJS. However, I can't update $rootScope value after submit a form, it's being returned as undefined.
The Controller:
app.controller('campaignCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
$scope.submit = function() {
$rootScope.campaign = this.campaign;
};
}]);
And the form:
<form class="holder" name="campaignForm" ng-submit="submit()" >
<div class="form-group" show-errors>
<label for="inputDate">Date</label>
<p class="help-block"><em>Ex: 12/10/2015</em></p>
<input type="date" class="form-control" name="inputDate" ng-model="campaign.date" id="inputDate" required>
</div>
<button type="submit" class="btn btn-lg btn-default pull-right">Submit</button>
</form>
I used your code and added a $watch on $rootScope.campaign and it worked great.
.controller('someController', function($scope, $rootScope) {
$scope.submit = function() {
$rootScope.campaign = $scope.campaign;
};
$rootScope.$watch('campaign', function(newVal, oldVal) {
if(newVal !== oldVal) {
console.log("New Val = ");
console.log(newVal);
}
});
});
JSFiddle
If you are looking for something that persists across a page refresh, that is not $rootScope. Look at something like this: AngualrJS: sustaining data on html refresh
Check this. Your code seems correct except that you are missing closing tag on the input
`http://jsfiddle.net/ashishmusale/2001cf6r/`
Try
$scope.submit = function() {
$rootScope.campaign = $scope.campaign;
};
EDIT:
Try removing type="submit" from your button. It could be that your handler doesn't get called because the browser handles the form submission automatically (although you could verify that by putting a log in that function).
<button class="btn btn-lg btn-default pull-right">Submit</button>
EDIT 2:
Per our conversation via comments: $rootScope doesn't persist across requests. You'll need to store it somewhere permanent (like local storage or a cookie) or pass it to the server and then back to the client if you want to hold on to that value. I bet if you add a log inside that $scope.submit function, it will have a value there.
I would like to submit a search form on page load if search terms were specified in the route. The problem is that searchForm isn't defined yet when search() runs. I've seen others get this to work by putting ng-controller right on the form element, but I have multiple forms on this page, so the controller has to be a parent of the forms.
How can I ensure the form is defined when I call search()?
myModule.controller('MyController', ['$scope', '$routeParams',
function($scope, $routeParams){
$scope.model = {searchTerms: ""};
$scope.search = function(){
if($scope.searchForm.$valid){
...
}
};
if($routeParams.terms !=""){
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
}
}]);
View:
<div ng-controller="MyController">
<form name="searchForm" ng-submit="search()">
...
</form>
<form name="detailForm" ng-submit="save()">
...
</form>
</div>
This seems to work:
$scope.$on('$routeChangeSuccess', function () {
if($routeParams.terms !=""){
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
}
});
Have you tried just using $watch on searchForm?
if($routeParams.terms != "") {
var unregister = $scope.$watch(function() {
return $scope.searchForm;
}, function() {
// might want to wrap this an if-statement so you can wait until the proper change.
unregister(); //stop watching
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
});
}