How to fill an AngularJS form loading data via XMLHttpRequest [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I have a form in an AngularJS controller, I would like to automatically fill the form with data collected with a XHTMLRequest.
Form has simply inputs with data-ng-model
page.html
<div id="PersonalAppID" data-ng-controller="PersonalAppCtrl as ctrl">
<form action="/" method="post" novalidate>
<div class="input-group">
<div class="form-group col-md-5">
<label>First name: </label>
<input name="fname" type="text" data-ng-model="ctrl.fname" placeholder="first name">
</div>
</div>
When controller init itself, it creates a request, downloads the data and put it into variables ctrl.variable
page.js
angular.controller('PersonalAppCtrl',function() { var ctrl = this;
ctrl.$onInit= function(){
var req=XMLHttpRequest();
req.onreadystatechange = function(){
if (req.status == 200&req.readyState==4){
var ret = convertJSON(req.responseText);
ctrl.fname = ret.FirstName;
return (true);
}
}
req.open();
req.send();
}
My problem is that input are filled only if user touch an input and then touch outside it.
I'd like to have form filled as soon as the page is loaded.
I also tried to use ng-load and ng-init but the behaviour is pretty the same in this case.
Any ideas?

Update your code like below:
angular.controller('PersonalAppCtrl',function() { var ctrl = this;
ctrl.$onInit= function(){
var req=XMLHttpRequest();
req.onreadystatechange = function(){
if (req.status == 200&req.readyState==4){
var ret = convertJSON(req.responseText);
$scope.$apply(function () {
ctrl.fname = ret.FirstName;
});
return (true);
}
}
req.open();
req.send();
}
You are making an XMLHTTPRequest which is a non angular code. Angular is not aware when the response is received so you need to make that angular aware by calling $scope.$apply()
Example:
function Ctrl($scope) {
$scope.message = "Waiting 2000ms for update";
setTimeout(function () {
$scope.message = "Timeout called!";
// AngularJS unaware of update to $scope
}, 2000);
}
But, if we wrap the code for that turn in $scope.$apply(), the change will be noticed, and the page is updated.
function Ctrl($scope) {
$scope.message = "Waiting 2000ms for update";
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
}
A very detailed explaination on this is given here - http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Its better to use Angular $http for all HTTP requests. So you dont have to bother about using $scope.$apply()

Related

Persisting form data when user navigates away from page in Angular

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);
}

Ng-model with Cookie

I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/

Submit form on page load in Angular

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();
});
}

Service to indicate some done criteria using Promise

We have several controllers in an application, for several different tabs/pages.
I want to have some mechanism to indicate that something is finished in one of
them for use in another controller. Promises should be this mechanism,
so I am trying to get a hang of it.
I have played around at http://jsfiddle.net/ExN6Q/ and gotten something that works like I want, but I am not super happy with the result for the services.
If I have the following html:
<div ng-app="myApp">
<div ng-controller=myController1>
Fill in this field: <input type="text">
<input type="submit" value="Done" ng-click="submit()">
</div>
<div ng-controller=myController2 ng-show="done1">
And this field: <input type="text">
<input type="submit" value="Done" ng-click="submit()">
</div>
<div ng-controller=myController3 ng-show="done2">
As well as this field: <input type="text">
<input type="submit" value="Done" ng-click="submit()">
</div>
</div>
and then the following controllers:
my_module.controller('myController1', function ($scope, Done1Service) {
$scope.submit = Done1Service.done;
});
my_module.controller('myController2', function ($scope, Done1Service, Done2Service) {
$scope.done1 = false;
$scope.submit = Done2Service.done;
Done1Service.get_promise().then(function () {
$scope.done1 = true;
});
});
my_module.controller('myController3', function ($scope, Done2Service, Done3Service) {
$scope.done2 = false;
$scope.submit = Done3Service.done;
Done2Service.get_promise().then(function () {
$scope.done2 = true;
});
Done3Service.get_promise().then(function () {
alert("Congratulations, you're done!");
});
});
then I am actually satisfied with the result, the "problem" is the implementation of
the services:
my_module.factory('Done1Service', function ($q) {
var deferred = $q.defer();
var get_promise_fn = function () {
return deferred.promise;
};
var done_fn = function () {
console.log("I'm done!");
return deferred.resolve(true);
};
return {
get_promise: get_promise_fn,
done: done_fn
};
});
my_module.factory('Done2Service', function ($q) {
... // identical except console.log("I'm done again!")
});
my_module.factory('Done3Service', function ($q) {
... // identical except console.log("I'm done at last!");
});
These feel a bit too boilerplatish, and I wonder if I am doing something wrong.
Could I create one common service and make three instances of it? Is this the normal way
to handle this by returning a promise from a dedicated get_promise function (
which I then assume would probably be called something else)?
As it is right now, your services are a one shot deal since promises cannot be reset. I think you could solve this problem by either using a single EventBus service through which every controllers communicates. E.g. The first controller sends a 'step1completed' message on the bus which is intercepted by the second controller, which does it's work and fires a 'step2completed' event, etc.
If you have many identical processes that have to run in parallel, you could allow creating multiple EventBus instances which would serve as independent communication channels for a specific set of objects.
Angular already allow you to emit or broadcast events through scopes, have a look at $emit and $broadcast.

