I simply want a single $.get call to satisfy a particular model. I can't seem to find any documentation that clearly shows how to go about doing this?
i.e. I have an application controller as:
App.ApplicationController = Ember.ObjectController.extend({
username: '',
firstname: '',
lastname: ''
});
How do I set the username with data from a $.get? I know there is Ember Data and other libraries that do this, but I'd like to do this without those libraries and furthermore better understand how Ember works with Asynchronous data.. Their guides unfortunately didn't help much :/
Thanks for any help :)
UPDATE
Thanks guys for the answers so far, but I don't think I explained my problem sufficiently.
I have application.hbs:
<div class="medium-8 columns">
<dl class="account-menu right">
<dd><p>Welcome, {{username}}</p></dd>
<dd><p>Customer Support</p></dd>
<dd>Log Out</dd>
</dl><!-- .acccount-menu -->
</div>
I have Model:
App.User = Ember.Object.extend({
id : '',
firstName : '',
lastName : '',
username : '',
init: function(){
console.log(App.Account.get('username');
},
fullName: function(){
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName'),
}).reopenClass({
loadAccount: function(){
return $.get( "http://localhost:8080", { id: 1, request: "account" }).then(function(response){
App.Account = App.User.create({'username': response.username});
});
}
});
The Controller is the same as above. I am successfully Creating a new App.Account from App.User.create and successfully triggering the App.User abstract class? init method.. Problem is {{username}} never gets updated? I know I'm missing something simple here! Thanks again for all your help
You would set the model of your ApplicationRoute to the JSON you fetch:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return $.getJSON('your_url_goes_here');
}
})
This way every time the route is entered the model (and thus implicitly the content of your controller) will be set to the JSON returned by your AJAX call.
The model is set with the model hook of the associated route. In your case:
App.ApplicationRoute = Ember.Route.extend({
model: function(){
return $.get.......
}
});
The model hook will accept an asynchronous call, but ember will not continue to render the view until the model hook promise is complete.
Guys thanks so much for your help.. Turns out it was a simple oversight...
I was not returning the Object Model promise 'App.User' that I had created in the loadAccount method, which is called by the ApplicationRouter model hook.
Bad Code
loadAccount: function(){
return $.get( "http://localhost:8080", { id: 1, request: "account" }).then(function(response){
App.Account = App.User.create({'username': response.username});
});
Good Code
loadAccount: function(){
return $.get( "http://localhost:8080", { id: 1, request: "account" }).then(function(response){
return App.User.create({'username': response.username});
});
It makes sense. The $.get method returns a promise to my model that I never delivered on with a return value inside the .then() method
Thank you all for you answers and attention to the matter
Related
I'm using ngResource to get the data from the server side into my view, and I have a custom action that I defined that gets items count, it looks like this in the code (controller):
$scope.totalItems = Item.item_count().total
And in my view :
This customer bought {{totalItems}}
In my view I just get the This customer bought without other part being interpolated. However also when I do this :
console.log(Item.item_count().total)
console.log(Item.item_count())
I get the undefined for first statement but for second one I get this :
Why am I unable to access the total count from the view, and what do I do to fix this?
Update (service code) :
app.factory('Item', function($resource) {
return $resource(
window.config.API_URL + '/:apiPath/:id', {
id: '#id',
apiPath: '#apiPath'
}, {
query: {
method: 'GET',
params: {
apiPath: 'items'
},
isArray: true
},
item_count: {
method: 'GET',
params: {
apiPath: 'item_count'
},
isArray: false
}
}
);
});
You need to get the $promise and pass in a callback like so:
Item.item_count().$promise.then(function(data) {
$scope.totalItems = data.total;
});
If you need to have the data resolved before loading your view, you can use $stateProvider to resolve it. This should give you a good idea how and some implications of using it: http://www.codelord.net/2015/06/02/angularjs-pitfalls-using-ui-routers-resolve/.
look like you get a promise returned.
Try calling console.log(Item.item_count().then(function(data) {
console.log(data);
});
Without seeing your other code my guess is the line
$scope.totalItems = Item.item_count().total is getting resolved before the promise is getting resolved. That line should be in sort of then catch for the promise. However this is just a guess since you haven't actually posted this code.
Because Item.item_count() returns a promise you have to wait until it is resolved before you set $scope.totalItems:
Item.item_count().then(function(item_count) {
$scope.totalItems = item_count.total;
});
Try removing the scope. from your {{}} in the view.
This customer bought {{totalItems}}
I am rendering a view in my backbone application when a collection is reset,
initialize: function(options) {
this.options = options;
this.model.get('projects').fetch({
data: {
organisation_id: this.model.get('id')
}
}, {reset:true});
this.model.get('projects').on('reset', this.render, this);
this.model.get('projects').on('add', this.addNewProject, this);
this.model.get('projects').on('sort', this.addProjects, this);
},
render: function() {
console.log(this.model.get('projects').state);
this.$el.html( this.template({
is_owner: this.options.is_owner,
className: this.options.className,
pagination: this.model.get('projects').state
}));
this.addProjects();
this.filter = new Pops.Views.OrganisationProjectsFilter({
el:this.$el.find('.div-organisation-filter-wrapper'),
model : this.model,
collection: this.collection
});
this.filter.render().el;
return this;
},
As you can see I run a fetch and the reset my collection to the fetched data. My problem is that in the view I am trying to use some values that come from the server, and it appears they are null, here is my collection,
App.Collections.PaginatedProjects=
Backbone.PageableCollection.extend({
url: App.API_ROOT + "/projects/paginated",
// Initial pagination states
state: {
pageSize: 2,
sortKey: "name",
order: 1,
totalRecords:null
},
// You can remap the query parameters from `state` keys from
// the default to those your server supports
queryParams: {
totalPages: null,
totalRecords: null,
sortKey: "sort",
},
// get the state from Github's search API result
parseState: function (resp, queryParams, state, options) {
this.state.totalRecords = resp.total;
this.state.totalPages = resp.total / this.state.pageSize;
this.state.lastPage = this.state.totalPages;
},
// get the actual records
parseRecords: function (resp, options) {
return resp.data;
}
});
As you can see I am running the parse functions to retrieve and set to values, if I console my collection I can see the correct values, but when I try and use them in my view they are null, am I using parse or reset wrong or maybe both?
I think this is because when you render the data didn't arrive yet. Try this:
this.model.get('projects').fetch({
success: function(model,response) {
var data = //some of your answer
}
});
From what I understand, you did a
console.log(this.model.get('projects').state);
and found out it is print out correctly but then used in the
this.template({
is_owner: this.options.is_owner,
className: this.options.className,
pagination: this.model.get('projects').state
})
then it does not appear? If this is the case, I don't think it's a problem in Backbone Collection. I think it has something to do with how you access the data in your template.
If you are using handlebars, because this "state" variable is a json,
you maybe using
{{pagination.pageSize}}
in the hbs file to print it out. I have a heizy memory that it was not supported a while ago. try using
{{pagination/pageSize}}
Also, if this snippet is inside a {{#if}} or a {{#each}} etc, according to the handlebars documentation, you may need to go in a scope. use
{{../pagination/pageSize}} or {{../../pagination/pageSize}}
I ran into the problem a while ago and did a lot of search to found out about this. Hope it helps
I have a backboneJS app that has a router that looks
var StoreRouter = Backbone.Router.extend({
routes: {
'stores/add/' : 'add',
'stores/edit/:id': 'edit'
},
add: function(){
var addStoresView = new AddStoresView({
el: ".wrapper"
});
},
edit: function(id){
var editStoresView = new EditStoresView({
el: ".wrapper",
model: new Store({ id: id })
});
}
});
var storeRouter = new StoreRouter();
Backbone.history.start({ pushState: true, hashChange: false });
and a model that looks like:
var Store = Backbone.Model.extend({
urlRoot: "/stores/"
});
and then my view looks like:
var EditStoresView = Backbone.View.extend({
...
render: function() {
this.model.fetch({
success : function(model, response, options) {
this.$el.append ( JST['tmpl/' + "edit"] (model.toJSON()) );
}
});
}
I thought that urlRoot when fetched would call /stores/ID_HERE, but right now it doesn't call that, it just calls /stores/, but I'm not sure why and how to fix this?
In devTools, here is the url it's going for:
GET http://localhost/stores/
This might not be the answer since it depends on your real production code.
Normally the code you entered is supposed to work, and I even saw a comment saying that it works in a jsfiddle. A couple of reasons might affect the outcome:
In your code you changed the Backbone.Model.url() function. By default the url function is
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
},
This is the function to be used by Backbone to generate the URL for model.fetch();.
You added a custom idAttribute when you declared your Store Model to be like the one in your DB. For example your database has a different id than id itself, but in your code you still use new Model({ id: id }); when you really should use new Model({ customId: id });. What happens behind the scenes is that you see in the url() function it checks if the model isNew(). This function actually checks if the id is set, but if it is custom it checks for that:
isNew: function() {
return !this.has(this.idAttribute);
},
You messed up with Backbone.sync ... lots of things can be done with this I will not even start unless I want to make a paper on it. Maybe you followed a tutorial without knowing that it might affect some other code.
You called model.fetch() "a la" $.ajax style:
model.fetch({
data: objectHere,
url: yourUrlHere,
success: function () {},
error: function () {}
});
This overrides the awesomeness of the Backbone automation. (I think sync takes over from here, don't quote me on that).
Reference: Backbone annotated sourcecode
I have a simple Ember.js app. There's an application view and inside that view there's a results view.
I'm using ember-model to fetch JSON from the server that way:
Map.Winery = Ember.Model.extend({
name: Ember.attr(),
address: Ember.attr(),
address_2: Ember.attr(),
city: Ember.attr(),
stateprovince: Ember.attr(),
country: Ember.attr(),
latitude: Ember.attr(),
longitude: Ember.attr(),
full_address: function () {
return this.get('address') + ' ' + this.get('city') + ' ' + this.get('stateprovince') + ' ' + this.get('country');
}.observes('address', 'city', 'stateprovince', 'country')
});
Map.Winery.adapter = Ember.Adapter.create({
findAll: function(klass, records) {
return Ember.$.getJSON('/api/wineries').then(function(response) {
if (response.success) {
records.load(klass, response.data);
}
});
}
});
I made it as simple as possible so people can have a fair look at what's going on.
So basically, I have a button in my application view that whenever I click, calls an action that calls Map.Winery.findAll(); (reloads data from the server).
What I'd like is, whenever there's new data loaded in the records, the results view should update with the new results.
application.hbs
<button class="locate" {{action "reload" on="click"}}>Reload</button>
{{view Map.ResultsView}}
application_controller.js
Map.ApplicationController = Ember.ObjectController.extend({
actions: {
reload: function() {
var results = Map.Winery.findAll();
}
}
});
results.hbs
<div class="results">
{{#each results}}
<p>{{this}}</p>
{{/each}}
</div>
results_view.js
Map.ResultsView = Ember.View.extend({
templateName: 'results',
didInsertElement: function(e) {
}
});
Now question is: How do I make it so whenever there's new data loaded from the server to the records, results view gets updated?
I tried inserting as much related code as possible, feel free to ask any questions.
Thanks
Just push the new objects into your collection (or replace the collection with the new collection from findAll).
http://emberjs.jsbin.com/EFiTOtu/1/edit
App.IndexController = Em.ArrayController.extend({
actions: {
addNewColor: function(){
this.get('model').pushObject(this.get('newColor'));
}
}
});
Best way I found is to add an event to whatever I need to be triggered and use that trigger whenever I need it.
Using Ember.Evented:
App.ApplicationController = Ember.ObjectController.extend(Ember.Evented, {
viewUpdated: function(args) {}
/* controller stuff here */
});
I'm now able to add events like this from almost anywhere:
this.get('controller').on('viewUpdated', $.proxy(this.viewUpdated, this));
So whenever I have to trigger the view to update I do:
this.get('controllers.application').trigger('viewUpdated', args);
(args is optional by the way)
That way the event gets triggered whenever I want to. It's not as powerful as an observer but still it does the job pretty well!
I have implemented find() and findAll() methods on my Property model. Both methods make asynchronous calls to an API. findAll() is called while connecting the outlets for my home route, and works fine. find() is called by Ember.js while connecting the outlets for my property route. Note that find() is not called when navigating to a property route through actions, but is called when you go directly to the route through the URL.
Here is my router:
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
showProperty: Ember.Route.transitionTo('property'),
home: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('home', App.Property.findAll());
}
}),
property: Ember.Route.extend({
route: '/property/:property_id',
connectOutlets: function(router, property) {
router.get('applicationController').connectOutlet('property', property);
}
}),
})
});
And here are my findAll() and find() methods:
App.Property.reopenClass({
find: function(id) {
var property = {};
$.getJSON('/api/v1/property/' + id, function(data) {
property = App.Property.create(data.property);
});
return property;
},
findAll: function() {
var properties = [];
$.getJSON('/api/v1/properties', function(data) {
data.properties.forEach(function(item) {
properties.pushObject(App.Property.create(item));
});
});
return properties;
}
});
When I go to a route other than index, for example http://app.tld/#/property/1, the route gets rewritten to http://app.tld/#/property/undefined. Nothing is being passed to the content property of the Property controller. How can I make asynchronous calls in the find() method? Unless I am mistaken, asynchronous calls work fine in the findAll() method, which is the source of my confusion.
This question is similar to Deserialize with an async callback, but I'm using the find() method instead of overriding the deserialize() method.
Thanks in advance.
I found that setting the id property explicitly solves this problem. In your case this would look like this.
find: function(id) {
var user = App.User.create();
$.getJSON('/api/v1/property/' + id, function(data) {
user.setProperties(data.user)
});
user.set("id",id); // <-- THIS
return user;
}
Once your user gets its properties set the view updates as normal. Ember just need the id part before in order to update the URL.
Hope this helps :-)
Here's what you want to be doing. I changed the model to User to make things a little clearer.
In the case of find(), you return a blank model instance that gets it's properties filled in when the AJAX request comes back. The nice thing about Ember's data-binding is that you can display this model in a view immediately and the view will update when the AJAX request returns and updates the model instance.
In the case of findAll(), you return a blank array that gets filled in when the AJAX request comes back. In the same way as find(), you can display this list of models (which at first will be blank) in a view and when the AJAX request returns and fills in the array, the view will update.
App.User.reopenClass({
find: function(id) {
var user = App.User.create();
$.getJSON('/api/v1/property/' + id, function(data) {
user.setProperties(data.user)
});
return user;
},
findAll: function() {
var userList = [];
$.getJSON('/api/v1/properties', function(data) {
var users = data.users.map(function(userData) {
return App.User.create(userData);
});
userList.pushObjects(users);
});
return userList;
}
});