Randomizing DOM elements with Angular - angularjs

This doesn't seem to work. Is it ok to pass arguments to the random() callback?
function getAnswers($scope){
$scope.answers = conjugate("작다");
$scope.$on('$viewContentLoaded', random($(".answer")));
}
function random(r) {
r.children().sort(function() {
return (Math.round(Math.random()) - 0.5);
}).appendTo(r);
};

This isn't really the "Angular way" to do this.
You're not supposed to be doing any DOM manipulation or even referencing the DOM in your controller.
Instead, what you'd want to do is manipulate model data that affects your view. In your case, I'd randomize the array of data, rather than randomize the DOM elements themselves.:
function MyCtrl($scope) {
$scope.answers = [ /*... some data here ... */];
$scope.randomizeAnswers = function () {
fisherYates($scope.answers);
};
//a good randomization function
//(see: http://stackoverflow.com/questions/2450954/how-to-randomize-a-javascript-array)
function fisherYates ( myArray ) {
var i = myArray.length, j, tempi, tempj;
if ( i == 0 ) return false;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
tempi = myArray[i];
tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
}
}
}
The use of $on is probably unnecessary here, it seems like you just want to randomize after the array is loaded? That's really going to depend on what in the world your conjugate() method is doing.
I have no idea what your conjugate() method does... but presuming it does some sort of Asynchronous work to return data, you might have to use $q and return a promise from that method. Then you can put your randomization call in a .then() callback that would fire when the data is loaded.

Related

FIlter in controller with multiple values

I have 3 switches (checkboxes, they return true or false) to filter in a list.
The list:
vm.products = Product.query();
In my controller, i want to filter vm.products, everytime one of the switchboxes/checkboxes get changed.
All i got so far, is a none working, filter argument:
vm.products = $filter('filter')('id', 1);
The parameter 'filter' - sems like its pointing at a directive? Do i have to do that? And what would be the best way of making a dynamic filter function/builder, when there is multiple values to check on?
I found a way using a custom filter;
.filter("myFilter", function(){
return function(products, productTypes){
var selectedProducts = [];
for (i = 0; i < products.length; i++) {
if (productTypes.indexOf(products[i].productTypeId.toString()) > -1) {
selectedProducts.push(incidents[i]);
}
}
return selectedProducts;
};
})
I then use this in my controller to call the filter function;
vm.filterTypeUpdated = function ($event, typeId) {
var productTypesId = "";
if (vm.filterType1)
productTypesId += "1,";
if (vm.filterType2)
productTypesId += "2,";
if (vm.filterType3)
productTypesId += "3,";
vm.productsRoot = $filter('myFilter')(vm.products, productTypesId);
}
I don't feel like this is the cleanest way of doing it, but it works. If there are any inputs on optimizing this, i am all ears :-)

Angular repeat scope calculations

