AngularJs change location after save - angularjs

I'm having trouble changing location after I save a new customer in an angular.
Here's my controller.
function CustomersNewController($scope, $location, Customer) {
$scope.selected_customer = '';
$scope.submit = function() {
rails_customer = new Customer({customer: $scope.new_customer});
rails_customer.$save({format: 'json'},function(data, getResponseHeaders) {
$location.path(data.id + '/edit/') ;
}, function(data, getResponseHeaders) {
console.log("ERROR");
});
};
}
The customer saves to the database fine. The problem is that I can't get the id of the object that was just saved in order to redirect to the edit page.
The 'data' object is populated but it looks like this:
b {customer: Object, $then: function, $resolved: true, $get: function, $save: function…}
$resolved: true
$then: function (callback, errback) {
customer: Object
Can anyone give me a hand on how to dig the id out of the returned data?
Thanks!

According to your data object it should be
$location.path(data.customer.id + '/edit');

Related

Issue with backbonejs model objects json representation

I have a question related to contained backbonejs model objects.
Using yeoman backbone generator described at https://github.com/yeoman/generator-backbone, I created a backbone application to test how a model object that contains another model object is being sent as json to the backend server.
I added two models contact and address to this test application.
yo backbone:model contact
yo backbone:model address
Contact object contains one address object. Contact and address backbone model objects are shown below. The init function in the generated main.js is also shown below. The console log is shown below. Ignore the POST error since there is no endpoint contacts.
My question is: in the Chrome browser Developer Tools window, network tab the request payload is:
{"name":"John Doe"}
What changes are needed to the backbone model objects so that the request payload will have the contained address object also? Thanks. I want the payload to look like this:
{
"name": "John Doe",
"address": {
"addressLine1": "Somewhere"
}
}
From console in the Developer Tools window, I can confirm addressLine1 is 'Somewhere' in the contained address object :
Inside Contact.initialize
address.js:14 Inside Address.initialize
contact.js:24 Inside Contact.getAddress
main.js:12 Hello from Backbone! name = John Doe addressLine1 = Somewhere
contact.js:21 Inside Contact.validate
main.js:22 return value from save [object Object]
jquery.js:8630 POST http://localhost:9001/contacts 404 (Not Found)
...
main.js:19 Error [object Object]
From main.js
init: function () {
'use strict';
var myContact = new Test.Models.Contact();
console.log('Hello from Backbone! name = ' + myContact.get('name') + ' addressLine1 = ' + myContact.getAddress().get('addressLine1'));
var rv = myContact.save(null,{
success: function(response) {
console.log('Success ' + response);
},
error: function(response) {
console.log('Error ' + response);
}
});
console.log ('return value from save ' + rv);
}
};
From contact.js
/*global Test, Backbone*/
Test.Models = Test.Models || {};
(function () {
'use strict';
Test.Models.Contact = Backbone.Model.extend({
url: '/contacts',
initialize: function() {
console.log ('Inside Contact.initialize');
this.address=new Test.Models.Address();
},
defaults: {
name: 'John Doe'
},
validate: function(attrs, options) {
console.log ('Inside Contact.validate');
},
getAddress: function() {
console.log ('Inside Contact.getAddress');
return this.address;
},
parse: function(response, options) {
console.log ('Inside Contact.parse');
return response;
}
});
})();
From address.js
/*global Test, Backbone*/
Test = {}; // a global object
Test.Models = Test.Models || {};
(function () {
'use strict';
Test.Models.Address = Backbone.Model.extend({
url: '',
initialize: function() {
console.log ('Inside Address.initialize');
},
defaults: {
addressLine1: 'Somewhere'
},
validate: function(attrs, options) {
console.log ('Inside Address.validate');
},
parse: function(response, options) {
console.log ('Inside Address.parse');
return response;
}
});
})();
Call stack when I set a breakpoint at contact validate function:
validate (contact.js:21)
_validate (backbone.js:568)
save (backbone.js:465)
init (main.js:14)
(anonymous) (main.js:29)
fire (jquery.js:3099)
fireWith (jquery.js:3211)
ready (jquery.js:3417)
completed (jquery.js:3433)
When the above break point is triggered, I can verify this object has address information:
this
child {cid: "c1", attributes: {…}, _changing: false, _previousAttributes: {…}, changed: {…}, …}
address:child
attributes:{addressLine1: "Somewhere"}
changed:{}
cid:"c2"
_changing:false
_pending:false
_previousAttributes:{}
__proto__:Backbone.Model
attributes:{name: "John Doe"}
changed:{}
cid:"c1"
_changing:false
_pending:false
_previousAttributes:{}
__proto__:Backbone.Model
Thanks a lot.
I saw Nesting collection or models within another model
I added the following line of code just before I save the model in main.js:
myContact.set('address', myContact.getAddress().toJSON());
then I can verify the request payload is what I was expecting:
{"name":"John Doe","address":{"addressLine1":"Somewhere"}}
Thanks to Emile Bergeron for pointing out toJSON() method must be called.
The take away for me is backbone is bare bones. It is not smart enough to figure out an object may contain other objects and do the needful. The javascript programmer must call toJSON() at appropriate time.