Unobtrusive JavaScript ajax with mootools?

Unobtrusive JavaScript with jQuery is available in MVC3.But how can I use the unobtrusive Javascript ajax with mootools?
yeah, this is trivial to do. have a look at the recently released http://mootools.net/blog/2011/12/20/mootools-behavior/, I think it supports it.
I have used this approach in my Modal.BootStrap (view source on github, link's there) as well whereby it uses data attributes to fetch data from an ajax resource, it's not quite the same but it certainly is a start.
I just spent 10 mins making this and it's a good start:
http://jsfiddle.net/dimitar/zYLtQ/
(function() {
var ajaxify = this.ajaxify = new Class({
Implements: [Options,Events],
options: {
mask: "form[data-ajax=true]",
props: {
ajaxLoading: "data-ajax-loading",
ajaxMode: "data-ajax-mode",
ajaxUpdate: "data-ajax-update",
ajaxSuccessEvent: "data-event-success"
}
},
initialize: function(options) {
this.setOptions(options);
this.elements = document.getElements(this.options.mask);
this.attachEvents();
},
attachEvents: function() {
this.elements.each(function(form) {
var props = {};
Object.each(this.options.props, function(value, key) {
props[key] = form.get(value) || "";
});
form.store("props", props);
form.addEvent("submit", this.handleSubmit.bind(this));
}, this);
},
handleSubmit: function(e) {
e && e.stop && e.stop();
var form = e.target, props = form.retrieve("props"), self = this;
var updateTarget = document.getElement(props.ajaxUpdate);
new Request({
url: form.get("action"),
data: form,
onRequest: function() {
if (props.ajaxLoading) {
var loading = document.getElement(props.ajaxLoading);
if (loading && updateTarget) {
updateTarget.set("html", loading.get("html"));
}
}
},
onSuccess: function() {
if (!updateTarget)
return;
if(props.ajaxMode != 'append') {
updateTarget.set("html", this.response.text);
}
else {
updateTarget.adopt(new Element("div", { html: this.response.text }));
}
if (props.ajaxSuccessEvent)
self.fireEvent(props.ajaxSuccessEvent, this.response);
}
}).send();
}
});
})();
new ajaxify({
onContactFormSuccess: function(responseObj) {
console.log(responseObj.text);
alert("we are done.");
}
});
works with a DOM of:
<form action="/echo/html/" data-ajax="true" data-ajax-loading="#loading" data-ajax-mode="replace" data-ajax-update="#update" data-event-success="contactFormSuccess" method="post">
<input name="delay" value="4" type="hidden" />
<input name="html" value="Thanks for your submission, this is the jsfiddle testing response" type="hidden" />
<input name="name" placeholder="your name" />
<button>submit</button>
</form>
<div id="update">The update will go here.</div>
<div id="loading">loading...</div>
you should be able to build on that. on refactor i'd move the request events into their own methods and add some more proofing etc but it's fine. i don't know all mvc does but one thing that is missing is form validation events. i also added a custom event that is fired when done so your ajaxifier instance can do something particular to that form (see data-event-success="contactFormSuccess")
also, it can use default request options if not implicitly specified, even what request object to create - Request, Request.HTML, Request.JSON etc. Events like onRequest, spinners etc are also feasible... I think you just need to work your way through the options that mvc provides and build them to get started.
Confirm data-ajax-confirm
HttpMethod data-ajax-method
InsertionMode data-ajax-mode *
LoadingElementDuration data-ajax-loading-duration **
LoadingElementId data-ajax-loading
OnBegin data-ajax-begin
OnComplete data-ajax-complete
OnFailure data-ajax-failure
OnSuccess data-ajax-success
UpdateTargetId data-ajax-update
Url data-ajax-url

Categories

Resources