Accessing Restangularized Data - angularjs

I am using Restangular in my Angular app. I can successfully pull data from by backend. However, I am having difficulty getting it to update the data after submitting a form. The PUT method will work and will send the request to the API but the payload isn't correct. It is sending the original data. I can't seem to get the restangularized data to show up on my page.
I can make the response for my data available in the views by doing:
$scope.user = Restangular.one('user', 1234).get().$object;
The user information does not show up unless I put in $object.
If I just use:
$scope.user = Restangular.one('user', 1234).get();
My views don't display the user information, putting {{user}} will be {"restangularCollection":false,"reqParams":null,"parentResource":null}.
As I understand it, Restangular appends the different functions in order to enable the restful actions which is why Restangular.one('user', 1234).get().$object wouldn't be preferable.
I tried putting in setResponseExtractor() into my config per the documentation (https://github.com/mgonto/restangular#how-can-i-access-the-unrestangularized-element-as-well-as-the-restangularized-one) but it is overwritten by setResponseInterceptor() which I need to transform the response from getList() into an array.
Here is my controller:
props.controller('UserEditController', function($scope, $routeParams, $location, Restangular) {
var UserEdit = Restangular.one('users', $routeParams.userId).get();
$scope.user_edit = Restangular.copy(UserEdit);
console.log($scope.user_edit);
$scope.save = function() {
$scope.project.put().then(function() {
$location.path('/');
});
});
My form:
<h1>Edit Profile</h1>
<form ng-submit="save_user()">
<label for="first_name">First Name:
<input type="text" name="first_name" ng-model="user_edit.first_name" ng-value="user_edit.first_name" /><br />
<label for="last_name">Last Name:
<input type="text" name="last_name" ng-model="user_edit.last_name" ng-value="user_edit.last_name"/><br />
<label for="email">Email:
<input type="text" name="email" ng-model="user_edit.email" ng-value="user_edit.email"/><br />
<label for="position">Position:
<input type="text" name="position" ng-model="user_edit.position" ng-value="user_edit.position"/><br />
<button type="submit">Save</button>
Cancel
</form>
I've looked over the Restangular example on Plunker (http://plnkr.co/edit/d6yDka) and it got me aways but I can't seem to figure this out. It is driving me crazy. Can someone help?

This only addresses the .$object portion of the question.
Restangular does everything with promises. Before the response data can be accessed, the promise has to be fulfilled/resolved.
// Assign data to $scope once response is returned.
Restangular.one('users', $routeParams.userId).get().then(function(user){
$scope.user_edit = user;
});
Then putting {{user_edit}} in your view should display the user data.

Related

angular scope initialization sometimes empty

I'm working with angular 1.5 and a python/mongodb api.
The api is working fine (consistent and fast), but sometimes when I load a page with a form the data fields are empty.
The app uses ui.router to associate the chap state with the correct url and the chapController. I type that url into the address bar and hit return to load the page. In the controller, the path/to/api returns json data.
Here's a simplified version of the controller:
angular.module('configurer')
.controller('chapController', function($scope, $http, $state) {
var url='path/to/api';
$http.get(url).success(function(data){
$scope.data = data;
});
$scope.save = function()( {
$http.post(url, $scope.data).success(function() {
$state.go('home', {reload:true});
});
});
The view looks like this:
<button class="btn btn-primary" ng-click="save()">Save Changes</button>
<form>
<div class="row">
<div class="col-sm-3 form-group">
<label for="name">Name</label>
<input class="form-control" id="name" type="text" name="name" ng-model="data.chap.name" />
</div>
<div class="col-sm-9 form-group">
<label for="title">Title</label>
<input class="form-control" id="title" type="text" title="title" ng-model="data.chap.title" />
</div>
</div>
</form>
Usually the form comes up populated with data but sometimes the fields are blank (using the same url).
I can reload the page from the browser and then it will populate, but of course that's not a good user experience.
What am I doing wrong?
After research plus trial-and-error, I think I've got the answer. The reason the scope wasn't binding to the data is that there was no data: my server was sending a 304 "not modified" response.
So of course it couldn't bind with non-existent data but the http response was still counted as success. Seems like this would be a common 'gotcha'.
What worked for me is to add a config object to each http.get call, like this
http.get(url, {cache:true}).success(function(data) { etc...
My guess is that you could also set max-age and public on the http request headers so the server cannot respond with a 304. That didn't seem like the most efficient thing to do, so I went with using cache on the client so the server isn't even bothered.
This is working for me, but if problems pop up again, I'll repost.
Since you're using ui.router, I recommend using resolve for this.
Example:
$stateProvider
.state('chap', {
// ...
resolve: {
data: function($http){
return $http.get(...);
}
}
});
and in your controller:
angular
.module('configurer')
.controller('chapController', function(data, $scope, $http, $state) {
$scope.data = data;
}
Sometimes data won't bind with the scope. can you check by including this line
$scope.data = data;
$scope.$apply();
Inside success handler.
I am not sure, it may work. please try

angular submitting only form data not whole model

I'm submitting form data to my API but my model contains my whole object. I should really only submit the updates, correct?
So,
my item is retrieved by Id, which I don't need to show to the user (hidden field).
I'll show the user the title of item so they know what they're editing, but I won't make it editable (read-only).
I'll expose description so the user can edit it and save.
(Imagine a larger form - a half dozen more read-only fields and a half dozen more editable fields.)
Correct me if I'm wrong but, when sending to my API, I should not be sending the entire item object. The db already has title, why put it in the payload if its just going to be thrown away?
So, I should really only be sending the values of the editable fields.
I'm trying to figure out the proper way of separating the model from the form data so I submit the latter, not the former. If this is not best practice, please correct me.
Controller:
.controller('editItemController', ['$stateParams',
function($stateParams) {
var vm = this;
vm.getItem = function () {
$http.get('/api/Items/' + $stateParams.id).then(function (response) {
vm.item = response.data;
});
};
vm.saveChanges = function () {
vm.submitted = true;
if (vm.detailsForm.$valid) {
$http.put('/api/Items/' + $stateParams.id, vm.item).then(function (response) {
});
}
};
vm.getItem();
}
View:
<form name="itemVm.detailsForm" ng-controllerAs="itemVm">
<input type="hidden" name="Id" ng-model="itemVm.item.Id" />
<div class="form-group">
<label for="title" class="control-label col-xs-3">Title:</label>
<div class="col-xs-9">
<input type="text" class="form-control" readonly="readonly" id="title" value="{{ itemVm.item.Title }}">
</div>
</div>
<div class="form-group">
<label for="description" class="control-label col-xs-3">Description:</label>
<div class="col-xs-9">
<input type="text" class="form-control" name="description" ng-model="itemVm.item.Description" required>
</div>
</div>
<button ng-click="itemVm.saveChanges()">Save Changes</button>
</form>
For a standard REST API, you should be sending the entire object. The entire idea behind REST is that you transfer representations of the state of an object, hence representational state transfer. As such, it should be a complete representation, not a portion.
There are multiple ways to circumvent this if you'd really like, and most have to do with the back end, not the front end.
Either create an ad hoc endpoint to take in the one parameter and update the persistent object accordingly. (Not recommended).
If you want to send partial data (which I think is a good idea some times depending on the size of the object), you should handle that on the back end accordingly.
See here.

Upload files with Angular with a post containing other form data

I have followed countless examples (from here and other sites) that explain how you upload files from Angular to a web server. I am happy with the solution of using angular-file-upload and processing the data on the server (Node) with Multer.
What I haven't been able to find is a way to upload files from the form with a post that contains all the other controller data.
controller:
$scope.files = [];
$scope.name = "";
$scope.post = //$http post to server from service
view:
<input type="text" ng-model="name">
<input type="file">
<button ng-click="post()">Send post without page refresh</button>
Is there a way I can send the [name] and the [files] in the same post? If I send with multi part data will that be ok for [name] and [files]? Do I need to send two separate posts?
At the moment, my working example submits with a form action of 'post' and an enctype of "multipart/form-data". But I don't want the page to refresh and I want to send [name] and [files] from the scope... do I need to attach the files from the form to the scope or get the scope to pull the files from the DOM?
You can push formData to file before upload.
$scope.uploader.onBeforeUploadItem = function(fileItem) {
fileItem.formData.push({name: $scope.name});
};
Please look through the demo for complete solution :)
Plunkr
<form>
<input type="text" placeholder="Enter Name here" ng-model="newCountry.Name" />
<label>Choose 1 or more files:</label>
<input type="file" ng-files="AddnewCountryFlag($files)" multiple />
<h3>Try Uploading Image file. It will preview your image.</h3>
<div ng-repeat="item in imagesrc">
<img src="{{item.Src}}" />
<label>{{item.Size}}kB</label>
</div>
<input type="submit" value="Send Data" ng-click="AddCountry()" />

Have AngularJS perform logic on form inputs when form submits

I currently have a form like the following:
<form autocomplete="on" enctype="multipart/form-data" accept-charset="UTF-8" method="POST" action="{{trustSrc(SUBMIT_URL)}}">
<input type="text" name="firstinput" ng-model="first"/>
<input type="text" name="secondinput" ng-model="second"/>
<input type='submit'/>
</form>
And a controller like so:
$scope.first = "first";
$scope.second = "second";
$scope.SUBMIT_URL = DATABASE_URL + "forms/submit/";
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
};
And when I submit this form as is, it works just fine with the backend. However I now need to use ng-submit in place of the standard HTML form submit because I need to run a find and replace in $scope.first. The backend expects the data to be posted exactly in the way the original HTML form does. How do I use $http or $resource to post in the exact same format as the HTML form?
It is not very clear what you are trying to accomplish. But, you should tidy up your code a bit, to make the implementation easier:
Don't use different variables for each input's field. Instead, use an object with different keys.
Use ng-click (or ng-submit) for your submission. Action will go inside your JS logic.
Use the novalidate attribute so that Angular can properly format, and validate your form on its own (and you don't get confusing cross-browser effects).
With those in mind, your new markup would be:
<form autocomplete="on" enctype="multipart/form-data" accept-charset="UTF-8" novalidate>
<input type="text" name="first" ng-model="form.first" />
<input type="text" name="second" ng-model="form.second" />
<button ng-click="submit">Submit</button>
</form>
Your JS directive is then:
app.directive('form', function($http)
{
return function(scope, element, attrs)
{
// Leave this empty if you don't have an initial data set
scope.form = {
first : 'First Thing To Write',
second : 'Second item'
}
// Now submission is called
scope.submit = function()
{
// You have access to the variable scope.form
// This contains ALL of your form and is in the right format
// to be sent to an backend API call; it will be a JSON format
// Use $http or $resource to make your request now:
$http.post('/api/call', scope.form)
.success(function(response)
{
// Submission successful. Post-process the response
})
}
}
});

AngularJS and nested forms: Correct way of naming and declaring model

Ok, so I am creating a form like so:
<form novalidate class="simple-form" action="" name="mainForm" ng-submit="doSubmit()" method="POST">
<div ng-form name="giftForm" class="panel-body">
<input type="text"
id="x_amount"
name="x_amount"
class="form-control input-lg"
ng-model="giftForm.amount"
ng-required="true">
</div>
<div class="row">
<button type="submit" class="btn" ng-disabled="mainForm.$invalid">Submit</button>
</div>
</form>
This works for validation, i.e. that mainForm.$invalid only highlights enables the button after the input has text. However, using batarang, I noticed that the scope looks like so:
{"giftForm":{"x_amount":{},"amount":"a"}}
So it is creating model values based on the name and the declared ng-model. If I change them to be the same like so:
<input type="text"
id="x_amount"
name="x_amount"
class="form-control input-lg"
ng-model="giftForm.x_amount"
ng-required="true">
The submit shows the correct scope of:
{"giftForm":{"x_amount":"a"}}
But the input field initially shows with [Object object] in the input, which makes me think I am confusing something here..... I can't have that in all of the input fields.
I'd like the name and the model to be the same. That would seem to be the progressive enhancement way and would allow a normal non-ajax post by simply removing the ng-submit method and the ajax way would look like:
$http({
method : 'POST',
url : 'formAction.do',
data : $.param(angular.toJson($scope.mainForm)),
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
})
.success(function(data) {
//success
})
.error(function(data, status, headers, config) {
//error
});
Anybody has insight into what I am missing or if my architecture is flawed from the ground-up, I'd appreciate the wisdom....
I'd like the name and the model to be the same.
You could do that, but you'd have to have a separate scope, and therefor a separate controller for your form.
More importantly, though, this isn't going to buy you anything. The input name attributes are primarily used for validation display, and not much else.
Your use of $http concerns me more. It looks like you're thinking in a JQuery mindset. I'd challenge you to throw JQuery out the window for a while until you get used to Angular.
Here's what people generally do with forms (From the model structure to the naming and validation):
View:
<form name="myForm" ng-submit="sendFoo()">
<div>
<label for="name">name</label>
<input type="text" id="name" name="name" ng-model="foo.name" required/>
<span ng-show="myForm.name.$error.required">required</span>
</div>
<div>
<label for="email">email</label>
<input type="email" id="email" name="email" ng-model="foo.email" required/>
<span ng-show="myForm.email.$error.required">required</span>
<span ng-show="myForm.email.$error.email">invalid email</span>
</div>
<button type="submit" ng-disabled="myForm.$invalid">Submit</div>
</form>
Controller:
app.controller('MyCtrl', function($scope, $http) {
$scope.foo = {
name: 'Someone Special',
email: 'test#monkey.com'
};
$scope.sendFoo = function (){
$http.post('/Some/Url/Here', $scope.foo)
.then(function(result) {
$scope.result = result.data;
});
});
});
You'll notice that the name of the form and the names of the inputs are only used for validation on those <span> tags. Like so: <span ng-show="[formName].[fieldName].$error.[validationName]">invalid message</span>. That object is available on the $scope at $scope.formName, but there usually isn't a reason to access it directly in your controller.
I hope this helps you (or someone).
Naming a form places a variable in scope with that name. Under it, it puts properties named after the form fields names. However, you already have a model in the scope with the same name as the form: giftForm. This causes confusion: the template overwrites the model and/or vice versa.
So, give another name to one of them, eg name the model giftModel.

Resources