Angularjs difference between fetched and locally modified data - angularjs

I fetch data from an API and it gives my something like this:
{id: 1, name: ''}
The field name is ng-model for an input field.
As soon as I write something in the field, the json object I have a reference to, will have name equals to something else than empty.
That's normal behaviour.
However, if Im interested in knowing if name "was" empty when I fetched data, can I do this in a simple way? Without needing to create new variables?
Basically I want to have 2 ng-if elements and only display one of them. But I cannot trust the data in the object because I don't know if the name-field has been changed locally or on the server:
<input type="text" ng-model="ctrl.name" ng-if="ctrl.name === ''" />
<span ng-if="ctrl.name !== ''">{{ctrl.name}}</span>

Use can the $dirty (this docs are about form but it's true for inputs as well) prop of an input in form so the logic will be: show the input if the name is not defined (undefined or an empty string) or when the input is dirty - means the user typed in the input. And the opposite for the span. This way you'll not have to hold another param.
angular.module('app', []).controller('myController', function() {
// comment this line to simulate name was empty
// this.name = 'text from server';
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="myController as ctrl">
<form name="myForm">
<input type="text" name="name" ng-model="ctrl.name"
ng-if="!ctrl.name || myForm.name.$dirty" />
<span ng-if="!!ctrl.name && !myForm.name.$dirty">{{ctrl.name}}</span>
</form>
</div>
</div>

Related

Deletion Error with Email Validation in Angular

So I've recently taken over an Angular Giving Form Application. I am running validation on the email field using ng-pattern and displaying the errors on blur with ngMessages. The validation works great, however once the validation passes as $valid if the user decides they need to make a change in their email and begin to delete part of the first deletion deletes the last character of the email as expected, but the second deletion deletes the entire field forcing the user to start from scratch.
The regex for ng-pattern is being set in the controller scope with the variable $scope.emailre
The files are much to large to place here but here is the link to the site I am working on for my client.
https://epiqa.moodyglobal.org/corporate/
Snippet of Angular controller:
myApp.controller('mainCtrl', function($scope, localStorageService, $http) {
$scope.emailre = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
Snippet of HTML Form:
<div class="row form-group">
<div class="col-sm-6">
<div>
<label class="label" for="txt_donorEmail">E-mail:</label>
<input ng-class="{ 'submitted-error' : givingForm.email.$invalid && submitted }" ng-model="email" type="text" id="email" name="email" class="textbox required full form-control" maxlength="50" ng-pattern="emailre" required />
</div>
<div ng-messages="givingForm.email.$error" ng-if="givingForm.email.$touched || submitted">
<div class="errorText" ng-message="required">This field is required</div>
<div class="errorText" ng-message="pattern">Enter a valid email</div>
</div>
</div>
</div>
I have tried changing the input type from type="text" to type="email" but when doing that any time the user types two (.) periods the field gets immediately deleted.
Please help any ideas are very welcome.
The behavior is caused by this section
$scope.$watch('email', function(value){
localStorageService.set('email',value);
$scope.emailValue = localStorageService.get('email');
});
By Angular documentation
The default behaviour in ngModel is that the model value is set to undefined when the validation determines that the value is invalid. By setting the allowInvalid property to true, the model will still be updated even if the value is invalid.
I'm not sure whether you want to save the invalid email into localStorage, though. Maybe you can add a check only update when the value is valid.

AngularJS - unchanged data passed by ng-model is interpreted as undefined instead of the value

I'm using Angular to generate some inputs and populate them with data using ng-repeat. I also want to bind the data inside the input to a save changes button which takes parameters provided by ng-model directives. save changes button prints the passed arguments using the built-in JS arguments object. For some reason, unless I change the text inside the input box, the output is [undefined, undefined]. Once I change the text inside the input boxes, the correct output is printed. Why is that?
JSfiddle code.
HTML:
<div ng-app="myApp" ng-controller="MainCtrl">
<p ng-repeat = "man in men">
<label>name</label><input type="text" ng-model="mname" ng-value="man.name"><br>
<label>status</label><input type="text" ng-model="mstatus" ng-value="man.status"><br>
<button ng-click="save(mname,mstatus)">
save changes
</button>
</p>
</div>
JS:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function($scope) {
$scope.men = [{
name: "jon snow",
status: "depands"
}, {
name: "rob stark",
status: "dead"
}];
$scope.save = function() {
console.log(arguments);
}
});
This is not recommended but for your specific requirement you can use ng-init to bind ng-value to your model
<p ng-repeat = "man in men">
<label>name</label><input type="text" ng-model="mname" ng-value="man.name" ng-init="mname = man.name"><br>
<label>status</label><input type="text" ng-model="mstatus" ng-value="man.status" ng-init="mstatus = man.status"><br>
<button ng-click="save(mname,mstatus)">
save changes
</button>
</p>
This wouldn't bind your changes to the original model.
Fiddle
ngModel doesn't update untill you use a key to change it, or set it from your controller. Because you are setting the field of the input using ngValue, it doesn't register to your ngModel untill you change it.
This problem is similar to how most datepickers don't work with ngModel, as they set the field with DOM-manipulation and NOT by inserting the value by "key".
You can easily fix this by using the following HTML instead:
<label>name</label><input type="text" ng-model="man.name"><br>
<label>status</label><input type="text" ng-model="man.status"><br>
I simply removed the ngValue and linked the ngModel to your "man".

Form validation with modals in Angular

I have a form inside a modal pop up. I am trying to run form validation on the inputs after the user attempts to submit the form. So far, I'm struggling to make things work.
In my view, I have the following (sorry if there are any syntax errors, I'm converting this from jade on the fly):
<script type="text/ng-template", id="modalVideoNew">
<div class="ngdialog-message">
<form class="form-horizontal" ng-submit="submitForm()" novalidate name="newVideoForm">
...
<div class="form-group">
<label> Title </label>
<div class="col-sm-8">
<input type="text" name="title", required='', ng-model="newVideoForm.title">
<span class="text-danger" ng-show="validateInput('newVideoForm.title', 'required')"> This field is required</span>
</div>
</div>
</div>
</script>
And then in my controller, where I'm calling the ng-dialog pop up, I have this:
$scope.newVideo = function() {
ngDialog.openConfirm({
template: 'modalVideoNew',
className: 'ngdialog-theme-default',
scope: $scope
}).then(function() {
$scope.validateInput = function(name, type) {
var input = $scope.newVideoForm[name];
return (input.$dirty || $scope.submitted) && input.$error[type];
};
var newVideo = $scope.newVideoForm;
...
Right now, I am still able to submit the form, but once I open it back up I see the 'This field is required' error message. Also, the input is pre-filled with [object, Object] instead of an empty text input box.
A way of cleaning your model would work with using a model var that belongs to your parent controller and cleaning it in the callback. Check out how the template has attached your parent controller's var FormData.
Check this out
So about your validation, what I would recommend you is to have your own controller in it, no matter how much code it will have. It helps you keeping concepts of modularization and a better control over your scopes. This way will also facilitate a lot when validating.

angular ng-loop dynamic input and dynamic value to data processing

i am trying to create a dynamic form in angular. if type is text, the input will be <input type="text">. If type is checkbox, it will display <input type="checkbox">
here is the data successfully requested from server that i put in controller $scope
.controller('FormCtrl', function($scope){
$scope.form_data = [
{"id":1,"question":"Name","type":"text","user_criteria":"faiz","answer":[]},
{"id":5,"question":"Hair Type","type":"checkbox","user_criteria":"Long","answer":[{"id_answer":10,"id_survey":5,"answer_txt":"Long"},{"id_answer":11,"id_survey":5,"answer_txt":"Short"}]}
];
$scope.updated_form = {};
});
this is the template
<div ng-repeat="fm in form_data">
<div ng-if="fm.type =='text'">
<label>{{fm.question}}</label>
<input type="text" ng-model="{{fm.user_criteria}}">
</div>
<div ng-if="fm.type =='checkbox'">
<div ng-repeat="ans in fm.answer">
<input type="checkbox"> {{ans.answer_txt}}
</div>
</div>
</div>
for the input type text value. it appears label print Name and the input value print faiz. the code are displayed successfully to the correct input type and value.
but i have 2 question.
is there a way in the checkbox to check the value of the user_criteria to check the checkbox input?
how to pass all this data into the $scope.updated_form so i can use the value from the input like this ?
$scope.updated_form = [
{"id":1, "user_criteria": "faiz new name"},
{"id":5, "user_criteria": "Short"}
];
i am stuck to pass the form data for processing since i am lost when it comes to nested loop in the checkbox
I made a small sample to resolve your issues:
http://jsbin.com/pahuwe/3/edit?html,js,output
I used a initial fill function, to transfer the data to updated_form field:
$scope.updated_form = [];
$scope.form_data.forEach(function(item) {
$scope.updated_form.push(item);
});
and I used input radio for select, but with model and value parameters:
<input type="radio" ng-model="fm.user_criteria" ng-value="ans.id_answer"/>
Use a same ng-model to permit select a unique response.
My understanding is, you want to know what user checked and inputted , you can use another model to store the user data.(You need to init the updated_form with ids) And for the checkbox, you need to add a function with ng-change to set the checked answer into related data. It will be like
<div ng-repeat="fm in form_data">
<div ng-if="fm.type =='text'">
<label>{{fm.question}}</label>
<input type="text" ng-model="{{updated_form[fm.id].user_criteria}}">
</div>
<div ng-if="fm.type =='checkbox'">
<div ng-repeat="ans in fm.answer">
<input type="checkbox" ng-model="theChecked" ng-change=" if (theChecked) updated_form[fm.id].user_criteria = ans.answer_txt; "> {{ans.answer_txt}}
</div>
</div>

angularjs object changes affects other object

I have AngularJS application. From server I am getting a JSON. I am assigning to two objects. One I am using in my form page to edit. When the user press submit, I want to check whether the new value and old are same. But whenever I update the value in form and checks, the other object value also gets updated., resulting both object becomes same always.
Here is the JSFiddle Demo. Whenever I am trying to change the value, the other value also gets updated.
What is the solution to prevent the other object to not change?
HTML
<div ng-app>
<h2>Form</h2>
<div ng-controller="MainCtrl">
<form name="myForm" ng-submit="saveForm()">
<input type="text" ng-model="obj.name">
<input type="text" ng-model="obj.value">
<input class="btn-primary" type="submit" value="Save" ng-disabled="myForm.$pristine">
</form>
</div>
</div>
JS
function MainCtrl($scope) {
$scope.obj={
name:"N1",
value:"val"};
var oldObj = $scope.obj;
$scope.saveForm=function() {
alert(JSON.stringify($scope.obj));
alert(JSON.stringify(oldObj));
if($scope.obj===oldObj){
alert("same");
} else {
alert("not same");
}
}
}
The problem is, that you just store an reference to oldObj.
So oldObj and the $scope.obj is always the same.
You need to copy it.
Check the fiddle: http://jsfiddle.net/fc0jo1so/1/
I did it with fromJson, toJson
You just change this line
var oldObj = angular.copy($scope.obj);
Try it will work. & give alert not same
Try using jQuery to make a deep copy
var oldObj = jQuery.extend(true, {}, $scope.obj);

Resources