angular: return array from object array properties - arrays

I've got a 'user' object passed in when I load up a form, which I can use to directly populate my edit user form. So:
$scope.userData = getUserData();
<input id="uid" value="{{userData.uid}}"/>
<input id="name" value="{{userData.name}}"/>
My data looks like this:
userData = [{
"empId":1,
"name": "bob",
"roles":[{
"roleId":1,
"title":"boss"
},{
"roleId":2,
"title":"employee"
}]
}]
I've got a custom control that wants the roles as a FLAT array of titles.
So, this is what I'm hoping to produce:
<sys-multi id="roles" options="['boss','employee']"></sys-multi>
The trick here is that I'm hoping to do this IN THE TEMPLATE, so no functions so my controller. i.e. something like this (if it worked):
<sys-multi id="roles" options="{{userData.roles.name.join(',')}}"></sys-multi>
I've been playing with grep, but angular doesn't like grep within its {{}}'s.

How about adding a getter (or a function) to your userData object:
$scope.userData = {
"empId":1,
"name": "bob",
"roles":[{
"roleId":1,
"title":"boss"
},{
"roleId":2,
"title":"employee"
}],
get roleNames() {
return this.roles.map(function (role) {
return role.title;
});
}
};
...and then using it this way:
<sys-multi id="roles" options="userData.roleNames"></sys-multi>
http://codepen.io/jlowcs/pen/emPKrB

Related

Create list of items of a single item at once

Angular task 1.5.x:
I have object like this:
{
id: 1,
name: "a",
items: [
{"holiday": "false", "day": "monday"},
{"holiday": "true", "day": "tuesday"...}
]
}
I want a new object to be created in the above way with click of a single button. Note I dont want to add each item separately, all at once. Means, for a single object with name "a", I want to add all items for all days at once.
I can make it work but I want to know the correct way.
ultimately we should be able to create a javascript object in the above format(without id)(I think this format is correct) and send it to server so it will work. But how to add html/angular code so I will get an object that way.
Please let me know for more info.
When using ng-model you do not have to fully define your object in order to have it constructed. E.g.:
Controller
$scope.object= {
items: []
};
var n = 7;
for (var i = 0; i < n; i++)
$scope.object.items({});
HTML
<input type="text" ng-model="object.name"/>
<div ng-repeat="currObj in object.items">
<input type="text" ng-model="currObj.holiday" />
<input type="text" ng-model="currObj.day" />
</div>
The general structure must be defined beforehand, but you do not have to initialize all the properties. They will receive values when binding is triggered (view -> model).
You can do like this :
Create a button in your view and use ng-click directive to capture on click event.
Html:
<button ng-click="createCopy()"></button>
Use angular.copy to create a deep copy of the source object.
This is what angular.copy does as explained in the docs :
Creates a deep copy of source, which should be an object or an array.
If no destination is supplied, a copy of the object or array is
created. If a destination is provided, all of its elements (for
arrays) or properties (for objects) are deleted and then all
elements/properties from the source are copied to it. If source is not
an object or array (inc. null and undefined), source is returned. If
source is identical to destination an exception will be thrown.
Controller:
$scope.mainObject = { id: 1, name: "a", items: [{"holiday": "false", "day": "monday"}, {"holiday": "true", "day": "tuesday"...}] };
$scope.createCopy = function (){
$scope.copyObject = angular.copy($scope.mainObject);
}

Array to multiple object in underscore

I have an array that looks like this:
[{
LocalBond:"0",
LocalCash:"2.42",
LocalEquity:"0",
ForeignEquity: "4",
...
}]
What I want it look like:
[{
Source: "LocalBond",
Value: "0"
},
Source: "LocalCash",
Value: "2.42"
},
Source: "LocalEquity",
Value: "0"
},
{...}
]
I want to turn a single object into many objects. I also need the exclude the 'ForeignEquity' result.
I tried using _.map, and returning the fields I want, but I am struggling a bit. Am I on the right track? When I pass more than one parameter into my function, I don't get the desired result.
The most simple code is pure javascript:
Using for..in access to the property of the object, and inside of the for loop build the array.
http://www.w3schools.com/jsref/jsref_forin.asp
Example:
https://jsfiddle.net/jewrsL8a/5/
var collection = [{
LocalBond:"0",
LocalCash:"2.42",
LocalEquity:"0",
ForeignEquity: "4"
}];
var result = [];
for (var property in collection[0]) {
if(property!=='ForeignEquity'){
result.push({'Source': property, 'Value': collection[0][property]});
}
}
console.log(result);

