Update Ember model live via action - ajax

I'm new to Ember.js and keep struggling on a simple task. My goal is to achieve live update of the page content after action is triggered. I'm quite lost in Ember logics regarding route-controller-model relationship in this case.
So my template.hbs is something like:
<h1>{{model.somedata}}</h1>
<button {{action 'getContent'}}>Get Content</button>
My controller accepts some params from user form and makes the AJAX call:
export default Ember.Controller.extend({
somedata: 'hello',
actions: {
getContent: function () {
var self = this;
Ember.$.ajax({
// ... standart call with some query data
success: function(result) {
self.set('somedata', result);
}
});
}
}
});
My route model returns only controller params, so if I get it right as controller properties get updated, there must be a simple step to update the current model and display all changes to the template.
export default Ember.Route.extend({
model: function(params) {
return params;
}
});
Can you guys give me a tip how this process is regularly built in Ember?

You are looking for self.set('model.somedata', results).
Your code as it stands is setting the somedata property on the controller, which doesn't affect anything.

Yeah it's a bit confusing, here's how I think of it...
First Ember sets up the route.
The route has multiple hooks that are used to get and process the model (i.e. beforeModel, model, afterModel). Ember will always look for these as part of it's opinionated nature.
There is also a setupController hook that will fire after all the model hooks. As part of the setupController hook, the controller will be created and the model will be passed to the controller.
Once that happens, I've found it helpful to think of the model as no longer being part of the route, but the controller instead.

Controllers will be deprecated. So IMO do not use controllers.
Handle that action in your route. If you bind value to the object returned by model hook, your data and page will be updated when you update the value.
export default Ember.Route.extend({
somedata: null,
model: function(params) {
return {
params: params,
somedata: this.get('somedata')
};
},
actions: {
getContent: function () {
...
var _this = this;
...
success: function(result) {
_this.set('somedata', result);
}
}
}
});

Related

Not able to scuffle between states and sharing data using ui-router