Hi i'm building a form doing a lot of calculations, such as summarizing keys in objects
[
{ bar: 5, .. },
{ bar: 6, .. },
...
]
I use this expression in currently 35 places in my HTML. Sometimes connected to further calculations and with different keys
<div>
{{ sumKeys('bar') + foobar }}
</div>
The function i use is declared as
app.controller('someCtrl', function($scope){
$scope.sumKeys= function(key){
return (calculated sum);
}
}
My problem is, that if i write a single letter into any input field the sum function is called about 400 times. I know there is a rule, that calls the function if a scope is changed up to 10 times, but isn't there a more efficient way?
Can i output the result without changing the scope? Or force this calculation just to be done once? I know the results do not change any involved scope. So the results should be the same after 2 iterations.
I also implemented the function as a filter with same results.
What you're looking for is a SINGLETON service. You can make one in angular like this: (one of my real examples)
angular.module('ocFileUpload', [])
.service('ocFileUpload', ['$http', '$rootScope', function ($http, $rootScope) {
// Will house our files
this.files = [];
// This fn appends files to our array
this.addFile = function(files){
for (var i = 0; i < files.length; i++){
this.files.push(files[i]);
}
};
this.getFileDetails = function(ele){
// push files into an array.
for (var i = 0; i < ele.files.length; i++) {
this.files.push(ele.files[i])
}
};
this.removeFile = function(fileToRemove){
var indexOfFile = this.files.indexOf(fileToRemove);
this.files.splice(indexOfFile, 1);
};
this.clearFiles = function(){
this.files = [];
};
// Return files array
this.getFiles = function(){
return this.files;
};
}]);
Then just simply use it in your controller/directive:
.controller(['ocFileUpload', function(ocFileUpload){
var ref = ocFileUpload.getFiles();
}]

Angular - Organise controller, factory and "class"

I would like to understand how to have a nice organisation in my angular project.
[see code below]
Does it makes sense to have the getFireList function into the Factory ? Or should i put it into the controller ?
Does the "class" Fire makes sense ? Should i remove it ? Should i move it to the controller ? Should i move it the the factory ?
If you see anything wrong in this code i'm really interested to learn more.
For now, i've got this :
A class "Fire" to create new object of type Fire.
function Fire (p_power) {
// ATTRIBUTES
this.id = null;
this.power = p_power;
this.position = {
x: null,
y: null
}
// GETTERS/SETTERS
// id
this.getId = function() {
return this.id;
}
this.setId = function(p_id) {
this.id = p_id;
}
// power
this.getPower = function() {
return this.power;
}
this.setPower = function(p_power) {
this.power = p_power;
}
// position
this.getPosition = function() {
return this.position;
}
this.setPosition = function(p_position) {
this.position = p_position;
}
// METHODS
this.increasePower = function(p_plus) {
this.power += p_plus;
}
this.decreasePower = function(p_minus) {
this.power -= p_minus;
}
}
A controller
simuApp.controller('FireController', function($scope, FireFactory) {
// ...
});
And a factory
simuApp.factory('FireFactory', function() {
return {
fire_list: [],
getFireList : function() {
return $http.get(site_url+'fire/fireList').
then(
function(success) {
var data = success.data;
var fires = [];
var fire_tmp;
for (i=0 ; i<data.length ; i++) {
fire_tmp = new Fire( data[i].power );
fire_tmp.setId( data[i].idFire );
fires.push( fire_tmp );
}
fire_list = fires;
return fire_list;
}, function(err) {
// ...
}
);
}
}
});
Thanks for your help.
First, let's get the terminology right. .factory is a method to register a function that generates an instance of the service - hence "factory". What it generates, though, is a singleton service instance.
So, the service you create would be more properly named as FireSvc (as opposed to FireFactory), whereas the function that creates it could have the word "factory" in it (although, in the case below, that function name is not really needed - it could just be an anonymous function):
.factory("FireSvc", function FireSvcFactory(){
});
It is a good practice to use a Service to abstract away any domain/business logic from the controller. Keep the controller thin, responsible only to define the ViewModel, and react to events by changing the ViewModel or invoking functions on the Model.
So, having FireSvc.getFireList() makes sense.
Now, whether the list is a collection of plain objects, or instances of Fire is completely independent of Angular and is entirely up to you. In any case, it is too broad of a topic to discuss in a SO answer.

AngularJS multiple concurrent chained $resource calls

I have a need to make multiple concurrent calls to an Angular resource, and chain some actions with the $promise api.
I define a resource like this
myServicesModule.factory('MyResource', ['$resource', 'SETTINGS', function($resource, SETTINGS) {
return $resource(SETTINGS.serverUrl + '/myResource/:id', { },
{
get: { method: "get", url: SETTINGS.serverUrl + '/myResource/show/:id' },
}
);
}]);
My controller needs to retrieve multiple records, and take actions on each one when the record is ready. I am having trouble passing values to the then() closure.
When I do this:
for (var i = 0; i < 3; i++) {
MyResource.get({id: i}).$promise.then(function(item) { console.log(i); });
}
The output is "2, 2, 2".
This code results in the desired output of "0, 1, 2" (order varies depending on when each resource call completes), but this is an ugly solution.
for (var i = 0; i < 3; i++) {
var closure = function(i) {
return function(item) { console.log(i); console.log(item); }
}
UwgCarrier.get({id: i}).$promise.then( closure(i) );
}
Why does the first code snippet return "2, 2, 2" ?
Is there a cleaner way to solve this problem?
It's a matter of closures. Just wrap your closure in another one.
You can make a workaround with a call to an immediate function, that would look like :
for (var i = 0; i < 3; i++) {
(function(c) {
UwgCarrier.get({id: c}).$promise.then( console.log(c); );
})(i);
}
In my example I have replaced "i" by "c" into the closure to make things clear. Like so, the immediate function invoke "i" with its current value within the loop process.
I don't think that there is a better way to do this as it's a javascript concern.
EDIT :
As ES6 is coming soon, you can use the "let" keyword to achieve the same goal without wrapping your inner loop in a closure, because "let" block-scoped your variable.
for (let i = 0; i < 3; i++) {
UwgCarrier.get({id: i}).$promise.then( console.log(i); );
}

How to avoid Infinite $digest Loop error while working with ngResource

I am $watching the $scope.randomObjects in my directive. The $watch will throw an error if the function is not stable and I think that my curried function inside getDesiredAmountOfObjects is idempotent. Is here something going on with ngResource -objects that I can not see?
How can I fix this? Also the desiredAmount is fixed for now.
Here is snippet from my controller:
..
var getDesiredAmountOfObjects = function (objects, randomObjects) {
return function (desiredAmount) {
var amount = desiredAmount || 1;
if (amount >= objects.length) {
return objects;
}
var randoms = randomObjects.slice(0, amount);
//logged objects are always the same in each $digest loop.
//including the $$hashKey
console.log(random);
return randoms;
};
};
//this will initialized only once in controller
ObjectRes.query(function(data) {
$scope.objects = data;
var randomObjects = [];
angular.extend(randomObjects, data);
randomObjects.sort(function () {
return 0.5 - Math.random();
});
$scope.randomObjects = getDesiredAmountOfObjects($scope.objects, randomObjects);
});
..
I can't see everything because I haven't seen how the $watch and views are set up, but something jumps out at me.
If randomObjects is being called from within a watcher, and set to a value on the scope which is also watched, it will cause an infinite $digest cycle.
This is because you are constantly returning a new array reference (slice returns a new array). The watcher of the result will recognize that the reference changed, which will require a new loop of the digest, which will ask for new randomObjects which will trigger that something changed, etc.
Usually, you fix this by making sure your function returns references to the same object, and does not create new arrays. In your case, you are calling slice which creates a new array every time. You need to make sure this function returns the same array every time.
Something like this, perhaps?
var getDesiredAmountOfObjects = function (objects, randomObjects) {
var result = [];
return function (desiredAmount) {
var amount = desiredAmount || 1;
if (amount >= objects.length) {
return objects;
}
// clear the result array and put randoms into it
result.length = 0;
result.push.apply(result, randomObjects.slice(0, amount));
//logged objects are always the same in each $digest loop.
//including the $$hashKey
console.log(result);
return result;
};
};

Resources