How to update Angular scope properties when they are passed into directives?

I have functionality where developers can add custom Angular views where they can bind properties to the $scope.settings object. When clicking on the save button the $scope.settings object will be converted to JSON and saved to the database. Something like this will be the result:
{
"name": "bob",
"age": "25"
}
As long as I add elements like <input type="text" ng-model="settings.name" /> everything goes as expected.
But, now I want to add directives like this:
<umb-property property="property in properties">
<umb-editor model="property"></umb-editor>
</umb-property>
With the following code:
$scope.properties = [
{
label: 'Name',
alias: 'name',
view: 'textbox',
value: $scope.settings.name
},
{
label: 'Age',
alias: 'age',
view: 'number',
value: $scope.settings.age
}
];
The 'editor' directive loads views in place based on the 'view' property. The views are third party. The editors are loaded in a dialog. After submission of the settings dialog, the following line of code will convert the settings to JSON:
$scope.dialog = {
submit: function (model) {
var settingsJson = JSON.stringify(model.settings);
},
close: function (oldModel) {
//
}
};
In this case I cannot parse the $scope.settings to JSON, because the $scope.settings.name is not updated anymore. The $scope.editorModel.value was updated instead.
How can I bind the $scope.editorModel.value to $scope.settings.name?
I don't want to end up with a solution where I must update all $scope.settings values with the corresponding values from the editor models, because I want to support the dynamic way to convert the $scope.settings to a JSON value in the database.
For example I dont want to do: $scope.settings.name = $scope.properties[0].value
Use property accessors:
for (var i=0; i<$scope.properties.length; i++) {
$scope.settings[$scope.properties[i].alias] = $scope.properties[i].value;
};
HTML
<div ng-repeat="prop in properties">
<input ng-model="prop.value">
</div>

How to uncheck a checkbox that gets filtered in ng-repeat