I'm using like this
$stateProvider
.state('/', {
url: '/Home',
templateUrl: 'Home.html',
controller: 'ctrl'
})
.state('category', {
url: '/category',
templateUrl: 'category.html',
controller: 'ctrl'
})
.state('Item', {
url: '/Item',
templateUrl: 'Item.html',
controller: 'itm'
})
Currently i'm using $rootScope.category=[]; to display category.is there any other way to do this.
i'm trying to load category with ng-init in Home state and on category click i want to load items with category id.How could i do this.
Plunkr https://plnkr.co/edit/A8JXIWa6Jc4RxsGvucFa?p=preview
Best way to go about doing this sort of nested view is with child states and resolve blocks.
.state('app', {
url: '',
template: '<div ui-view></div>',
controller: 'AppCtrl',
abstract: true
})
The main state in my example, 'app' is an abstract state. Abstract states can have child states but cannot be accessed directly. If we prefix all of the states in our application with 'app.' they will be a child of the app state and will inherit the scope of the app state. Notice how I use $scope.goHome() in the category controller even though it is defined in the app controller.
.state('app.home', {
url: '/home/',
templateUrl: 'app.home.html',
controller: 'HomeCtrl',
resolve: {
Categories: function (CategoryService) {
return CategoryService.getAll();
}
}
})
.state('app.category', {
url: 'category/:categoryId/',
templateUrl: 'app.category.html',
controller: 'CategoryCtrl',
resolve: {
Category: function ($stateParams, CategoryService) {
return CategoryService.getById(+$stateParams.categoryId);
},
Items: function ($stateParams, ItemService) {
return ItemService.getByCategoryId(+$stateParams.categoryId);
}
}
});
These next two states in the example, 'app.home' and 'app.category' are the ones most relevant to your question. Notice how I have resolved the data we need to populate the view before entering the state. This functionality happens in the resolve block, and all variables that we resolve in the state can be accessed directly in the state controller (as well as all childrens' controllers) or in consecutive resolve statements.
For the sake of abstracting out reusable functionality I created an ItemService and CategoryService that I use in the resolve block. They are relatively simple and can be seen in the plunkr. The two important functions are CategoryService.getById and ItemService.getByCategoryId. These both take the same param categoryId for which I retrieve from the state's $stateParams which are passed in to the $state.go function as the second parameter as seen in the HomeCtrl controller. These parameters are be included in the url in the state.
.controller('AppCtrl', function ($scope, $state) {
$scope.goHome = function () {
$state.go('app.home');
}
})
.controller('HomeCtrl', function ($scope, $state, Categories) {
$scope.Categories = Categories;
$scope.goCategory = function (category) {
$state.go('app.category', {categoryId: category.id});
};
})
.controller('CategoryCtrl', function ($scope, Category, Items) {
$scope.Category = Category;
$scope.Items = Items;
});
The controllers here are also relatively simple. The resolved variables are injected into the controllers (and must be named the same). They are then assigned to the scope so that they can be accessed in your templates.
I went ahead and created models for both categories and items as an unnecessary, but useful abstraction. They are unnecessary in regards to state management.
Hopefully this example is enough to get you started with nested states in ui-router.

Ember model with session data

I have a problem with ember when I want to use a session data (companyId that is assigned to a user) in a model. I am using ember-cli and ember 0.13.1 with ember-simple-auth. Lets say I have a route called user/profile when I want to show information about company that the user is assigned to. In order to do that I need to retrieve a companyId that is assigned to a user from session and make a call with a model to my API. Currently the route for user/profile looks like that:
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole'),
company: this.store.find('company',this.session.get('user.companyId'))
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
controller.set('company', model.company);
}
});
Everything works when you go to that route from different route, but when you are in user/profile route and you press F5 then the this.session.get('user.companyId') is undefined.
Does somebody have an idea how I can solve that problem ?
Best regards
Pawel
It's seems you need to look in session.content setting (if you not overriding beforeModel hook in user/profile route, of course).
If there is two step data fetching in your case, you need to wait for session data in user/profile model hook, and only then you can get user.companyId.
I came up with some solution. I have set an observer for session.user.companyId in route of user/profile. So currently my route looks like that:
//app/routes/user/profile.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
onSessionIsAuthenticated: function () {
var isAuthenticated = this.get('session.isAuthenticated');
if (!isAuthenticated) {
return false;
}
var _this = this;
return Ember.RSVP.hash({
company: _this.store.find('company',this.get('session.user.companyId')).then(function (data) {
_this.controllerFor('user.profile').set('company', data);
})
});
}.observes('session.user.companyId'),
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole')
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
}
});
The rest industry and industry roles are still left in a controller. Now everything seems correctly but I am not sure if this is the correct solution for this problem. I am not sure if this is what you was in your mind #Microfed?
If someone is still struggling with the problem described above. This is the solution:
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function() {
return Ember.RSVP.hash({
industries: this.store.findAll('industry'),
industryroles: this.store.findAll('industryrole'),
company: this.store.find('company', this.get('session.data.user.companyId'))
});
},
setupController: function(controller, model) {
controller.set('industries', model.industries);
controller.set('industryroles', model.industryroles);
controller.set('company', model.company);
}
});

Ember model hook in Route vs Ember.Object.extend({})

