Angular 1 dynamic form object - angularjs

I'm using Angular 1 and creating a dynamic form. It works by looping through some objects and rendering dynamically binded input fields like:
<div class="quest-form form-group" ng-repeat="task in tasks">
<div ng-if="task.Class == 'TaskText'" ng-class="'task ' + task.Class">
<input ng-model="questForm.Task[task.ID].Value" ng-name="task_{{task.ID}}" ng-required="task.Required == 1" type="text" class="form-control" placeholder="{{task.Title}}" />
</div>
...
...
</div>
I also have a upload field in the loop:
<div ng-if="task.Class == 'TaskUpload'" ng-class="'task ' + task.Class">
<input class="btn btn-primary upload-btn" ngf-max-size="10MB" type="file" ng-model="upload" ngf-multiple="false" ngf-select="uploadFile(upload, task.ID, $invalidFiles)" />
<input class="" ng-model="questForm.Task[task.ID].FileUploadID" ng-required="task.Required == 1" ng-name="task_{{task.ID}}" type="text" />
</div>
When the file uploaded event is called I'm trying to set the value of the hidden field which is ng-model="questForm.Task[task.ID].FileUploadID" like this:
$scope.uploadFile = function(file,taskID) {
file.upload = Upload.upload({
url: assetsURL+'/home/UploadFile',
data: {file: file}
});
file.upload.then(function (response) {
$scope.questForm.Task[taskID].FileUploadID = response.data; // THIS MESSES UP
}, function (response) {
...
});
};
I get the following error, it's like $scope.questForm.Task[128] does not exist even though the hidden input looks correct and is binded to the $scope.questForm.Task[128].
angular.js:14362 TypeError: Cannot read property '128' of undefined
at file.upload.then.$scope.errorMsg (http://localhost/carl-mygps-app/js/controllers/quest-details-controller.js:120:26)
at processQueue (http://localhost/carl-mygps-app/bower_components/angular/angular.js:16689:37)
at http://localhost/carl-mygps-app/bower_components/angular/angular.js:16733:27
at Scope.$eval (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18017:28)
at Scope.$digest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:17827:31)
at Scope.$apply (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18125:24)
at done (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12233:47)
at completeRequest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12459:7)
at XMLHttpRequest.requestLoaded (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12387:9) Possibly unhandled rejection: {}
I have tried defining blank objects in the scope like:
$scope.questForm = [];
$scope.questForm.Task = {};
But I should not need to because they are created in the template? confused. Thanks all.

Actually nope. Having your template compiled does not mean all the ng-models are initialized. While ng-model is smart enough to create all the intermediate objects, if they don't exist, it doesn't do so until $viewValue is changed. In your case if you upload a file without editing any other input first, $viewValue for inputs has never changed, and thus you have to initialize questForm, questForm.Task, and questForm.Task[taksID] yourself.
if (!$scope.questForm) {
$scope.questForm = {};
}
if (!$scope.questForm.Task) {
$scope.questForm.Task = {};
}
if (!$scope.questForm.Task[taskID]) {
$scope.questForm.Task[taskID] = {};
}
$scope.questForm.Task[taskID].FileUploadID = response.data;
Or you can initialize questForm and questForm.Task at the beginning. And only check if questForm.Task[taskID] exists before initializing it.

Related

How to empty form inputs in angular js after submission

Hello amazing Stackoverflow Coders, Please how do I empty form inputs in angular Js after form submission.
below is my working code
$rootScope.sendNow = function () {
if ($rootScope.send_login.username.length < 1)
alert("Please enter username");
else if ($rootScope.send_login.password.length < 1)
alert("Please enter password");
else {
$http.post($rootScope.server_url + "post_Data", $rootScope.send_login)
.success(function (response)
{
// on success
$('#send_login.username').val('');
$('#send_login.password').val('');
toastr.success('Successful');
}
).error(function (response)
{
alert("sending failed");
});
}
};
My Form is here below
<input type="text" id="username" name="username" ng-value="send_login.username">
<input type="text" id="pass" name="pass" ng-value="send_login.password">
<input type="button" ng-click="sendNow()" value="Send">
Unlike Ajax, I have tried this below but cannot get it to work
$('#send_login.username').val('');
$('#send_login.password').val('');
you should use ng-model in the input tag, like:
html:
<input type="text" ng-model="name">
<button ng-click="sendData()"> Send</button>
js (angularjs Controller):
$scope.sendData = function (item) {
$scope.name = "";
}
Its better to take every form field ng-modal as a property of a object, like you have taken in "send_login".
After form submission reinitialize this object send_login = {};
Every field will be reinitialize. and a undefined property doesn't create a error in angular.
You should avoid so much use of $rootScope in your code.
You need to change ng-value to ng-model (to bind the angular variable to html input) and on ajax success clear the binded variable:
$http.post('...').success(function (response) {
$rootScope.send_login = {};
});

Is this the correct way of binding factory to controller?

