angularjs - select with object of object as options - javascript

I'm currently trying to build a AngularJS app with a complex data structure.
The data source is an array of people with languages and skill level.
I need to filter those people by language skill, to do so I tried to build a select with the languages and another select with the skill levels, but i failed.
Here is a plnkr of my effords
Maybe there is also a simpler/better way to structure the data array ($scope.people)

Take a look at this
Working Demo
Html
<div ng-app='myApp' ng-controller="MainCtrl">LANGUAGES:
<select ng-model="selectLang" ng-options="lang as lang for lang in languages"></select>
<br>SKILL:
<select ng-model="selectSkill" ng-options="skill as skill for skill in skills"></select>
<br>
<button ng-click="getPeople()">Submit</button>
<br>PEOPLE:
<select ng-model="selectPeoples" ng-options="people as people for people in peoples"></select>
</div>
script
var app = angular.module('myApp', []);
app.controller('MainCtrl', function ($scope) {
$scope.people = [{
"name": "Jane Doe",
"gender": "Female",
"languages": [{
"lang": "German",
"skill": "Good"
}, {
"lang": "English",
"skill": "Very Good"
}]
}, {
"name": "John Doe",
"gender": "Male",
"languages": [{
"lang": "French",
"skill": "Good"
}, {
"lang": "English",
"skill": "Very Good"
}]
}];
$scope.languages = [];
$scope.skills = [];
angular.forEach($scope.people, function (peopleValue, peopleKey) {
angular.forEach(peopleValue.languages, function (langValue, langKey) {
$scope.languages.push(langValue.lang);
$scope.skills.push(langValue.skill);
});
});
$scope.languages = _.uniq($scope.languages);
$scope.skills = _.uniq($scope.skills);
$scope.getPeople = function () {
$scope.peoples = [];
angular.forEach($scope.people, function (peopleValue, peopleKey) {
angular.forEach(peopleValue.languages, function (langValue, langKey) {
if (langValue.lang === $scope.selectLang && langValue.skill === $scope.selectSkill) {
$scope.peoples.push(peopleValue.name);
}
});
});
}
});