What's the difference between using the model hook in an Ember Route:
App.PhotoRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/photos/'+params.photo_id);
}
});
Versus creating your own Ember Object?
App.PhotoModel = Ember.Object.extend({});
App.PhotoModel.reopenClass({
find: function(id){
$.ajax({
url: 'https://www.go.get.my.photo',
dataType: 'jsonp',
data: { id: id },
success: function(response){
return response.data;
}
});
}
});
Why would you use one over the other?
One is part of the workflow and the other is a class.
The model hook will provide the model for a route when it's accessed (in that case photo). Additionally it will wait for the async call to complete and use the result of the ajax call.
Example: http://emberjs.jsbin.com/
Extending Ember.Object will define a class for reusability. It's very much a building block for the entire Ember framework.
App.MyModelObject = Ember.Object.extend({});
A more useful example would be
App.MyModel = Ember.Object.extend({
total: function(){
return this.get('val1') + this.get('val2');
}.property('val1', 'val2')
});
var foo = App.MyModel.create({val1:3, val2:5});
console.log(foo.get('total'));
Example: http://emberjs.jsbin.com/xinozi/1/edit
The two are completely different. model on an Ember route is a hook which ember gives you to (fetch data from an api and create an object that holds the data your controller needs and so on) return a promise which gets resolved to the route's controller's model, when the said route is entered and transitioned into. On the other hand, App.MyModel = Em.Object.extend({}) creates a class which is just a template from which objects which are instances of App.MyModel can be instantiated.
If your application wanted to model users, for example, it would have a user "model" like
App.User = Em.Object.extend({username: 'Alice'})
or something similar. However, if you have a user route which looks like /#/user/id, then the model hook on the route would be something like this
model: function(params) {
return new Ember.RSVP.Promise(function(success, failure) {
//make an ajax call and invoke the success and failure handles here appropriately
});

Unable to consume an Angular service from a Controller

So I've done extensive research and read many tutorials but I can't find a straightforward answer to what I'm trying to accomplish.
I'm simply trying to access a JSON object stored at JSON-Generator.com and stick it in a table and repeat it. Sounds simple enough but it seems there many different ways to do this and all the sources leave out the one piece of info I need.
if I console.log eventsService inside the controller it returns a constructor, if I console.log eventsService.data it returns undefined.
How do I access the data returned from $http in my controller?
// CONTROLLERS
app.controller('currentTradeshowsController', ['$scope', 'eventsService', function($scope, eventsService){
$scope.events = eventsService.data;
}]);
// SERVICES
app.service('eventsService',function($http){
var eventsUrl = 'http://www.json-generator.com/j/bVWJaYaWoO?indent=4';
$http({method: 'GET', url: eventsUrl}).
success(function(data){
// not sure what to write here....
return data;
})
.error(function(){
console.log('ah shit');
})
});
first of all, $http call is asynchronous, so you probably want to use promises to load the data correctly into scope variable
second - you need to return something from the service to make it work outside of the service declaration function
controller
app.controller('currentTradeshowsController', function($scope, eventsService){
eventsService.getData().then(function(data) {
$scope.events = data;
});
});
service
app.service('eventsService',function($http){
var eventsUrl = 'http://www.json-generator.com/j/bVWJaYaWoO?indent=4';
return {
getData: function() {
return $http.get(eventsUrl);
}
}
});
$http.get is a shorthand for GET request, and it returns a promise, so you can simply use it with .then in the controller

Ajax call return json to model I can use data from in my own function

New to Backbone, so I may be over/under complicating things. (built spa's with my own functions in the past)
Psudo code of what I used to do:
AjaxCall(
url: "get json result"
success:
parse json
call Update(json.a, json.b)
)
function Update(a, b){
//do something with a/b var's
}
For a more abstract idea of what I am envisioning atm. If I click an update button, I want it to hit the server and return a success/fail status along with an Id and a message (imagining all in json format).
Found a few examples, but none seem to fit so far.
To do that, you'd use a Backbone model:
var Message = Backbone.Model.extend({
urlRoot: "messages"
});
var myMessage = new Message();
myMessage.save({}, {
success: function(model, response, options){
// code...
},
error: function(model, xhr, options){
// code...
}
});
Basically: the model configures the API call, and save will use backbone's sync layer to handle the actual AJAX. You can pass success and error callbacks to the `save function.
The first parameter to the save function are the attributes which are to be saved (see http://backbonejs.org/#Model-save), which seem to need to be empty according to your question.
Since the model instance has no id attribute, the save call will trigger a POST request to the API. If by any chance you actually need to provide an id (so that a PUT call gets triggered instead), simply do
myMessage.save({id: 15}, {
success: function(model, response, options){
// code...
},
error: function(model, xhr, options){
// code...
}
});

Resources