AngularJS: ng-model not clearing input - javascript

I have a dynamic input for CPFs (Brazilian 'social security' number).
Every time I enter one CPF, another input should be displayed, and so on..
But for some reason, the ng-model is not being cleared after the CPF is added.
Here's my HTML (inside a directive with isolate scope):
<div ng-repeat="cpf in cpfs" class="field-input">
<input type="text" class="field" ng-model="cpf.number" required>
<label>added CPF</label>
</div>
<div class="field-input">
<input type="text" class="field" ng-model="cpf.number" required>
<label>add new CPF</label>
<button ng-click="addCpf(cpf)" class="btn btn-primary">add</button>
</div>
Here's my controller (inside a directive with isolate scope):
$scope.cpfs = [];
$scope.addCpf = function(cpf) {
$scope.cpfs.push(angular.copy(cpf));
delete $scope.cpf;
};

instead of delete $scope.cpf; use $scope.cpf.number = "";
we can't delete an model, we have to set it to blank, because its linked with our View part

Related

is there any way to bind ng-model to multiple input fields uniquely inside a directive?

In my Project i Got a Issue like.I need to bind the user hobbies in the text field.if the user comes with a single hobby he can directly enter the hobby that he has. but when he had multiple then he had to click add multiple hobbies button.that working fine when i am displaying input fields dynamically using directives.but the issue is the value that coming from ng-model for that input field is binding to all input fields.
Here is my code.
Thanks in advance!
these are the images
this is how i am getting
this is what i need
In HTML
<div>
<div id="showHobbyfield"></div>
<input type="number" class="form-control" placeholder="ADD HOBBIES"
ng-click="addHoby()">
</div>
In controller
$scope.addHoby= function(){
var compiledeHTML = $compile("<div my-hobby></div>")($scope);
$("#showHobbyfield").append(compiledeHTML);
};
$scope.addUser = function(){
$scope.Users= [];
var obj = {
userhobby : $scope.user.morehobies
};
$scope.Users.push(obj);
menuStorage.put($scope.Users);
//menustorage is service to store user in localStorage.
In directive
'use strict';
angular.module('myApp')
.directive('myHobby', function() {
return {
scope : false,
templateUrl: 'views/my-hobby.html'
};
});
this is template: my-hobby.html
<div class="input-group">
<input type="text" ng-model="user.morehobies" class="form-control" placeceholder="type your hobbies here">
<div class="close-icon">
<span class="glyphicon glyphicon-remove" style="padding-left: 6px;"> </span>
</div>
</div>
For this i would suggest some other way if its ok with you.
If your hobbies is coming in array, like
user.morehobies = ['Reading', 'Writing']
or create array for storing hobbies.
then inside directive you can pass that object in directive.
I will use ng-repeat inside directive.
<div class="input-group" ng-repeat="h in hobies">
<input type="text" ng-model="h" class="form-control" placeceholder="type your hobbies here">
<div class="close-icon">
<span class="glyphicon glyphicon-remove" style="padding-left: 6px;"> </span>
</div>
</div>
so whenever user clicks on "Add hobbies" then we can add empty string in hobbies object in directive.
and whenever user clicks on remove you can remove that item from array.

textbox validation for number and required in repeating mode angular js

Please refer below link
https://plnkr.co/edit/9HbLMBUw0Q6mj7oyCahP?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.NDCarray = [{val: ''}];
$scope.NDCadd = function() {
$scope.NDCarray.unshift(
{val: ''}
);
};
$scope.data = angular.copy($scope.NDCarray);
$scope.NDCcancel=function(){debugger
$scope.NDCarray=$scope.data;
}
$scope.NDCdelete = function(index) {
if(index != $scope.NDCarray.length -1){
$scope.NDCarray.splice(index, 1);
}
};
});
It contains the textbox with add button. I have added validation for number and required field, it is working fine. but when i click add button it will create another textbox with entered value that time it showing the validation message for all the textboxes , i don't want to show validation message for all the textboxes. need to show validation for corresponding textbox only. that means when i enter something wrong in second textbox it is showing message to that textbox only.refer below screenshot.
validation message displaying for all textboxes.that should display for only one textbox.
Working plnkr : https://plnkr.co/edit/f4kAdZSIsxWECd0i8LDT?p=preview
Your problem is in your HTML, to get independant fields you must :
Move outside the form of the ng-repeat
Provide a dynamic name using $index on your fields, because name is what make each fields independant on the validation.
Here is the final HTML from the plnkr i didn't touch at all the javascript :
<body ng-controller="MainCtrl">
<form name="myForm">
<div ng-repeat ="ndc in NDCarray">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value"
min="0" max="99" name="{{'input_'+$index}}" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.input.$dirty && myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.number">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt><br/>
<tt>myForm['input_{{$index}}'].$valid = {{myForm['input_'+$index].$valid}}</tt><br/>
<tt>myForm['input_{{$index}}'].$error = {{myForm['input_'+$index].$error}}</tt><br/>
</div>
<div class="col-sm-4 type7 " style="font-size:14px;">
<div style="padding-top:20px; display:block">
<span class="red" id="delete" ng-class="{'disabled' : 'true'}" ng-click="NDCdelete($index)">Delete</span>
<span>Cancel </span>
<span id="addRow" style="cursor:pointer" ng-click="NDCadd()">Add </span>
</div>
</div>
</div>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</body>
Couple of changes:
If you add "track by $index" to your ng-repeat it will make each group of elements unique so that you don't have to worry about deriving unique names for elements.
Your validation on the number (myForm.ndcValue.$error.number) didn't work so I changed it to myForm.ndcValue.$error.max || myForm.ndcValue.$error.min
Also, you can throw an ng-form attribute directly on the div with your ng-repeat.
Like this:
<div ng-repeat="ndc in NDCarray track by $index" ng-form="myForm">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value" min="0" max="99" name="ndcValue" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.ndcValue.$dirty && myForm.ndcValue.$error.required">
Required!</span>
<span class="error" ng-show="myForm.ndcValue.$error.max || myForm.ndcValue.$error.min">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt>
<br/>
<tt>myForm.ndcValue.$valid = {{myForm.ndcValue.$valid}}</tt>
<br/>
<tt>myForm.ndcValue.$error = {{myForm.ndcValue.$error}}</tt>
<br/>
</div>
Here's the working plunker.
I changed the input element name from "input" to "ndcValue" to be less confusing.