I have the following code in my module:
.controller('ModalInstanceCtrl', function($rootScope, $scope, emailService) {
$scope.emailService = emailService; // Good or not; if not, why?
$scope.showed = false;
$rootScope.$watch('showed', function () { $scope.showed = $rootScope.showed; }); // In case you wonder why I did this - I'm using this trick to prevent watch from firing twice, because that would happen if I remove the watch below and put its code here.
$scope.$watch('showed', function () {
if (!$rootScope.showed) return;
$scope.selected = 0;
$scope.primary = true;
$scope.verified = true;
if (emailService.emails.length == 0) emailService.load();
});
$scope.EmailSelected = function () {
emailService.setCurrent($scope.selected);
$scope.primary = emailService.emails[$scope.selected].primary;
$scope.verified = emailService.emails[$scope.selected].verified;
};
});
.factory('emailService', function($resource, $http) {
var emails = []; // [{email: 'sample#email.dom', verified: true, primary: false}, ...]
var selected = 0;
function sendreq(action, email){
$http({
method: 'POST',
url: '/email/',
data: "action_" + action + "=&email=" + email,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(response) {
console.log(response.data);
return true;
}, function(data){
return data;
});
}
return {
emails: emails,
selected: selected,
setCurrent: function(curr){
selected = curr;
},
load: function(){
$resource('/api/email/?format=json').query({},
function success(result) {
emails.push.apply(emails, result);
});
},
add: function(email) {
for (var e in emails) if (emails[e].email == email) return false;
return sendreq('add', email);
},
remove: function() {
sendreq('remove', emails[selected].email);
}
}
})
And this code in my HTML template:
<div ng-repeat="e in emailService.emails">
<input type="radio" ng-model="$parent.selected" ng-value="$index" ng-change="EmailSelected()" id="email_{{ $index }}" name="email">
<label for="email_{{ $index }}" ng-bind='e.email'></label> <span ng-show="e.verified">Verified</span> <span ng-show="e.primary">Primary</span>
</div>
<div><button ng-disabled="primary" ng-click="emailService.remove()">Remove</button></div>
<form novalidate>
<input class="form-control" type="email" name="email" ng-model="email" placeholder="Email">
<input type="submit" ng-disabled="email === undefined" ng-click="emailService.add(email)" value="Add Email Address">
</form>
And I want to ask, whether I've correctly assembled the module and template because I'm working with AngularJS for the first time. Specifically, I want to ask if it's correct to bind the whole factory to the scope? Also if someone has more time he can look at the other code to see if everything is right or not. Feel free to write any suggestions about my code.
Thanks in advance!
It always depends on particular case.
This way boilerplate wrapper methods
$scope.add = (...args) => emailService.add(...args);
can be omitted, as well as their tests in controller spec.
Another benefit is that it provides existing object for proper data binding and scope inheritance of scalar scope properties:
<parent-scope>
<p ng-init="emailService.selected = 0"></p>
<child-scope>
<p ng-init="emailService.selected = 1"></p>
{{ emailService.selected === $parent.emailService.selected }}
</child-scope>
</parent-scope>
This certainly would not work as expected if there's no emailService object. This is particularly useful when controllerAs syntax isn't used.
There's nothing wrong with exposing a service to scope - if its API matches the controller scope. And this may indicate an antipattern if it doesn't - or if there are too many services that are abused like that.
Why do you want to bind the entire service ? I do not see a need to that in your code. You are invoking parts of the service using the service handler, there is no specific need to put the entire service on scope.

Angularjs ng-if not working with ng-model

I am using angularjs to integrate my api.
I am facing problem with using ng-if inside textbox.
so below is my snippet of HTML code:
<input type="text" value="" data-ng-if="edit" ng-model="name">
<input type="text" value="" data-ng-if="!edit" ng-model="singleAppDetails.name">
Here edit variable is there in scope
that is in my controller i have declared it like this:
$scope.edit = false;
So if edit is false it should get bind with ng-model="name"
and if edit is true it should get bind with ng-model="singleAppDetails.name"
But it is not binding it as expected.
Further I am using $http.post to post the data to server like below:
$scope.addApp = function(){
$scope.apps = [];
$scope.apps.push({'name':$scope.name, 'domain':$scope.domain, 'appId':$scope.appId, 'secret':$scope.secret});
// Writing it to the server
//
var dataObj = {
name : $scope.name,
domain : $scope.domain,
appId : $scope.appId,
secret : $scope.secret
};
var res = $http.post('http://192.168.1.30:8090/apps/', dataObj);
res.success(function(data, status, headers, config) {
$scope.message = data;
});
res.error(function(data, status, headers, config) {
alert( "failure message: " + JSON.stringify({data: data}));
});
// Making the fields empty
//
$scope.name='';
$scope.domain='';
$scope.appId = '';
$scope.secret = '';
};
But this always sends null data.
ng-if has its own scope. So the name attribute that is populated by the first input is in the ng-if scope instead of being in your controller scope.
The second input should work fine, provided that your controller initializes singleAppDetails to a non-null object:
$scope.singleAppDetails = {};
Rule of thumb: always have a dot in your ng-model. Always populate objects in the scope rather than the scope itself.
ng-if is creating a child scope because that the input elements does not see the scope variable defined in the controller you have two ways to solve this problem
use an object reference
ex :
$scope.user = { name : "" }
inside the template
<input type="text" ng-model='user.name' />
you can tell angular to look for the variable in parent scope instead child scope
<input type="text" ng-model='$parent.name' />

TypeError: Cannot call method 'replace' of undefined

I am pretty new to angularJS. I want to use a typeahead for one of my textboxes but when i use it it does that error:
TypeError: Cannot call method 'replace' of undefined
at Scope. (http://XX.YY.ca/js/lib/ui-bootstrap.js:3426:32)
at fnInvoke (http://XX.YY.ca/js/lib/angular.js:10017:21)
at OPERATORS.| (http://XX.YY.ca/js/lib/angular.js:9532:59)
at Object.extend.constant [as get] (http://XX.YY.ca/js/lib/angular.js:9962:14)
at Scope.$digest (http://XX.YY.ca/js/lib/angular.js:11800:40)
at Scope.$apply (http://XX.YY.ca/js/lib/angular.js:12061:24)
at HTMLButtonElement. (http://XX.YY.ca/js/lib/angular.js:17839:21)
at HTMLButtonElement.n.event.dispatch (http://code.jquery.com/jquery-1.11.0.min.js:3:8066)
at HTMLButtonElement.r.handle (http://code.jquery.com/jquery-1.11.0.min.js:3:4767)
My code is basically the same as the one on the angular-ui website. Here it is:
Markup:
<div class="modal-body">
<div class="form-group">
<input type="text" id="customer" autocomplete="off"
ng-model="nom" tabindex="0"
typeahead="customer.customerFirstName for customer in getCustomers($viewValue) | filter:$viewValue"
typeahead-wait-ms="300" typeahead-on-select="setId($item)"
typeahead-editable="false" class="form-control input-sm" placeholder="Customer" />
</div>
</div>
Controller:
[...]
$scope.getCustomers = function (val) {
return $http.get('/index.php/customers/get_customers_ajax', {
params: {val: val}
}).then(function (res) {
var customers = [];
angular.forEach(res.data, function (item) {
customers.push({
'customerID': item.customerID,
'customerFirstName': item.customerFirstName,
'customerLastName': item.customerLastName
})
})
console.log(customers);
return customers;
});
}
$scope.setID = function ($item) {
$scope.newOrder.customerID = $item.customerID;
Console.log($scope.newOrder.customerID);
}
});
Backend response:
[
{"CustomerID":"1","CustomerLastName":"Pan","CustomerFirstName":"Petter"},
{...}
]
I don't know what additionnal information i can provide, but if any, ask and i will post !
I think you should change this line:
typeahead="customer.customerFirstName for customer in getCustomers($viewValue) | filter:$viewValue"
It seems that the syntax should be:
typeahead="<name> for <name> in getCustomers($viewValue) | filter:$viewValue"
if <name> in the first position is not equal to <name> in second position it returns an error.
Try to change your code to:
typeahead="customer for customer in getCustomers($viewValue) | filter:$viewValue"
And in your javascript put customerFirstName direcly in returned array.
I think the problem is you are setting ng-model="nom", but you don't have a $scope.nom variable defined in your controller. Angular is trying to bind the customer selected from the typeahead to a nom variable in your controller.

angularjs - undefined error from input text field

Trying to initialize text input box for user input but getting error. $scope can set intext when uncommentted. I've hacked around this (sort of) but I'm missing something basic as usual.
Console error starts: 'Error: $scope.intext is undefined' unless I assign a value. The input box is exclusively for user input. Also noticed I can assign 'why' and don't get the error until I try to split.
angular.module('myApp', [])
.controller('TextScreen', ['$scope', function($scope) {
//$scope.intext = "what";
var why = $scope.intext.split('-');
}]);
html
<div id="cont" ng-app="myApp">
<div ng-controller="TextScreen">
<input type="text" ng-model="intext" />
</div>
</div>
The reason is that $scope.intext is undefined. intext.
It is unclear what you are trying to do but I would suggest initializing intext or moving your code to a function. Like this:
$scope.change = function() {
var why = $scope.intext.split('-');
};
html
<input type="text" ng-model="intext" ng-change="change()" />
Like suggested in angular documentation here:
http://docs.angularjs.org/api/ng.directive:ngChange
Or try using ng-init
ng-init="intext= 'demo'"
Sample
<input type="text" ng-model="intext" ng-init="intext= 'demo'" />
O
The
var why = $scope.intext.split('-');
is being processed immediately, but at that moment, $scope.intext is undefined, therefore you cannot call .split on it.
If you are trying to act on the value the user enters, you should place a watch on it
$scope.$watch('intext', function(oldvalue, newvalue){
if(angular.isDefined(newvalue) && newvalue != oldvalue) //ensuring undefined should not processed
var why = newvalue.split('-');
});

Resources