I've been scratching my head on this one for hours worth of troubleshooting and I can't seem to figure it out so was wondering if any of you could help.
I have an array of objects in a json file, and I'm making a filtering menu based on different properties in the file that one can check/uncheck in view to filter the results. The issue I have is to be able to uncheck any items in the menu that hide as a result of not being available in the current results being displayed.
I have a plunker example here: https://plnkr.co/edit/KZmMiSisA1gKyahG5rHF
Sample from plunker:
$scope.list = [
{ parent : 'fruit', type : 'orange' },
{ parent: 'fruit', type : 'apple' },
{ parent : 'fruit', type : 'kiwi' },
{ parent : 'vegetable', type : 'kale' },
{ parent : 'vegetable', type : 'cabbage' }
];
$scope.filtered = $scope.list;
$scope.selectedType = [];
$scope.selectedParent = [];
$scope.$watch(function () {
return {
selectedType: $scope.selectedType,
selectedParent: $scope.selectedParent,
}
}, function (value) {
var filterType = {
parent : $scope.selectedParent,
type : $scope.selectedType,
};
var startFilter = $scope.list;
for (var i in filterType) {
startFilter = filter(startFilter, filterType[i], i);
}
$scope.filtered = startFilter;
}, true);
Basically, if someone selects "fruit" and then "orange", but then unchecks "fruit", I would want "orange" to uncheck as well.
I just checked your plunker. The code on the bottom is very complicated, but I might be able to help you with these snippets.
Add a ng-change attribute to the parents:
<input type="checkbox"
checklist-model="selectedParent"
checklist-value="key"
data="{{::key}}"
ng-change="checkParent(key, checked)"/>
Now you can detect the changes in your controller:
$scope.checkParent = function(parent, checked) {
if (!checked) {
$scope.list.filter(function(fruit) {
return fruit.parent === parent;
}).forEach(function(fruit) {
$scope.selectedType = $scope.selectedType.filter(function(_selectedType) {
return _selectedType != fruit.type;
});
});
}
};
Plunkr
Beware, that this is inefficient, as it filters Selected type for every fruit to be unselected, it can be refactored with some nice functional tools.
But in general I'd change the controller if possible, and create a map with this structure:
{
parent: {
name: "fruit"
selected: false,
children: [{
type: "organge"
selected: false
}]
...
}
This way you can make your controller code much more readable.
Edit:
I was checking the two filter what you wrote. I couldn't come up with a better code as I still think that you should change the data structure. Iterating over and over lists is an expensive process, and both of your filters has two nested for loops. I cannot think of an easy way of getting rid of them with your data structure.
I spent some time on refactoring your code, getting rid of the watches and utilizing lodash. Check the updated Plunk, I hope it helps.
I added this function to your plunker:
$scope.uncheck = function(key){
$scope.selectedType.splice(key)
}
And this to the parent:
<input type="checkbox" checklist-model="selectedParent" checklist-value="key" data="{{::key}}" ng-change="uncheck(key)" />
It works for me if this is in fact what you are trying to accomplish.

Angular ng-repeat with constant & Restangular

I'm working with a weird data model (no way around it at this point). I'm using restangular to make a rest call to get back a single resource object
Normally, the resource object returned by restangular is just whatever I set my
$scope.resource = response to and I can do resource.name , resource.id in the view/template, etc..
Except this group of resources instead of returning the key, value pairs in the response object, it returns an object within an object like so
resource1: {name: 'value', stuff: 'value', etc}
which is fine because then I would just set my $scope.resource = response.resource1 in my controller
except the problem is, is that there's 5 different kind of resource object names so if I make a resource by id call I might get back resource2, resource4, resource1, etc. so setting my $scope.resource = response.resource1 would only work when I get resource1.
My first attempt to solve this was to just use ng-repeat in which I set
<ul ng-repeat="(resource, value) in resource">
<li class="list-group-item">
Name:
<span class="pull-right"> {{ resource.name }} </span>
</li>
</ul>
which works great except because restangular returns all this extra stuff it's looping through each object it's repeating a bunch of blank html stuff if that makes sense.
My other thought was to try making a constant and make an object that has all 5 resources there and my ng-repeat would only populate based off that constant object (ie: it would check for those strings "resource1, resource2, etc" and if it's there then it will populate the template. But I'm not exactly sure how to do this.
Any other options or are there ng-repeat features i'm just not utilizing? any Help thanks
Here's the example I will be working from. Initially your incoming data looks something like this I believe...
$scope.data = [
{
resource1 : { name: 'r1' }
},
{
resource2 : { name: 'r2' }
},
{
resource2 : { name: 'r2' }
}];
When you receive the data you can normalize it by flattening it out into the following structure...
$scope.normalized = [
{ name : 'r1' },
{ name : 'r2' },
{ name : 'r2' }
];
Or you can add a common field for the object "type"
$scope.expanded = [
{
type : 'resource1',
resource1 : { name: 'r1' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
}];
Or you can normalize but retain type data...
$scope.normalizedType = [
{ type : 'resource1', name : 'r1' },
{ type : 'resource2', name : 'r2' },
{ type : 'resource2', name : 'r2' }
];
Normalizing upon retrieval of the data is probably your best bet. The question then becomes do you need to retain the objects type information.
So another solution I came up with was put all the resource key names into a list
resources = ['resource1', 'resource2', 'resource3', 'etc..']
and in my restangular service promise I just checked for which resource number it would be with a for loop like this
return ResourceRestangular.all('resource').get(resourceId).then(function(response){
for (i = 0; i < resources.length ; i++){
if (resources[i] in response){
self.resource = response[resources[i]];
}
}
return self.resource;
No need for ng-repeat anymore!

Resources