Set ng-model value to button click

I have this little snippet of code to set the root.lowWeight and root.highWeight to my controller.
<input type="number" placeholder="Enter Low Range..." class="form-control input-xs">
<input type="number" placeholder="Enter High Range..." class="form-control input-xs">
<button class="btn btn-primary" ng-click="assignWeights()">Select Model</button>
Controller model:
//set the initial root scope to empty object
$scope.root = {};
//define root weight
$scope.root.lowWeight = 0;
$scope.root.highWeight = 0;
I know I could just use ng-model="root.lowWeight" to set the value, but ng-model triggers on element input event.
How can I either:
a) Send the input values in via assignWeights(param1, param2)
b) Change ng-model to trigger input on button click (seems hacky, less preferred solution)
I also know I could use JS/jQuery to trigger the input event and block the input event, but that's a last ditch effort I will 99.9% not do.
Solution using seperate scope object: plunkr
Html:
Low: <input type="text" name="lowRange" ng-model="inputData.lowWeight">
High: <input type="text" name="highRange" ng-model="inputData.highWeight">
Js:
$scope.assignWeights = function() {
$scope.lowWeight = $scope.inputData.lowWeight;
$scope.highWeight = $scope.inputData.highWeight
}
In your assignWeights function, you could use a selector to retrieve the values of the inputs and then assign your scope variable values based on that. Just assign a name to each input.
E.g.
<input type="number" placeholder="Enter Low Range..." class="form-control input-xs" name="lowRange">
<input type="number" placeholder="Enter High Range..." class="form-control input-xs" name="highRange">
<button class="btn btn-primary" ng-click="assignWeights()">Select Model</button>
Controller:
$scope.assignWeights = function() {
$scope.root.lowWeight = document.querySelector('input[name="lowRange"]').value
$scope.root.highWeight = document.querySelect('input[name="highRange"]').value
}
Plunker: http://plnkr.co/edit/lm8GFA0CD0zWwgAMSLah?p=preview

AngularJs how to create a list of same services

