Persisting form data when user navigates away from page in Angular - javascript

I would like to persist the data entered in a form so that the information entered will still display in the respective fields if the user clicks the back button and then subsequently returns to the form. I've tried using this Stack Overflow answer as a model, but am not having any luck: https://stackoverflow.com/a/16806510/640508
I'm using the Controller As syntax.
Here's my adapted code:
Controller:
angular.module('myApp')
.controller('ContactFormCtrl', ['formMemory', '$http', function (formMemory, $http) {
var contactForm = this;
contactForm.contact=formMemory;
formMemory.set();
formMemory.get();
// . . .
}]);
Service:
angular.module('formMemory.fact', [])
.factory('formMemory', function () {
var contact = {};
return {
get: function () {
return contact;
},
set: function (value) {
contact = value;
},
reset: function () {
contact = {};
}
};
HTML:
<h1><small>ContactInformation</small></h1>
<form name="myForm" novalidate >
<div class="row">
<div class="col-sm-4 form-group">
<label class="control-label" for="first-name">First Name</label>
<input type="text" id="first-name" name="firstName" ng-model="contactForm.contact.firstName"
placeholder="First Name" class="form-control">
</div>
// . . .
app.js:
angular.module('myApp', [
'formMemory.fact',
//. . .
]);

The factory formMemory returns an anonymous object, with 3 functions attached. You aren't using the correct syntax for accessing these functions.
To access the saved data, you would want to set your controller variable to the return value of the get() function, like so:
contactForm.contact = formMemory.get();
and to save the data if you navigate away, you should be passing the contact in as a parameter to the set(value); most likely, you would do this in the $routeChangeStart.
$scope.$on('$routeChangeStart', function() {
//we are leaving the page, so let's save any data we have
formMemory.set(contactForm.contact);
}

Related

Angular directive ng-model working for arrays but not string

The ng-model directive seems to be lacking a reference to the actual object within the JavaScript, but only for string values. Using the list of dictionary objects and looping over the elements with ng-repeat as shown below though, it works.
I can only think that it may be due to returning the array acts like returning a reference to the object, whereas returning the string variable is simply returning the literal string, neutralizing the Angular's ability to do it's two-way data binding and leaving me with a variable that still holds a value of undefined.
Why is my service module below unable to pull the updated value from the view for the variable gitRelease?
In a service module I have this functionality:
(function () { //start iife
'use strict';
angular.module('gms.autoDeploy')
.factory('AutoDeployService', ["$http", "$q", "$log", "$cookies", "APP_CONFIGS", "SweetAlert", "$timeout", "GridSettingsService", "APP_USER", AutoDeployService]);
function AutoDeployService($http, $q, $log, $cookies, APP_CONFIGS, $timeout, SweetAlert, GridSettingsService, APP_USER) {
var tibcoCopyJobs = [];
var gitRelease = "";
function addNewTibcoCopyJob() {
tibcoCopyJobs.push({
sourceServer: "",
sourcePath: "",
destinationServer: "",
destinationPath: ""
});
}
function getTibcoCopyJobs() { return tibcoCopyJobs; }
function getGitRelease(){ return gitRelease; }
function extractFormData() {
console.log(gitRelease);
for (var i = 0; i < tibcoCopyJobs.length; i++) {
console.log(tibcoCopyJobs[i]);
}
}
return {
addNewTibcoCopyJob: addNewTibcoCopyJob,
getTibcoCopyJobs: getTibcoCopyJobs,
getGitRelease: getGitRelease,
extractFormData: extractFormData
};
} //end AutoDeployService
}()); //end iife
Using it with this controller:
angular.module("gms.autoDeploy").controller('AutoDeployController', ['$scope', '$compile', 'AutoDeployService',
function ($scope, $compile, AutoDeployService) {
var model = this;
init();
function init() {
model.tibcoCopyJobs = AutoDeployService.getTibcoCopyJobs();
model.gitRelease = AutoDeployService.getGitRelease();
}
function btn_addNewTibcoCopy() { AutoDeployService.addNewTibcoCopyJob(); }
function btn_extractFormData() { AutoDeployService.extractFormData(); }
model.btn_addNewTibcoCopy = btn_addNewTibcoCopy;
model.btn_extractFormData = btn_extractFormData;
}
]);
To give functionality to this view:
<div ng-controller="AutoDeployController as autoDeploy">
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" ng-model="autoDeploy.gitRelease" placeholder="MM-DD-YYYY">
</div>
</div>
<div class="row">
<fieldset class="col-md-2" style="margin-bottom: 10px" ng-repeat="item in autoDeploy.tibcoCopyJobs track by $index">
<legend>Copy</legend>
<input type="text" class="form-control" placeholder="Source Server..." ng-model="item.sourceServer">
<br/>
<input type="text" class="form-control" placeholder="Source Path..." ng-model="item.sourcePath">
<br/>
<input type="text" class="form-control" placeholder="Destination Server..." ng-model="item.destinationServer">
<br/>
<input type="text" class="form-control" placeholder="Destination Path..." ng-model="item.destinationPath">
</fieldset>
</div>
<button ng-click="autoDeploy.btn_extractFormData()">extract</button>
<button ng-click="autoDeploy.btn_addNewTibcoCopy()">TIBCO copy</button>
</div>
</div>
I think you have explained why in your question. Array is returned by reference, whereas string is just copied by value. But I will try to make it a bit more clear.
When you do
model.gitRelease = AutoDeployService.getGitRelease();
the model object will create property getRelease like this:
{getRelease: "", ... (more properties from the ctrl)}
so whatever you update in the view it will just update the getRelease in the controller.
One possible fix is like what Jags mentioned in the comment.
Or you can make a reference to your service in the ctrl
var model = this;
model.autoDeployService = AutoDeployService;
In your view
<input type="text" class="form-control" ng-model="autoDeploy.autoDeployService.gitRelease" placeholder="MM-DD-YYYY">
that should work.

validating data in database in angularjs

I am using angularjs in my project. In a process/module, the form will not be submitted if a certain data input is already exist in the database. For example, this process: registering /Signing up. If the user inputted a username that's already been used by someone, the form will not be submitted. And it will be checked in a controller where the list of usernames has been loaded prior to user entering the data by comparing (in a for loop). My question is, is this a good way of checking the data or do I have to use $http?
Edit:
<div class="col-xs-6 col-sm-6 col-md-6">
<div class="form-group"><label class="control-label">Username</label>
<input type="text" ng-model="reg.username" usernameAvailable
name="username" class="form-control input-md"
placeholder="Username" required />
<div ng-if="regForm.$pending.usernameExists">checking....</div>
<div ng-if="regForm.$error.usernameExists">username exists
already
</div>
</div>
</div>
mainApp.directive('usernameAvailable', function($timeout, $q, $http) {
return {
restrict: 'AE',
require: 'ngModel',
link: function(scope, elm, attr, model) {
model.$asyncValidators.usernameExists = function() {
//here you should access the backend, to check if username exists
//and return a promise
// var defer = $q.defer();
// $timeout(function(){
// model.$setValidity('usernameExists', true);
// defer.resolve;
// }, 1000);
// return defer.promise;
console.log(model);
return $http.get(BASE_URL+'Register/validate?u='+username).
then(function(res){+$timeout(function(){
model.$setValidity('usernameExists', !!res.data);
}, 1000);
});
};
}
}
});
Php controller:
public function validate(){
$this->load->model('account_model');
$data =$this->account_model->exist($this->input->get('u'));
echo json_encode($data);
}
You should take the data to the server. And there, should fire a query in db to check if this data already exists. If it does then show error message on UI and do not save other wise save it with success message.
Fire query (sample) Something like :
SELECT * FROM users WHERE username='myName'
If records are more than 0, then what you have is a repeated value.
Do not fetch all the records on UI and then loop through them.
Think of :
What if there are 1 Million or more records?
Security ? (You are getting all the user names to client)
And other such things.
Hope it guides you.

Strange binding permanence between controllers

I've got a project in which you write a note in a formulary. Then, you submit that note into an information container (now it's just an array for testing purposes, but it's intended to be a DB later).
The formulary has the following controller:
app.controller('controlFormulario', ['$scope', 'SubmitService', function($scope, submitService) {
$scope.formData = {
"titulo":"",
"texto":"",
"fecha": new Date()
};
$scope.submit = function() {
var temp = $scope.formData;
submitService.prepForBroadcast(temp);
}
// more things we don't need now
... which is bound to this part of the DOM, which is added into it, via a directive:
<form ng-controller="controlFormulario as formCtrl">
<div class="element">
<div class="form-group" ng-class="{'has-error': formData.titulo.length > 50 }">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo" ng-model="formData.titulo">
<span ng-show="formData.titulo.length > 50" id="helpBlock" class="help-block">El título no puede exceder los 50 caracteres.</span>
</div>
<div class="form-group">
<label for="inputTexto">Texto</label>
<textarea class="form-control" id="inputTexto" ng-model="formData.texto"></textarea>
</div>
<div class="form-group">
<label for="fecha">Fecha</label>
<input type="fecha" class="form-control" id="fecha" ng-model="formData.fecha" disabled>
</div>
<div class="form-group" >
<button class="btn btn-primary" style="height:35px;width:100px;float:right;" id="submit"
ng-disabled="isDisabled()" ng-click="submit()">
Enviar
</button>
</div>
</div>
<div class="note" ng-show="formData.titulo.length > 0">
<div class="title" ng-model="formData.titulo" class="title">{{formData.titulo | limitTo:50}}</div>
<div class="text" ng-model="formData.texto" class="text">{{formData.texto}}</div>
<div class="date" ng-model="formData.fecha" class="date">{{formData.fecha | date}}</div>
</div>
</form>
This is my directive (I don't think it's really needed, but just in case):
app.directive('formulario', [function() {
return {
restrict: 'E', // C: class, E: element, M: comments, A: attributes
templateUrl: 'modules/formulario.html',
};
}]);
I use a service for passing the data between the previous controller, and the note controller (which controls the note objects of the array). This is the service:
app.factory('SubmitService', function($rootScope) {
var data = {};
data.prepForBroadcast = function(recvData) {
data.data = recvData;
this.broadcastItem();
};
data.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return data;
});
... and I receive it in this part of my note controller:
app.controller('noteController', ['$scope', 'SubmitService', function($scope, submitService) {
var nc = this;
$scope.$on('handleBroadcast', function() {
nc.pruebaNota.push(submitService.data);
$scope.formData.titulo = "";
$scope.formData.texto= "";
$scope.formData.fecha = new Date();
});
// more things, the array, etc...
Ok. This should work, and it does, but something strange happens: as you can see, the preview note is binded with ng-model to the form. That works great, ok. But when I submit the form, the new note object keeps bound to the form (so if I delete the form text, the note appears in blank, and if I write something, it gets automatically updated both in the preview note, and the new note), when there isn't any relation between them. The new note, which appears dynamically on the screen, shouldn't be bound to anything.
Am I doing something wrong? Some help would be really nice!
You are forgetting something really important. Memory address. So, the rought idea is something like: imagine that $scope.formData is in the address 123123. You first create a temp var pointing to 123123 then you send it to the service and the service holds the same address 123123 into data.data.
Then in your second controller you say: hey, I want to work with that data.data (AKA your data in 123123) you have SubmitService.
Now when you modify $scope.formData again, you are updating what you have in that 123123 and everything that is "looking" into that address will be updated.
That is the rough idea. To point it simple, you're modifying the same piece of information everywhere.
See it here: http://plnkr.co/edit/zcEDQLHFWxYg4D7FqlmP?p=preview
As a AWolf suggested, to fix this issue, you can use angular.copy like this:
nc.pruebaNota.push(angular.copy(submitService.data));

Angular two-way binding

I have an Angular directive, which reads in the contents of a file <input> and populates a form. Currently, it seems to update the $scope variables within the controller, but the form elements bound to those $scope variables are not being updated in the view. Why is this?
The code snippets below are as follows:
my directive, which i use to read in from a file <input> and call the controller's $scope['modify_form'], which allows me to update the $scope variables corresponding to my form.
the HTML (view-side) for my directive
my controller logic for $scope['modify_form'], which calls a bunch of helper functions, one of which is shown below.
a snippet of the HTML of my view for my form; this is where the fundamental problem lies, since it does not get updated when i call $scope['modify_form']
Here is my directive, for reading in content:
app.directive('modifyBtn', function($compile) {
return {
restrict: 'E',
scope: {
modifyJSON:'&modifyJson',
},
link: function(scope, element, attrs) {
var fileElem = angular.element(element.find("#file_input2"));
console.log(fileElem);
var showUpload = function() {
// display file input element
var file = fileElem[0].files[0];
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(e) {
var uploadedJSON;
try {
uploadedJSON = JSON.parse(reader.result);
} catch(e) {
// should display help block (or warning)
return;
}
console.log(uploadedJSON); // #debug
for (var key in uploadedJSON) { // only one key
if (uploadedJSON.hasOwnProperty(key)) {
sessionStorage[MODEL] = key;
sessionStorage[NAMESPACE] = uploadedJSON[key][NAMESPACE]
var fields = [FIELDS, LINKS, PRIMARY_KEY, TABLE, SQL, VALIDATIONS];
fields.forEach(function(field) {
if (uploadedJSON[key].hasOwnProperty(field)) {
sessionStorage[field] = JSON.stringify(uploadedJSON[key][field]);
}
});
// trigger modification without reloading page
scope.modifyJSON();
}
}
}
};
$(fileElem).on('change', showUpload);
}
}
});
My view (HTML), for the directive is as follows:
<modify-btn modify-json="modifyForm()">
<li class="active">
<span class="btn creation-btn" style="background-color: #d9534f;" ng-click="fileshow = true">
Upload JSON
</span>
</li>
<li><input type="file" id="file_input2" ng-show="fileshow" /></li>
</modify-btn>
In my controller, here is where I update the $scope variables bound to the form:
$scope['modifyForm'] = function() { // filling in elements if reading in JSON
modify_data();
modify_fields();
modify_links();
modify_pkeys();
modify_table();
modify_sql();
modify_validations();
sessionStorage.clear();
}
function modify_data() {
var overall = [MODEL, NAMESPACE];
overall.forEach(function(elem) {
if (exist(sessionStorage[elem])) {
$scope['data'][elem] = sessionStorage[elem];
}
});
}
And, here, in the view is how my form elements are bound.
<div class="form-group">
<label class="col-sm-4">{{myConstants["model"]}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" ng-model="data[myConstants['model']]" id="model" />
</div>
</div>
<div class="form-group">
<label class="col-sm-4">{{myConstants["namespace"]}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" ng-model="data[myConstants['namespace']]" id="namespace" />
</div>
</div>

Clear data in angular form

I have the following angular form:
<form name="client_form" id="client_form" role="form">
<div class="bb-entry">
<label for="firstname">First Name:*</label>
<input type="text" name="firstname" ng-model="client.first_name" required class="form-control"/>
</div>
<div class="bb-entry">
<label for="lasttname">Last Name:*</label>
<input type="text" name="lastname" ng-model="client.last_name" required class="form-control"/>
</div>
<div class="bb-entry">
<label for="email">E-mail:*</label>
<input type="email" name="email" ng-model="client.email" required class="form-control"/>
</div>
</form>
<button type="button" ng-click="resetForm(client_form)">Clear</button>
I would like to add behaviour so that when users select 'Clear', all form data is cleared. I've written this method at present:
resetForm: (form) ->
form.submitted = false
form.$setPristine()
angular.copy({}, client)
However, this clears the entire client object, when really, I only want to clear the attributes referenced in my form.
I realise I can iterate around each attribute of the form object, which gives me access to the ngModelController instances as such:
resetForm: (form,) ->
form.submitted = false
form.$setPristine()
angular.forEach form, (value, key) ->
if value.hasOwnProperty("$modelValue")
# set model value here?
But can I actually assign the model value here or would a different approach be better?
I think you need to copy the client first, then clear the new client object.
Here is a fiddle link that does something very similar: http://jsfiddle.net/V44fQ/
$scope.editClient = function(client) {
$scope.edit_client = angular.copy(client);
}
$scope.cancelEdit = function() {
$scope.edit_client = {};
};
<form name="client_form" id="client_form" role="form">
<div class="bb-entry">
<label for="firstname">First Name:*</label>
<input type="text" name="firstname" ng-model="edit_client.first_name" required class="form-control">
</div>
...
<button type="button" ng-click="cancelEdit()">Clear</button>
</form>
I solved the problem by writing two directives, one that is attached to the form and the other to each individual input that I want to be 'resettable'. The directive attached to the form then adds a resetForm() method to the parent controller:
# Adds field clearing behaviour to forms.
app.directive 'bbFormResettable', ($parse) ->
restrict: 'A'
controller: ($scope, $element, $attrs) ->
$scope.inputs = []
$scope.resetForm = () ->
for input in $scope.inputs
input.getter.assign($scope, null)
input.controller.$setPristine()
registerInput: (input, ctrl) ->
getter = $parse input
$scope.inputs.push({getter: getter, controller: ctrl})
# Registers inputs with the bbFormResettable controller allowing them to be cleared
app.directive 'bbResettable', () ->
restrict: 'A',
require: ['ngModel', '^bbFormResettable'],
link: (scope, element, attrs, ctrls) ->
ngModelCtrl = ctrls[0]
formResettableCtrl = ctrls[1]
formResettableCtrl.registerInput(attrs.ngModel, ngModelCtrl)
Probably overkill, but this solution ensures only the attributes specified are cleared, not the entire client object.
No need to complicate things. Just clean the scope variables in your controller.
Plain JS:
$scope.resetForm = function() {
$scope.client.first_name = '';
$scope.client.last_name = '';
$scope.client.email = '';
}
If you wish, you can parse the $scope.client object and set properties to false (if the form is dynamic, for example).
Here is a simple example: http://jsfiddle.net/DLL3W/

Categories

Resources