Ember.js computed property from store - javascript

I'm trying to get a simple count of objects returned by REST get request from the server to use in another controller in Ember.js
For this reason I need to make an additional request to the server. Basically here's my code and it almost works.. but not quite yet. Maybe someone can figure out why.
It return a PromiseArray, that's why I'm using .then() to access the properties .
App.TestController = Ember.ObjectController.extend({
totalCount: function() {
return this.store.find('question', {test: this.get('id')}).then(function(items) {
var count = items.get('content').get('length');
console.log(count); // This actually logs correct values
return count;
})
}.property('question')
})
It does what it suppose to do and I'm getting correct values printed out in the console.log(), but when I try to use {{totalCount}} in the view template I'm getting [object Object] instead of an integer.
Also, am I properly observing the questions property? if the value changes in its proper controller will the value update?
Thanks

The problem you are seeing is because your are returning a promise as the value of the property and handlebars won't evaluate that promise for you. What you need to do is create a separate function that observes question and then call your store there to update the totalCount-property. It would be something like this.
App.TestController = Ember.ObjectController.extend({
totalCount: 0,
totalCountUpdate: function() {
var that = this;
this.store.find('question', {test: this.get('id')}).then(function(items) {
var count = items.get('content').get('length');
console.log(count);
that.set('totalCount', count);
})
}.observes('question')
})

Alternatively totalCount might lazily set itself, like this:
App.TestController = Ember.ObjectController.extend({
totalCount: 0,
question: // evaluate to something,
totalCount: function() {
var that = this;
that.store.find('question', {test: that.get('id')}).then(function(items) {
var count = items.get('content').get('length');
that.set('totalCount', count);
})
}.observes('question').property()
})

Related

Angular controller not able to read update from service. Any advice?