I have a Service who can add by a user. It can be one or more.
I have a button to add a Service
<button type="button" class="btn btn-success" ng-click="addService()">
<span class=" glyphicon glyphicon-plus">
</span>Add Service
</button>
When i click Add Service angular should creare a new Service in a list of services.
I have two textareas for Informations of the Service.
<label for="usr">Name:</label>
<input type="text" class="form-control" id="name"></br>
<label for="usr">Service:</label>
<input type="text" class="form-control" id="service"></br>
When i click on the Add Service Button a knew Service Button should be generated with this textareas.
How can generate that and add the new Service to a list of services?
$scope.services = [];
$scope.addService = function() {
var newService = {
name : 'a name',
service : 'a service'
};
$scope.services.push(newService);
}
and the HTML
<div ng-repeat="service in services"><label for="usr">Name:</label>
<input type="text" class="form-control" value="{service.name}"></br>
<label for="usr">Service:</label>
<input type="text" class="form-control" value="{service.service}"></br></div>
You would use ng-model which will take care of 2 way binding fields to scope object
<label for="usr">Name:</label>
<input ng-model="newService.name"></br>
<label for="usr">Service:</label>
<input ng-model="newService.service"></br>
Then in controller
$scope.addService = function() {
// copy and push newService object to array
$scope.services.push(angular.copy($scope.newService));
// reset newService object to clear fields
$scope.newService = {};
}
If you use a form for this you can use angular validation and move the ng-click to ng-submit on the form. ng-submit won't trigger if validation fails

Bootstrap modal dialog text fields don't update after I programmatically change the values until the user types in the field