Your problem is that you're not actually looping through each person's languages array in your ng-options directive. And I don't believe such a thing is actually possible given how your data is structured. I don't think you can loop through nested arrays (or at least I'm not aware of any ng-options syntax that would allow for such a thing.
So to make things easier, I would suggest doing the following in your controller:
$scope.langs = [];
angular.forEach($scope.people, function(person){
angular.forEach(person.languages, function(lang){
$scope.langs.push({
lang: lang.lang,
skill: lang.skill,
name: person.name,
gender: person.gender
});
});
});
This will give you an array that will allow you to filter using ng-options with the `orderBy' filter.

Related

Vue mapping or filtering array based on UI selections,

I have JSON data hundreds of entries like this:
{
"product":"Protec",
"type":"Central Opening",
"attribute":"Triple Lock",
"height":"2100",
"width":"1600",
"price":"3000"
},
{
"product":"Protec",
"type":"Sliding Door",
"attribute":"Single Lock",
"height":"2100",
"width":"1600",
"price":"3000"
},
{
"product":"ForceField",
"type":"Hinge Door",
"attribute":"Triple Lock",
"height":"2300",
"width":"1200",
"price":"100"
},
my vue component
var distinct_product = new Vue({
el: '#distinct',
data:{
distinct_product: [],
all_products: []
},
I fetch it and store it in my vue component and store it in a second data so when I render it to the ui the user only sees distinct elements.
mounted: async function(){
fetch("/Data/products.json")
.then(res => res.json())
.then(res => {
this.all_products = res
this.distinct_product = res
var disProduct = [...new Set(this.distinct_product.map(x => x.product))]
var disType = [...new Set(this.distinct_product.map(x => x.type))]
var disAttribute = [...new Set(this.distinct_product.map(x => x.attribute))]
this.distinct_product.productArray = disProduct;
this.distinct_product.typeArray = disType;
this.distinct_product.attributeArray = disAttribute;
My problem is, it also renders elements that aren't available to certain products.
for example a product : 'Window' can't have the attribute : 'triple locks'
I was wondering if I could filter/map the all_products array as the user selects a product.
I looked into computed properties mainly but I'm not sure of a good way to do it. this is my first attempt at a web app and I'm fairly new to JS too.
I aimed to iterate through the array pushing only objects containing the product selected in the UI
atm this is what I've attempted with no luck:
this.distinct_product.product which is bound to the UI
for (var i = 0; i < this.all_products.length; i++){
if (this.all_products[i] === this.distinct_product.product){
this.product.push(i);
return this.product;
}
}
so it would iterate over all_products looking for objects containing this.distinct_product.product which would contain 'Protec' or another product
Am I going at this the wrong way? should I step back in general and try and work with that data a different way?
Sorry if the question is structured poorly it's a skill I'm trying to work on, criticism is welcomed.
You are on the right track. I'll share a simple example so you can understand and make changes to your code accordingly.
var productdata = [
{
"product": "Protec",
"type": "Central Opening",
"attribute": "Triple Lock",
"height": "2100",
"width": "1600",
"price": "3000"
},
{
"product": "Protec",
"type": "Sliding Door",
"attribute": "Single Lock",
"height": "2100",
"width": "1600",
"price": "3000"
},
{
"product": "ForceField",
"type": "Hinge Door",
"attribute": "Triple Lock",
"height": "2300",
"width": "1200",
"price": "100"
},
];
//setTimeout(function () {
distinct_productVue = new Vue({
el: '#distinct',
data: {
//selected: {},
distinct_products: [],
all_products: productdata.map(function (x, index) {
return { text: x.product, value: index + 1 };
}),
selected: '0'
},
computed: {
},
mounted: function () {
this.all_products.unshift({ text: 'Please select a product', value: 0 });
},
methods: {
getDistinctProduct: function () {
var self = this;
self.distinct_products = productdata.filter(function (x, index) {
if (x.product === self.all_products[self.selected].text) {
return { text: x.product, value: index };
}
else { return false; }
});
}
}
});
<html>
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.0/vue.min.js'></script>
</head>
<body>
<div id="distinct">
<select v-model="selected" v-on:change="getDistinctProduct">
<option v-for="option in all_products" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<!--<span>Selected: {{ selected }}</span>-->
<div v-show="selected != 0" style="margin-top:15px;">
<b>Available products</b>
<div v-for="pro in distinct_products" style="margin-top:15px;">
<div>product: {{pro.product}}</div>
<div>type: {{pro.type}}</div>
<div>attribute: {{pro.attribute}}</div>
<div>height: {{pro.height}}</div>
<div>width: {{pro.width}}</div>
<div>price: {{pro.price}}</div>
</div>
</div>
</div>
</body>
</html>

Retrieving Data from Firebase in Specific Array Format

I am new to both Angular and Firebase. Still trying to learn the ropes so I am hoping really bad that someone can help me out. I have tried looking through documentations and watching videos but honestly, I still don't get it.
I was able to retrieve my data from firebase using Angular's HTTPCLIENT service. However, it is in the below format:
{
"test01": {
"title": "News",
"descr": "Amazing!"
}
},
"test02": {
"title": "Panda",
"descr": "Amazing!"
}
}
But the problem is that I want to use ngFor to loop through my data so that it will display all the titles and descriptions (regardless of the id) in my homepage. Is there anyway to restructure the data I retrieved into the below format instead so I can loop through it and display the data as it is?
favItems = [{id: "test01", title: "News", descr: "Amazing!" }, {id: "test02", title....}]
Thank you!
You can use Object.keys - docs:
let obj = {
"test01": {
"title": "News",
"descr": "Amazing!"
},
"test02": {
"title": "Panda",
"descr": "Amazing!"
}
}
let arr = Object.keys(obj).map((k) => {
return { ...obj[k],
id: k
}
})
console.log(arr)

Angularjs Load "$ scope" in a table based on a select

I try to view the "$ scope" in the table to be displayed according to the select chosen. Keeping the default selection in the select.
The $scope that are in the json just a server. example : " status " : $ scope.DC01_GF1_STATUS "
Here is modeled : https://plnkr.co/edit/CaWNeIDHe2nFyEgDABvg?p=preview
$scope.templates = {"webapp": {
"Dc01": [
{
"name": "Groupe Froid 1A",
"value": "gf1a",
"data": {
"status": $scope.DC01_GF1_STATUS,
"capacite": $scope.DC01_GF1_CAP_T
},
},
{
"name": "Groupe Froid 3A",
"value": "gf3a",
"data": {
"status": $scope.DC01_GF3_STATUS,
"capacite": $scope.DC01_GF3_CAP_T
},
}]
}
};
Thank you
You are missing some data. You'll need to import it.
Here's an example: https://plnkr.co/edit/NPfRM0
relevant code:
// loading dummy status codes:
$scope.DC01_GF1_STATUS = 'status GF1'
$scope.DC02_GF1_STATUS = 'status GF2'
$scope.DC01_GF1_CAP_T = 10
$scope.DC02_GF1_CAP_T = 5
...
$scope.testBat = function(arg) {
...
$scope.capacite = $scope.templates.webapp[arg][0].data.capacite;
$scope.status = $scope.templates.webapp[arg][0].data.status;
...
}
index.html change
<p class="gfInterface">{{capacite}} %</p>
the problem comes from the declaration of the "$scope". The problem comes when I select a title in the "select" for example "Groupe Froid 3A" values "$scope" name, capacite and status does not change. Because the $scope of such name is $scope.name = $scope.templates.webapp[building][0].name; but for the selection "Groupe Froid 3A" the $scope should be $scope.name = $scope.templates.webapp[building][1].name;. And it is like for the rest. I would have come to retrieve the id or the value of intitullé selected to generate the $scope.name example. As for building variable $scope.templates.webapp[building][number].name. The variable retrieves the id of the selection.

How to write array which collects ng-click event in AngularJs?

I am working with a project, where I need to collect multiple items from user and send it to the server. There is list on my view, where user can click and select the items. My HTML looks like this,
HTML
<div ng-repeat="topicList in searchCtrl.topic">
<div ng-repeat="topicTerm in topicList">
<p>{{topicTerm.number}}&nbsp&nbsp{{topicTerm.name}}</p>
<div ng-repeat="subTopic in topicTerm.subTopics">
{{subTopic.number}}&nbsp&nbsp{{subTopic.name}}
</div>
</div>
</div>
I have used anchor tag, there user can click and at the same time I want the clicked items (which have also unique ID) collected in an Array or variable, which I need to send (these selected items) to the server via form submission.
This is how my controller looks like,
JavaScript Controller
angular.module('myApp').controller("searchController", function($log, searchService, $scope){
var self = this;
self.initializeSearch = function(){
self.searchEntry =
{
"contact":{
"person": "",
"organization": ""
},
"request": {
"input": "",
"language": "en"
},
"topicIds": []
};
// The POST request must looks like above
What I want is that the clicked subTopics IDs collects in an Array "topicIds : []" and I could successfully send the POST request mentioned above. The searchService is a Angular service which helps to get Topics from server and also to POST user input to the server.
This is how my JSON looks like,
JSON API
{
"TopicList" :[
{
"id": "798790fa-78c8-4f00-8179-9e70f40adb14",
"name": "Topic1",
"number": 1.0,
"subTopics": [
{
"id": "82c90f2e-deac-4fa4-80f4-d077edacc2dc",
"name": "data1.1",
"number": 1.1
},
{
"id": "0f0c2b89-6dae-4f60-90f8-df49d96b9af9",
"name": "data1.2",
"number": 1.2
},
{
"id": "131b68b6-1f45-477f-9b0f-8ac80c5b4f4e",
"name": "data1.3",
"number": 1.3
},
{
"id": "16c8f46d-d20c-48f9-a0c0-e3989763082b",
"name": "data1.4",
"number": 1.4
}
]
},
{
"id": "9ed3fee0-5347-4f00-9b56-721b61439f88",
"name": "Topic2",
"number": 2.0,
"subTopics": [
{
"id": "eec13511-1408-4f4b-be6f-8b5a8b6ea28b",
"name": "data2.1",
"number": 2.1
},
...
]
},
...
]
}
How to write a function or array which collects the IDs via ng-click event?
Thanks in Advance.
No need to use an $event, simple pass the subTopic.id, or whatever, in your ng-click, like ng-click="searchCtrl.select(subTopic)"
And then in your controller, you could have:
angular.module('myApp').controller("searchController", function($log, searchService, $scope){
var self = this;
var subTopicIds = []; // array to hold subTopicIds
self.select = function(subTopic) {
subTopicIds.push(subTopic.id);
}
self.initializeSearch = function(){
self.searchEntry =
{
"contact":{
"person": "",
"organization": ""
},
"request": {
"input": "",
"language": "en"
},
"topicIds": subTopicIds // use the object created previously
};
...
You can get an ID in angular like this.
<div ng-click="recordClick($event)">Click</div>
That will feed the click event into the recordClick method, where you can then call it's target property (i.e. the div it was invoked on) and push it in the array.
$scope.clickArray = [];
$scope.recordClick = function(event){
clickArray.push(event.target);
}
I solved this problem by passing subTopics ID in ng-click as a parameter. And as per the requirement I need to call also another event while user click, which I passed as a second argument. So, now both the events works as I wanted via single ng-click.
Here is my updated code,
HTML
<div ng-repeat="topicList in searchCtrl.topic">
<div ng-repeat="topicTerm in topicList">
<p>{{topicTerm.number}}&nbsp&nbsp{{topicTerm.name}}</p>
<div ng-repeat="subTopic in topicTerm.subTopics">
{{subTopic.number}}&nbsp&nbsp{{subTopic.name}}
</div>
</div>
</div>
And here is my controller,
Controller
angular.module('myApp').controller("searchController", function($log, searchService, $scope){
var self = this;
var subTopicIDs = [];
self.select = function(TopicIDs, event){
subTopicIDs.push(TopicIDs);
$(event.target).addClass('selor'); // This is class which changes the background color of the clicked item
console.log(TopicIDs);
}
self.initializeSearch = function(){
self.searchEntry =
{
"contact":{
"person": "",
"organization": ""
},
"request": {
"input": "",
"language": "en"
},
"topicIds": subTopicIDs
};
This is how it solved my problem.
Btw, Thank you Tom and OceansOnPluto.

Complex JSON nesting of objects and arrays

I am having difficultly with syntax and structure of JSON objects/arrays.
{
"accounting" : [
{ "firstName" : "John",
"lastName" : "Doe",
"age" : 23 },
{ "firstName" : "Mary",
"lastName" : "Smith",
"age" : 32 }
],
"sales" : [
{ "firstName" : "Sally",
"lastName" : "Green",
"age" : 27 },
{ "firstName" : "Jim",
"lastName" : "Galley",
"age" : 41 }
]
}
I want to make a nested structure of objects and arrays that would house the following info:
{
"problems": [{
"Diabetes":[{
"medications":[{
"medicationsClasses":[{
"className":[{
"associatedDrug":[{
"name":"asprin",
"dose":"",
"strength":"500 mg"
}],
"associatedDrug#2":[{
"name":"somethingElse",
"dose":"",
"strength":"500 mg"
}]
}],
"className2":[{
"associatedDrug":[{
"name":"asprin",
"dose":"",
"strength":"500 mg"
}],
"associatedDrug#2":[{
"name":"somethingElse",
"dose":"",
"strength":"500 mg"
}]
}]
}]
}],
"labs":[{
"missing_field": "missing_value"
}]
}],
"Asthma":[{}]
}]}
But I have no idea what the right way to do this should be. Should I just be making JavaScript objects? Does JSON make sense for this project?
What is the correct syntax to set something like this up?
Here is my code so far:
$(document).ready(function() {
$.getJSON('js/orders.json', function(json) {
$.each(json.problems, function(index, order) {
$('.loadMeds').append('<p>' + order.name + '</p>')
});
});
});
The first code is an example of Javascript code, which is similar, however not JSON. JSON would not have 1) comments and 2) the var keyword
You don't have any comments in your JSON, but you should remove the var and start like this:
orders: {
The [{}] notation means "object in an array" and is not what you need everywhere. It is not an error, but it's too complicated for some purposes. AssociatedDrug should work well as an object:
"associatedDrug": {
"name":"asprin",
"dose":"",
"strength":"500 mg"
}
Also, the empty object labs should be filled with something.
Other than that, your code is okay. You can either paste it into javascript, or use the JSON.parse() method, or any other parsing method (please don't use eval)
Update 2 answered:
obj.problems[0].Diabetes[0].medications[0].medicationsClasses[0].className[0].associatedDrug[0].name
returns 'aspirin'. It is however better suited for foreaches everywhere
I successfully solved my problem. Here is my code:
The complex JSON object:
{
"medications":[{
"aceInhibitors":[{
"name":"lisinopril",
"strength":"10 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}],
"antianginal":[{
"name":"nitroglycerin",
"strength":"0.4 mg Sublingual Tab",
"dose":"1 tab",
"route":"SL",
"sig":"q15min PRN",
"pillCount":"#30",
"refills":"Refill 1"
}],
"anticoagulants":[{
"name":"warfarin sodium",
"strength":"3 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}],
"betaBlocker":[{
"name":"metoprolol tartrate",
"strength":"25 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}],
"diuretic":[{
"name":"furosemide",
"strength":"40 mg Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}],
"mineral":[{
"name":"potassium chloride ER",
"strength":"10 mEq Tab",
"dose":"1 tab",
"route":"PO",
"sig":"daily",
"pillCount":"#90",
"refills":"Refill 3"
}]
}
],
"labs":[{
"name":"Arterial Blood Gas",
"time":"Today",
"location":"Main Hospital Lab"
},
{
"name":"BMP",
"time":"Today",
"location":"Primary Care Clinic"
},
{
"name":"BNP",
"time":"3 Weeks",
"location":"Primary Care Clinic"
},
{
"name":"BUN",
"time":"1 Year",
"location":"Primary Care Clinic"
},
{
"name":"Cardiac Enzymes",
"time":"Today",
"location":"Primary Care Clinic"
},
{
"name":"CBC",
"time":"1 Year",
"location":"Primary Care Clinic"
},
{
"name":"Creatinine",
"time":"1 Year",
"location":"Main Hospital Lab"
},
{
"name":"Electrolyte Panel",
"time":"1 Year",
"location":"Primary Care Clinic"
},
{
"name":"Glucose",
"time":"1 Year",
"location":"Main Hospital Lab"
},
{
"name":"PT/INR",
"time":"3 Weeks",
"location":"Primary Care Clinic"
},
{
"name":"PTT",
"time":"3 Weeks",
"location":"Coumadin Clinic"
},
{
"name":"TSH",
"time":"1 Year",
"location":"Primary Care Clinic"
}
],
"imaging":[{
"name":"Chest X-Ray",
"time":"Today",
"location":"Main Hospital Radiology"
},
{
"name":"Chest X-Ray",
"time":"Today",
"location":"Main Hospital Radiology"
},
{
"name":"Chest X-Ray",
"time":"Today",
"location":"Main Hospital Radiology"
}
]
}
The jQuery code to grab the data and display it on my webpage:
$(document).ready(function() {
var items = [];
$.getJSON('labOrders.json', function(json) {
$.each(json.medications, function(index, orders) {
$.each(this, function() {
$.each(this, function() {
items.push('<div class="row">'+this.name+"\t"+this.strength+"\t"+this.dose+"\t"+this.route+"\t"+this.sig+"\t"+this.pillCount+"\t"+this.refills+'</div>'+"\n");
});
});
});
$('<div>', {
"class":'loaded',
html:items.join('')
}).appendTo("body");
});
});
Make sure you follow the language definition for JSON. In your second example, the section:
"labs":[{
""
}]
Is invalid since an object must be composed of zero or more key-value pairs "a" : "b", where "b" may be any valid value. Some parsers may automatically interpret { "" } to be { "" : null }, but this is not a clearly defined case.
Also, you are using a nested array of objects [{}] quite a bit. I would only do this if:
There is no good "identifier" string for each object in the array.
There is some clear reason for having an array over a key-value for that entry.
First, choosing a data structure(xml,json,yaml) usually includes only a readability/size problem. For example
Json is very compact, but no human being can read it easily, very hard do debug,
Xml is very large, but everyone can easily read/debug it,
Yaml is in between Xml and json.
But if you want to work with Javascript heavily and/or your software makes a lot of data transfer between browser-server, you should use Json, because it is pure javascript and very compact. But don't try to write it in a string, use libraries to generate the code you needed from an object.
Hope this helps.
You can try use this function to find any object in nested nested array of arrays of kings.
Example
function findTByKeyValue (element, target){
var found = true;
for(var key in target) {
if (!element.hasOwnProperty(key) || element[key] !== target[key]) {
found = false;
break;
}
}
if(found) {
return element;
}
if(typeof(element) !== "object") {
return false;
}
for(var index in element) {
var result = findTByKeyValue(element[index],target);
if(result) {
return result;
}
}
};
findTByKeyValue(problems,{"name":"somethingElse","strength":"500 mg"}) =====> result equal to object associatedDrug#2

Categories