I want to call a variable outside the function in backbone.js

i want to call name variable which is inside function
mycollection.fetch().Actually whenever i try to declare name variable
globally it fails to work.Thank you in advance.
var MyModel = Backbone.Model.extend({
defaults: {
"id": "",
"name": ""
}
});
var MyCollection = Backbone.Collection.extend({
url: "http://ec2-54-186-169-140.us-west-2.compute.amazonaws.com/automation-poc-api/list",
model: MyModel,
parse : function(response){
return response;
//api returns objects in the content attribute of response, need to override parse
}
});
var myCollection = new MyCollection();
myCollection.fetch({
success: function(col) {
// console.log(col.toJSON()[0].lists[0].name);
var name=col.toJSON()[0].lists[0].name;
}
});
You are declaring name locally inside the callback.
If you define name like
var name;
before calling myCollection.fetch(), you can then put
name=col.toJSON()[0].lists[0].name;
inside the success callback. Just be aware that the name value will not be updated until the asynchronous fetch has completed. If you tried this before and didn't see a value, it is probably because the response had not occurred and the name value was not yet initialized.

angular grep or filter to get an object given an ID

I have a json array of Category objects from my database. (I am storing it in vm.data in my controller, which is bound to an angular repeater.)
Now I need to go back and get the SubCats in each category using the 'Category' field in each SubCat.
Here is my generic data getter:
function getData(params, callback) {
$http({
method: 'GET',
url: 'database.php',
params: params
}).then(function successCallback(response) {
callback(response.data);
});
}
So, to get my Categories, I do:
function getCats() {
var params = {};
params.table = "Categories";
getData(params,buildCats);
};
which calls back to
function buildCats(data) {
vm.data = data;
$.each(vm.data,function(index,value){
getSubCat(value.uID);
});
};
then, one by one it gets the SubCats:
function getSubCat(categoryId) {
var params = {};
params.table = "SubCats";
params.where_field = "Category";
params.where_value = categoryId;
getData(params, buildSubCat);
};
which calls back to this: (I no longer have the Category Id, so I have to go looking for it in my SubCat data)
function buildSubCat(data) {
var categoryId = data[0].Category;
$filter('filter')(vm.data, function(el){
el.uID==categoryId;
}, true).SubCats = data;
}
and it is here I am running into trouble.
I need to get hold of the unique Category object in vm.data - the actual object, not a copy of it - whose uID equals the ID of the SubCats I am currently processing, then ADD the current SubCat object to it. The above does not work. At least, I never see the SubCats within the Categories in vm.data.
I tried to pull it apart, like this:
var foo = $filter('filter')(vm.data, function(el){
el.uID==categoryId;
}, true);
foo.SubCats = data;
but foo is new instance of the Category object, not a reference to the one in vm.data, so my adding is not affecting vm.data.
I suppose I could try to replace vm.data[n] with foo, but this seems a poor practice.
How can I asynchronously retrieve the Categories and SubCats and combine them?
Something tickling the back of my brain tells me I should be passing the actual Category object into the getData, so it is directly available in the callback, but I'm not sure if that makes sense.
OK, I figured out a solution, just not sure if it's the best.
I accept a third param to pass the target object into the getData function:
function getData(params, target, callback) {
$http({
method: 'GET',
url: 'index.php',
params: params
}).then(function successCallback(response) {
callback(target, response.data);
});
}
In my getCats, I pass vm.data as the target param (though there is no need to, as will be seen in a moment).
function getCats() {
var params = {};
params.table = "Categories";
params.sort_field = "Order";
params.sort_dir = "ASC";
getData(params, vm.data, buildCats);
};
Now, I should be able to do this:
function buildCats(target, data) {
target = data;
$.each(target,function(index, catObj){
getSubCat(catObj);
});
};
But for some reason it does not work. target seems to have lost its reference to vm.data. Bonus points if you can figure out what went wrong.
But that's OK - I don't need the target - I can assign the results directly to vm.data:
function buildCats(target, data) {
vm.data = data;
$.each(vm.data,function(index, catObj){
getSubCat(catObj);
});
};
I can now pass the actual Category object itself to the getSubCat call:
function getSubCat(categoryObject) {
var params = {};
params.table = "SubCats";
params.where_field = "Category";
params.where_value = categoryObject.uID;
params.sort_field = "Order";
params.sort_dir = "ASC";
getData(params, categoryObject, buildSubCat);
};
which means I now have the actual object in the callback:
function buildSubCat(categoryObject, data) {
categoryObject.SubCats = data;
};
And my HTML looks like this:
<div class="category" ng-repeat="cat in vm.data">
<h2>{{cat.Name}}</h2>
<div class="subcategory" ng-repeat="SubCat in cat.SubCats">
<h3>{{SubCat.Name}}</h3>
</div>
</div>