I'm having the following problem: I have a list of items, any of which a user can click on to edit. At this time, a bootstrap modal dialog shows with fields for each of the editable values. I'm updating the values for the fields from an underlying Knockout viewmodel, so when the user edits an item on the modal dialog, they can see the field being modified in the background. So, the modifying currently works fine. However, when the dialog first opens, it doesn't have the values from the item the user selected; instead, it has the values that were loaded previously. But, when the user starts to edit a field, it instantly updates to what the value is supposed to be, and allows the user to continue editing the correct field. Not sure what's going on here.
Here is my modal:
<script id="myModal" type="text/html">
<div class="modal-header">
<button type="button" class="close" data-bind="click: close" aria-hidden="true">×</button>
<h3 data-bind="html: header"></h3>
</div>
<div class="modal-body">
<div class="form-group">
<label>First Name</label>
<input type="text" data-bind="value: modal.firstName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Last Name</label>
<input type="text" data-bind="value: modal.lastName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Phone</label>
<input type="text" data-bind="value: modal.phone, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Email</label>
<input type="text" data-bind="value: modal.email, valueUpdate: 'afterkeydown'" class="form-control" />
</div>
</div>
<div class="modal-footer">
</div>
</script>
<!-- Create a modal via custom binding -->
<div data-bind="bootstrapModal: modal" data-keyboard="false" data-backdrop="static"></div>
Here is the part where the list is populated via knockout:
<ul data-bind="foreach: tasks">
<li>
<div style="border:double">
<div>
<label data-bind="text: firstName"></label>
</div>
<div>
<label data-bind="text: lastName"></label>
</div>
<div>
<label data-bind="text: phone"></label>
</div>
<div>
<label data-bind="text: email"></label>
</div>
<div>
<button data-bind="click: editI.bind(this)">Edit</button>
#*<button data-bind="click: $parent.removeUser">Delete</button>*#
</div>
</div>
</li>
</ul>
<form #*data-bind="submit: addUser"*#>
<button type="submit">Add User</button>
</form>
<button #*data-bind="click: save"*#>Save</button>
Here is where I set the modal values, which works, when the knockout viewmodel is loaded:
viewModel.modal = {
header: ko.observable("This is a modal"),
firstName: ko.observable("a"),
lastName: ko.observable("a"),
phone: ko.observable("a"),
email: ko.observable("a"),
body: ko.observable("test body"),
closeLabel: "Close",
primaryLabel: "Do Something",
show: ko.observable(false), /* Set to true to show initially */
onClose: function () {
viewModel.onModalClose();
},
onAction: function () {
viewModel.onModalAction();
}
Finally, here is the edit function that gets called when the modal is opened. This is where things go awry. Item elements are set to the modal viewmodel, but aren't shown in the modal until a user starts editing that item...then, bam, the item shows in the field.
self.editI = function (item) {
viewModel.modal.body = item.email;
viewModel.modal.firstName = item.firstName;
viewModel.modal.lastName = item.lastName;
viewModel.modal.phone = item.phone;
viewModel.modal.email = item.email;
prevState = item;
viewModel.modal.show(true);
Note: I've found a couple of SO posts similar to this one:
how to destroy bootstrap modal window completely?
They haven't really helped though.
After a quick glance, what jumps out is that you don't use the correct syntax to assign the values to your observables. Try this:
self.editI = function (item) {
viewModel.modal.email(item.email()); // item.email() OR ko.unwrap(item.email) if unsure whether it is an observable you're receiving
viewModel.modal.firstName(item.firstName()); // item.firstName() OR ko.unwrap(item.firstName) if unsure whether it is an observable you're receiving
viewModel.modal.lastName(item.lastName()); // item.lastName() OR ko.unwrap(item.lastName) if unsure whether it is an observable you're receiving
viewModel.modal.phone(item.phone()); // item.phone() OR ko.unwrap(item.phone) if unsure whether it is an observable you're receiving
prevState = item;
viewModel.modal.show(true);
Edit: observables are basically function wrappers around your variable. By assigning their value with =, you remove the observable wrapper, because you re-assign the entire variable. By using the ()-syntax, you actually CALL the wrapper function and it will handle the UI update and assigning the new value to its inner variable.
Edit 2: you can make your code a little cleaner (at least in my opinion) by using chaining:
self.editI = function (item) {
prevState = item;
viewModel.modal
.email(item.email())
.firstName(item.firstName())
.lastName(item.lastName())
.phone(item.phone())
.show(true);
To wrap up:
In order to give an observable a new value and see the change in the ui, use ():
var x = ko.observable(3);
x(4); // UI will now reflect 4
To get the underlying value out of an observable, use () or ko.unwrap (works if the variable is observable and also if it isn't, which is often useful)
var x = ko.observable(3);
console.log(x()); // 3
console.log(ko.unwrap(x)); // 3
console.log(ko.unwrap(3)); // 3, not giving any errors
You needed to assign the value of one observable to another, so you combine both:
var x = ko.observable(3);
var y = ko.observable(4);
x(y()); // UI will reflect x = 4
x(ko.unwrap(y)); // UI will reflect x = 4
var z = 4;
x(ko.unwrap(z)); // 4
x(z()); // Error
Edit 3: live edit in a simple way (added because of comments below this answer). First, some updates to your HTML template (notice the with-binding).
<script id="myModal" type="text/html">
<div class="modal-header">
<button type="button" class="close" data-bind="click: close" aria-hidden="true">×</button>
<h3 data-bind="html: header"></h3>
</div>
<div class="modal-body">
<div class="form-group" data-bind="with: modal.item">
<label>First Name</label>
<input type="text" data-bind="value: firstName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Last Name</label>
<input type="text" data-bind="value: lastName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Phone</label>
<input type="text" data-bind="value: phone, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Email</label>
<input type="text" data-bind="value: email, valueUpdate: 'afterkeydown'" class="form-control" />
</div>
</div>
<div class="modal-footer">
</div>
</script>
Now what is this modal.item? It is basically the item you're editing. Not a copy, but the item itself! This will give you live-edit. However, we put the item inside the observable item that was created for this case and used in the with-binding. See what we do here? We created bindings in the modal template, in order not to break them we need to bind against an observable that will be filled with our item. This way we avoid your initial problem.
self.editI = function (item) {
viewModel.modal.item(item); // viewModel.modal.item = ko.observable(null);
prevState = ko.toJS(item); // Because we are now editing 'item' directly, prevState will change along if we simply set it to item. So instead, I used ko.toJS to make a 'flat' copy.
viewModel.modal.show(true);
A 'restore' to the previous state can be done by copying back the properties of prevState into item:
self.restoreChanges = function () {
var editingItem = viewModel.modal.item(); // Get the item we're editing out of the observable
if (editingItem && prevState) {
editingItem.email(prevState.email);
editingItem.firstName(prevState.firstName);
// Rest of properties
}
}

Categories