I have a situation where within my angular service, I have a number of properties. These properties are linked to the controller.
Service:
angular.module('...')
.factory('PollServ', function PollServ($http, ...) {
var service = {
question: '',
votes: [[]]
}
...
// make http request to API
var request = $http({ ...
// once the value is retrieved, update properties
request.then(function (res) {
service.question = res.data.question;
...
}
Controller:
angular.module('...')
.controller('PollCtrl', function PollCtrl(..., PollServ) {
$scope.question = PollServ.question;
$scope.votes = PollServ.votes;
...
Now, although the votes are being updated properly, the question is not. I am not doing anything different, except the fact that votes is an array and question is just a regular string. I think the array may have something to do with being able to dynamically update, but not the simple string.
How can I get it to work, without unnecessary turning the string into an array as well?
You said it yourself - the question is a string and thus will not be updated in your controller/view.
What you could do is turning the question into an object. For example:
In Factory
var service = {
question: {
name: ''
},
votes: [[]]
}
...
service.question.name = res.data.question;
You then need to change the reference in your view to question.name.

Use promise with angularjs user filter

I have a user filter. You can see the mock example below (it returns the same data which it gets, i.e. users_data_contents, in reality there is the function call):
var appModule = angular.module('appModule', [])
.filter('filter_ghosts', function($rootScope){
return function(users_data_contents){ // gets data from view
return users_data_contents;
};
});
I need to use it in async mode, but not quite understand how. I tried something like this:
var appModule = angular.module('appModule', [])
.filter('filter_ghosts', function($rootScope){
function getPromised(){
var cnt = 0, intrv = setInterval(function() {
++cnt;
if (cnt>10) {
return function(users_data_contents){
clearInterval(intrv);
return users_data_contents;
}
}
if (cnt > 50) {
clearInterval(intrv);
}
}, 100);
}
getPromised.$stateful = true;
return getPromised;
});
But this doesn't work.
The html where the filter is applied looks like this:
<tr ng-repeat="user_data in usersList | filter_ghosts">
....
Can anybody help?
UPD
The most close solution to my issue is here: AngularJS : Asynchronously initialize filter
However, it is turned out that the more appropriate approach is not to use filter at all but bind the repeater to array which is changed on corresponding procedures.
Create an empty array in the scope. Then bind your ng-repeat with filter to that array. Then just fill that array with the values when your async operation completes.

Angular Factory Object Persisting Over Storing in Local Storage

I've got a problem that i need help with in Angular 1.4.0.
I have a factory in angular which has a complex structure and method within so i am able to restore the factory back to its original state by calling the method.
The factory looks like this.
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var default values {
a = 1,
b = 2,
resetData : function(){
currentValues = angular.extend(currentValues, defaultValues);
return currentValues
};
};
defaultValues.resetData();
return currentValues;
});
In order to add values to 'a' i call itemFcty.a = 2;
So this method works well when i want to overwrite all the values as and when required.
However i have been asked could i persist the data over a refresh. So i stringify the object into JSON. Like this:
localStorage.setItem('itemFcty', JSON.parse(itemFcty);
However i have hit a snag. The only data to be stored in the local storage is the
{a = 1,b = 2,}
I can add the method back in by doing this:-
itemFcty.resetData = function(){return currentValues = angular.extend(currentValues, defaultValues);}
This is the issue that now the factory does function the same way as before as i am not able to call the function as the call and return outside the default values object is not there any more i can cannot for the life of me work out how to add it back into as everything goes directly into the object as a whole.
Your help would be greatly appreciated!
/*************************EDIT *****************************/
Ok, so i think that i havent explained the point very well.
My factory looks exactly like the above. The user hits refresh. The factory is stored in local storage. I get it back from local storage. But heres the issue.
It looks like this before local storage
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var defaultValues = {
a : 1,
b : 2,
resetData : function(){
angular.extend(currentValues, defaultValues);
// you don't have to return the values
} // <------you can't use ; in the object properties
};
defaultValues.resetData();
return currentValues;
});
Now when i get the data out f local storage and into the factory the factory then looks like this.
angular.module('myApp').factory('itemFcty', function(){
a : 1,
b : 2,
});
I can add the reset data function back in, however as the factory does not contain current or default values, the reset data function will not work.
So basically i am asking how to make my factory, look the same as it does originally after i have reloaded data from the local storage.
Did you load the variable back from localStorage? Also, there is a typo, I'd say var default values. In general stringifying does not account for methods, because that wouldn't make sense for other languages importing them.
Problems:
var default values {, space separated variable and missing assignment operator =.
In the object you can assign values with = but :, so it will produce error { a = 1, b = 2 }.
what you can do is:
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var defaultValues = { // <-------should have to be declared like this
a : 1, // = should be replaced with :
b : 2, // and here too.
resetData : function(){
angular.extend(currentValues, defaultValues);
// you don't have to return the values
} // <------you can't use ; in the object properties
};
defaultValues.resetData();
return currentValues;
});

How to use angular promise as part of a string

I'm looking for a generic solution regarding handling promises and string creation. Basically a timing issue. This code isn't the actual code, but illustrates my problem and my attempted solutions.
I have a two json objects that I need to combine. Either one or both objects might have values that require some information from an API. This information is used to create a label showing which two objects have been combined.
Object with defined label (no lookup necessary):
var object1 = {
type: "some.type",
distribution: 50,
label: "Male"
}
Object with dynamic label (and psuedo code to get label via service $http request):
var object2 = {
type: "some.type",
distribution: 50,
value: "68"
}
// call service to get the data to populate the label
myService.getDynamicObjectData("68").then(function(response){
// should be "Alaska"
object2.label = response.data.label;
});
Desired combination:
var combinedObj = {
type: "some.type.combined",
distribution: 25,
// ideally label would be "Male > Alaska"
label: object1.label + " > " + object2.label
values: [object1, object2]
}
My problem is that object2.label is not populated until after the combination object has been created, specifically the label string. In the view, I'm seeing "male > undefined". I've managed to get as far as "male > 68" but that doesn't really help. When I'm not combining objects, the label is updated as soon as the promise is resolved and there is no issue getting "Alaska" and "Male" to show up as two unique entries. When I combine and create the string from the two labels, it's happening too fast.
The object1 and object2 are created in a service that deals with reading in data and creating these kinds of objects for internal use, then this combination code is in another service dealing with the nesting of such data; so I can't really use a watcher to update that value.
I've tried setting the label to the promise hoping that will work, but it doesn't:
var promise = myService.getDynamicObjectData("68").then(function(response){
// should be "Alaska"
object2.label = response.data.label;
});
var object2 = {
type: "some.type",
distribution: 50,
value: "68",
label: promise
}
The label is just an object with {then(), catch(), finally()} inside. I can't figure a way to get the actual returned values, even if then() returns the right value.
I've tried to use an array and a filter so that I'm never really creating the string until the last while, which means that since the string isn't "real" then it should work as the model is finally updated (as it does when showing objects separately):
var combined = {
type: "some.type.combined",
distribution: 25,
label: [object1.label, object2.label]
values: [object1, object2]
}
module.filter('labelFilter', function(){
return function(input){
if(angular.isArray(input)){
// but input[1].label is a promise object, how do I get the resolved value?
return input[0].label + " > " + input[1].label
}
return input;
}
});
So, I'm turning to the community to see what I might be able to do here. How to you create a string where part of that string is based on the result of a promise? I think if I use $resource, I'd be able to set label: labelResource, and labelResource would eventually resolve to the actual data I want (even the parent object of the data I want would be helpful). Unfortunately, there is other logic that is too complex for $resource so I can't use it without a bit of a refactor. I'm hoping to be able to set label to something like $q.deferred.result and have it all work out (even if I still need the filter).
Anyway, thanks for looking!
You can do it with promises and $q
https://docs.angularjs.org/api/ng/service/$q go to the bottom of page where you can find $q.all which basically is a solution to your problem, when all promises will be resolved you can then call you function to join the strings (labels)
You can try checking out the method mentioned here to do it inside filters.
I made a simple fiddle demonstrating async filters which you can use as a starting point to this problem -> http://jsfiddle.net/7eqsc/5/
angular.module('myApp', [])
.controller('myCtrl', function ($scope, $timeout) {
})
.filter('test', function ($q, $timeout) {
var cache={};
return function (input) {
if (!cache[input]){
//I used timeout, but anything that needs to happen async can happen here
$timeout(function () {
//do something with the input
cache[input]=input+'!';
}, 500);
//return something in the meanwhile to hold it
return 'loading';
}
else return cache[input];
}
});
Not tested of course, but try something like:
module.filter('labelFilter', function () {
var cache = {};
return function (input) {
var cachedItem = cache[inputHash(input)];
if (!cachedItem) {
if (angular.isArray(input)) {
input[0].label.then(function (text) {
//once the promise is finished, put the correct verison inside the cache
cache[inputHash(input)].label = text + " > " + input[1].label;
})
//meanwhile, return the unmodified object.
return input;
}
}
return cachedItem;
}
//you'll have to identify each input for the cache somehow.. it can be a combination of fields for example
function inputHash(input) {
return input.id; //some unique identifier..
}
});
Just keep in mind calling a promise "label" can be very confusing, you should try and reorganize your code so it's more clear when a prop has a promise inside.
Good luck!

How do I get the total number of records in a fixture from a controller?

I'd like to get the total number of records in my fixture from my controller. Here is my fixture code:
FIXTURE
App.Items.FIXTURES = [
{
id: 1
},
{
id: 2
},
{
id: 3,
}
];
In this case, there are 3 total records in the fixture. How do I get that total in my controller?
If you are using something like this:
App.ItemsRoute = Ember.Route.extend({
model: function() {
return this.store.find('items');
}
});
The store.find returns a Ember.PromiseProxyMixin instance, so you can observe the isFulfilled property to know when the data is loaded:
App.ItemsController = Ember.ArrayController.extend({
doSomethingWithTotal: function() {
var length = this.get('model.length');
// do something with de length
}.observes('model.isFulfilled')
});
Like this sample http://jsfiddle.net/marciojunior/UeCWV/
You can ask ArrayController for the property content and it will return an array, which gives you access to length.
Here's an updated Fiddle: http://jsfiddle.net/bYSjD/
App.ItemsController = Ember.ArrayController.extend({
doSomethingWithTotal: function() {
return this.get('content.length')
}.property('content')
});
The way this works is that App.ItemsRoute.model returns a Promise to find items, and when that completes, another method of ItemsRoute is called: setupController.
Ember will automatically handle setupController for you, which populates a controller with the returned model data. The content property of ItemsController will be set, giving your property doSomethingWithTotal a real value.

Categories