Iterate over returned angular resource in controller - angularjs

I have a controller that accesses a resource Tag like so:
$scope.tags = Tag.query();
which resolves to something like this:
$scope.tags = [
{ name: "tag1", label: "Tag1" },
{ name: "tag2", label: "Tag2" },
{ name: "tag3", label: "Tag3" },
{ name: "tag4", label: "Tag4" },
];
For this particular controller, the returned tags should have an additional attribute "active": true, like { name: "tag1", label: "Tag1", active: true }.
How can I iterate over the returned promise once it is resolved to add this boolean?

Use the promise.then() function.
Tag.query().$promise.then(function (results) {
angular.forEach(results, function (result) {
result.active = true;
});
$scope.tags = results
});
see the docs on $q

I think what you need is this:
$scope.tags = Tag.query(function() {
$scope.tags['active'] = true;
});
When the server finishes calling the function that adds the property is executed.
I recommend you read https://docs.angularjs.org/api/ngResource/service/$resource

Related

Capturing checkbox values to table

I'm trying to create a form that will capture input, select and checkbox values and post them to a table. Everything seems to be working fine, with the exception of the checkboxes. What am I doing wrong?
https://jsfiddle.net/mujaji/uvkzmyox/8/
var app = angular.module("myapp", []);
app.controller("ListController", ['$scope', function($scope) {
$scope.jobtypes = ['accountant', 'janitor', 'ceo', 'receptionist'];
$scope.hobbies = [{name: "long walks on the beach", id: "1", isChecked: false},{name: "eating cheese", id: "2", isChecked: false},{name: "writing haikus", id: "3", isChecked: false},{name: "looking for bigfoot", id: "4", isChecked: false},{name: "watching police academy", id: "5", isChecked: false}];
$scope.personalDetails = [
{
'fname':'Muhammed',
'lname':'Shanid',
'jobtype': 'ceo',
'email':'shanid#shanid.com',
'active': true,
'hobbies': [{name: "long walks on the beach", id: "1"},{name: "watching police academy", id: "5"}]
},
{
'fname':'John',
'lname':'Abraham',
'jobtype': 'accountant',
'email':'john#john.com',
'active': true,
'hobbies': [{name: "writing haikus", id: "3"},{name: "looking for bigfoot", id: "4"}]
},
{
'fname':'Roy',
'lname':'Mathew',
'jobtype': 'janitor',
'email':'roy#roy.com',
'active': false,
'hobbies': [{name: "eating cheese", id: "2"}]
}];
$scope.addNew = function(personalDetails){
$scope.personalDetails.push({
'fname': personalDetails.fname,
'lname': personalDetails.lname,
'email': personalDetails.email,
'jobtype': personalDetails.jobtype,
});
$scope.PD = {};
};
$scope.remove = function(){
var newDataList=[];
$scope.selectedAll = false;
angular.forEach($scope.personalDetails, function(selected){
if(!selected.selected){
newDataList.push(selected);
}
});
$scope.personalDetails = newDataList;
};
$scope.checkAll = function () {
if (!$scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
angular.forEach($scope.personalDetails, function (personalDetails) {
personalDetails.selected = $scope.selectedAll;
});
};
}]);
The problem your facing is caused by the fact that hobbies and their checked state is outside the scope of your addNew(personalDetails) function.
Although not ideal for angular, something like this would work:
$scope.addNew = function(personalDetails) {
//get all checked hobbies
let selectedHobbies = [];
$scope.hobbies.forEach(function(hobby) {
if (hobby.isChecked) selectedHobbies.push(hobby);
});
$scope.personalDetails.push({
'fname': personalDetails.fname,
'lname': personalDetails.lname,
'email': personalDetails.email,
'jobtype': personalDetails.jobtype,
'hobbies': selectedHobbies
});
$scope.PD = {};
};
EDIT
The answer above is really a pure javascript approach to filtering by selected hobbies. A more 'angular' approach would be to break your filter logic out into its own function so that you're not mixing concerns. For example:
Add the 'filterFilter' dependency to your controller:
app.controller("ListController", ['$scope','filterFilter',
function($scope, filterFilter) { ... }
then in your controller, define an array to hold your selected hobbies and add a filter helper to filter by isChecked:
$scope.hobbiesSelection = [];
$scope.selectedHobbies = function selectedHobbies() {
return filterFilter($scope.hobbies, { selected: true });
};
place a watch on the hobbies object to watch for changes to selected hobbies:
$scope.$watch('hobbies|filter:{isChecked: true}', function (hobbies) {
$scope.hobbiesSelection = hobbies;
}, true);
and finally, you can update your addNew() method to simply be:
$scope.addNew = function(personalDetails){
$scope.personalDetails.push({
'fname': personalDetails.fname,
'lname': personalDetails.lname,
'email': personalDetails.email,
'jobtype': personalDetails.jobtype,
'hobbies': $scope.selectedHobbies
});
$scope.PD = {};
};
So while there is a bit more code (and complexity) there for this example, you are separating the logic from the data a bit better. There is working example here: https://jsfiddle.net/mt0tvkwm/

how to expect result form filter in unit test

How to expect for equal result from filter if it array with mocked data (tree).
My custom filter takes two parameters, tree and query. If query is 'foo' the result from filter will be:
{
nodes: [
{
label: 'foo1'
},
{
label: 'foo2'
}
],
label: 'foo'
}
how to expect any element in array for equal with mocked tree?
My unit test:
describe('customFilter', function () {
'use strict';
var definitionSearch,
tree;
beforeEach(module('myApp'));
beforeEach(inject(function ($filter) {
customFilter= $filter('customFilter');
tree = [
{
nodes: [
{
label: 'foo1'
},
{
label: 'foo2'
}
],
label: 'foo'
},
{
nodes: [
{
label: 'bar1'
}
],
label: 'bar'
}
];
}));
it('should find name', function () {
var query = 'foo'
expect(customFilter(tree, query)).toEqual(/* what should be here*/);
//customFilter(tree, query) ===> tree[0]
});
});
simple solution is save result from filter to variable and iterate for items in array to compare
var result = customFilter(tree, 'foo')
expect(_.isEqual(result[0].nodes.sort(), tree[0].nodes.sort())).toBe(true);
expect(result[0].label === tree[0].label).toBe(true);
expect(result[0].nodes.length === tree[0].nodes.length).toBe(true);

get and getList why no have the same unit?

In my application I'm having the bellow problem
SERVER RESPONSE DATA
route /users
{ data: [
{ id: 5, name: 'peter' },
{ id: 10, name: 'adan' }
] }
route /users/5
{ data: { id: 5, name: 'peter' } }
case01
Restangular.one('users', 5).get().then(function(user){
$scope.user = user;
});
case02
Restangular.all('users').getList().then(function(users){
$scope.user = users[0];
});
In my case02 I can access $scope.user.id, but in case01 I have that to do $scope.user.data.id (what's not preferible when I render this in my template with {{ user.id }} where I have that use {{ user.data.id }} )
In my case02 I can change my data and use $scope.user.save(), but in my case01 I can't access this function
In my restangular I configure this for handle data from server when I use getList because it come in one object and not in one Array
RestangularProvider.setResponseInterceptor(function(response, operation, what) {
if (operation === "getList") {
// from:
// {data: [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]}
// to:
// [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]
return response.data;
}
return response;
});
So I would not work, because then I will have a normal JS object and not a Restangular object, the Restangular object has several methods that I still intend to use the same $scope, so we would like to preserve this.
example:
In an item from a case02 consultation (array) I have an object with methods of Restangular:
all (), allUrl (), one (), oneUrl (), ... and the properties of the object I sought (id, name) within $ scope.user.
{
all: function(){...},
allUrl: function(){...},
one: function(){...},
oneUrl: function(){...},
...
id: data_from_user,
name: data_from_user
}
In an item from a case01 query (object) I have an object with methods of Restangular within $ scope.user and object properties that sought (id, name) within $ scope.user.data.
{
all: function(){...},
allUrl: function(){...},
one: function(){...},
oneUrl: function(){...},
...
data: {
id: data_from_user,
name: data_from_user
}
}
For this reason it would not work the solution you presented because it is okay to work with restangular the subject, not an object of normal JS, but thank you for the answer Tim Castelijns.
if (operation === "getList") {
// from:
// {data: [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]}
// to:
// [{ id: 1, name: 'peter' }, { id: 2, name: 'adan' }]
return response.data;
}
return response;
If operation is getList, you return response.data, which is
[
{ id: 5, name: 'peter' },
{ id: 10, name: 'adan' }
]
but if operation is not getList, you return response, which is the entire object, including the data sub-object
{
data: {
id: 5,
name: 'peter'
}
}
Your choice to return different results depending on what operation it is, is the reason you need to do user.data.id for case01, because you assign the result to user with then(function(user), when in fact user represents the entire data sub-object containing the user.
You should be able to solve it by changing to this
Restangular.one('users', 5).get().then(function(response){
$scope.user = response['data'];
});

angularJs filter from in controller

i have the following arry in my scope
items: [
{
id: "0",
name: "כיבוי אורות",
roomId: "0",
type: "scenario",
status: 1
},
{
id: "1",
name: "הדלקת אורות",
roomId: "0",
type: "scenario",
status: 1
},
{
id: "0",
name: "תנור מטבח",
roomId: "0",
type: "heater",
status: 0
}]
i would like to filter it by id and type within the controller ( not by ng-repeat | filter).
Thanks allot
Avi
Well if you want to filter by name and id from within the controller you could just use native filter. Look at the polyfill for support for older browsers.
var type = "TypeTOfilter", id=idToFilter;
$scope.items = items.filter(function(itm){ return itm.id === id && itm.type === type });
Or you could even inject ``$filter` in your controller, if you just want to do it one time, and not have it in the ng-repeat.
.controller('ctrl', ['$scope', 'filterFilter', function($scope, filter){
//...
$scope.items = filter(items)({type:type, id:id});
//....
}]);
Or you could even do a for-loop to filter out items..

get checkbox group values in angular watch

when tick on each checkbox, i can get all the checked values.
i want to put these values to their respective group.
here are my expected result :
{
'pattern' : ["Plain","Self Design"],
'colour' : ["Blue","Grey"]
}
im using angular $watch to get the selected values.
$scope.$watch('filters|filter:{selected:true}', function (nv, ov, scope) {
$scope.filter_selected = [];
angular.forEach(nv, function (value) {
angular.forEach(value.options, function (v, k) {
if (v.selected == true) {
this.push(v.name);
}
}, $scope.filter_selected);
});
}, true);
here is the full code in fiddle
UPDATE:
i manage to get my expected result with these code :
$scope.$watch('filters|filter:{selected:true}', function (nv, ov, scope) {
$scope.filter_selected = {pattern: [], colour: []};
angular.forEach(nv, function (value) {
if (value.name == 'pattern') {
angular.forEach(value.options, function (v, k) {
console.log(this);
if (v.selected == true) {
this.push(v.name);
}
}, $scope.filter_selected.pattern);
}
if (value.name == 'colour') {
angular.forEach(value.options, function (v, k) {
//console.log(this);
if (v.selected == true) {
this.push(v.name);
}
}, $scope.filter_selected.colour);
}
});
updated fiddle
now, how to make my checking part dynamic if i have more groups?
I have updated your code to simplify what you have above, hopefully achieving the outcome you want. I don't really think you need the watch (unless your update requirements are more complicated), but you should be able to build upon this never the less.
http://jsfiddle.net/j35zt/
The controller code was simplified as follows:
app.controller('FilterCtrl', function ($scope, $http) {
$scope.filters = [
{ name: 'pattern', placeholder: 'pattern',
options: [
{ name: 'Plain', selected: false },
{ name: 'Self Design', selected: false },
{ name: 'Check', selected: false },
{ name: 'Stripe', selected: false },
{ name: 'Print', selected: false }
]},
{ name: 'colour', placeholder: 'colour',
options: [
{ name: 'White', selected: false },
{ name: 'Blue', selected: false },
{ name: 'Grey', selected: false }
]}
];
$scope.updateOutput = function() {
$scope.filter_selected = {};
angular.forEach($scope.filters, function(f) {
$scope.filter_selected[f.name] = [];
angular.forEach(f.options, function(o){
if(o.selected){
$scope.filter_selected[f.name].push(o.name);
}
});
});
}
});
Just note, that the view also needed to be changed to match the controller. Basically ng-change is the sole cause of the updating.

Resources