Backbone, getting parameters others than data in collection

Given the following json:
{
"admin": false,
"data": [
{
value: key,
value :key
},
{
value: key,
value :key
}
]
}
I defined my collection like this:
var myCollection = Backbone.Collections.extend({
url: myurl.com,
parse : function (response) {
return response.data;
}
});
It works like charm, it fill my collection with the data array, however, into the tamplate, I need to render some content when admin is equal true. But I cannot find a way to pass that value to the template.
Any chance any of u kind guys can point it into the right direction to solve this?
You could save the admin flag as a property of the collection in the parse method:
var myCollection = Backbone.Collection.extend({
model: myModel,
isAdmin: false,
...
parse : function (response) {
this.isAdmin = response.admin; //save admin flag from response
return response.data;
}
});
Then you could retrieve it and pass it to your template or use it in any other way in the view render method:
var myView = Backbone.View.extend({
collection: new myCollection(),
...
render: function(){
//retrieve admin flag from collection:
var isAdmin = this.collection.isAdmin;
//you could add it into the json you pass to the template
//or do anything else with the flag
}
});
You can try this fiddle with a very basic render function.

Backbone is interpolating query string in model URL. How do I stop it?

I have a Backbone model that looks like this
...
var Address = Backbone.Model.extend({
urlRoot: '/address/'
});
return { address: Address }
});
I have a template that prints out an address in a form. The template is rendered by a view that is passed an address id in it's 'render' function. The view is reached by a route like this 'address/:id'.
The view looks like this:
var AddressView = Backbone.View.extend({
el: $('#myclass'),
render: function(options) {
var that = this;
var addr = new A.address({id: options.aid});
addr.fetch({
reset: true,
success: function(address) {
var template = _.template(ATemplate, {address: address});
that.$el.html(template);
}
});
return this;
},
events: {
'submit .edit-address-form': 'editAddress'
},
editAddress: function(ev) {
//serializeObject creates JSON object from form data
var addressDetails = $(ev.currentTarget).serializeObject();
addr.save(addressDetails, function(addr) {
R.router.navigate('', {trigger: true});
});
return false;
}
});
return {
addressView: new AddressView()
};
});
There are two problems. The first problem is that the 'editAddress' function is never getting called, even though the class name is correct and the button type = is 'submit'.
The second problem is when I submit the address form the URL is garbled, a query string is interpolated between the base URL and /#/address, as in
http:///ldmanclient/address=2500+Moffitt+Library&address2=University+of+California%2C+Berkeley&city=Berkeley&zipcode=94720&mailcode=6000&aid=1#/address/1
Has anyone seen this type of behavior before? What am I doing wrong?
As mu said, the form is being submitted the standard way before Backbone gets to it. Try preventing the submit action:
editAddress: function(ev) {
ev.preventDefault();
// same code as above
}

Resources