Populate HTML template with $http response VueJS - javascript

I am new to VueJs and working on a small nutrition app. Currently, we want to make food recs based on certain nutrients.
The JS is as follows:
recommendFood: function() {
this.recs = {};
var _this = this;
var getItem = function(ndbno,i,nid) {
_this.$http.get('http://127.0.0.1:3000/item?ndbno=' + ndbno).then(function(response) {
var someData = response.body;
var nutrients = someData.report.food.nutrients;
var item = someData.report.food;
item = this.addNutrientsToItem(item, nutrients);
this.recs[nid].push(item);
});
};
for (var i=0; i<this.low_nutrients.length; i++) {
this.low_nutrients[i].recs = [];
this.recs[this.low_nutrients[i].id] = [];
for (var k=0; k<this.low_nutrients[i].food_map.length; k++) {
var ndbno = this.low_nutrients[i].food_map[k];
getItem(ndbno,i,this.low_nutrients[i].id);
}
}
console.log(this.recs)
}
I want this.recs to be an object with attributes that are equivalent to a nutrient id (that we store). Each nutrient has a food_map array attached to the object that contains id's of foods that would be the recommendations. I need to send those id's (ndbno) to the http request to receive the object of that food recommendation (item).
The this.recs object actually populates correctly (despite there probably being a better way to write my code), however since it's waiting on the loop and promise, the html renders before the object is complete. Therefore, my html is blank. How can I display the recs on the html once they are updated on the promise result?
Here is my HTML:
<div v-for="(nutrient, idx) in low_nutrients">
<h2>{{nutrient.name}}</h2>
<div>Recommended Foods:</div>
<div>
<div>Recs:</div>
<div v-for="rec in recs[nutrient.id]">{{rec}}</div>
</div>
</div>
The desired object this.recs should look something like this (and it does show this in the console):
this.recs = {
291: [{},{},{}],
316: [{},{},{}]
}

The problem is that this.recs starts out empty. Vue cannot detect property additions, so you have to use set to tell it that new properties have been added (and to make those properties reactive).
Or you can re-assign a new object to this.recs rather than modifying its contents.

Related

Fill javascript object with form data

I have an object declared, and I have an html form with some matching fields.
All fields in the form are in the object, but the object also has a couple of additional variables and functions.
I'd like to fill the object with the data entered in the form, what I'm trying right now overwrites the declared object, and so doesn't have the functions nor the other variables.
The object :
var Container = {
nodes: [],
Contains: function (Node) {
for (var i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].nodeID === Node.nodeID)
return (i);
}
return (-1);
}
How I fill it from the form :
const handleContainerForm = event => {
event.preventDefault();
ContainerFormToJSON(form.elements);
var i = JSONData.Contains(Container);
if (i === -1)
JSONData.containers.push(Container);
else
JSONData.container[i] = Container;
output = JSON.stringify(JSONData, null, " ");
displayContents(output);
};
The form has ID, Title, Folder, Image and Description as fields, so this last Container object doesn't have the Contains() function nor the nodes[] array.
How do I end up with a complete, filled version of the object I have declared ?
In ContainerFormToJSON function, before the statement
return Container
define:
//container.nodes and container.contains
You are right, JavaScript is very different from C#, especially in regards to OOP. But that doesn't make it better or worse.
In JavaScript, you don't need to declare an object's properties, like you have to when you use classes. I think that you only want to serialize the form's input values to JSON. I recommend not to use an object that additionally has a nodes property and a Contains method.
If you need to keep a copy of the unserialized object, create two objects:
class Container {
constructor () {
this.nodes = [];
}
indexOf (node) {
return this.nodes.findIndex(n => n.nodeID === node.nodeID);
}
}
Container.nodeID = 0; // think of it as a static property
function extractValues (elements) {
// 'elements' is an array of <input> elements
// the 'container' will be filled and serialized
var container = new Container();
for (var index in elements) {
var element = elements[index];
container[element.name] = element.value;
}
container.nodeID = Container.nodeID++; // give the container a unique ID
return container;
}
var inputs = document.querySelectorAll('input');
var jsonData = new Container();
document.querySelector('button').addEventListener('click', function () {
var newContainer = extractValues(inputs);
var index = jsonData.indexOf(newContainer);
if (index === -1) {
jsonData.nodes.push(newContainer);
} else {
jsonData.nodes[index] = newContainer;
}
var jsonString = JSON.stringify(jsonData, null, ' ');
console.log(jsonString);
});
<input name="containerID">
<input name="containerTitle">
<!-- ... -->
<button>Serialize</button>
Please note: only setting an object's properties doesn't make it to JSON. It's only JSON if it's serialized to a string. I recommend this article. To serialize a JavaScript object, use JSON.stringify.
Edit:
Looking at the edit of your question, I think it might be preferable to create a Container class. Both jsonData and the containers of the form data will be instances of that class. It can contain other containers (nodes), and can get the index of such a nested container using the indexOf method. I implemented this in the above code snippet. Whenever you hit the "Serialize" button, a new container with the current <input>s' contents will be added to jsonData. The JSON form of jsonData will be logged to the console.
I hope this is what you are looking for. To better understand JavaScript OOP,
take a look at some of the articles at MDN.

Underscore.js `filter` not working

I have the following code snippet I use to choose which suburbs from a list the user has selected (with irrelevant code omitted):
var allSuburbsList = new Array([{"SuburbID":1,"SuburbAreaID":3,"SuburbName":"Alberante","SuburbActive":true,"Area":null,"Agents":[]},{"SuburbID":4,"SuburbAreaID":3,"SuburbName":"Alberton North","SuburbActive":true,"Area":null,"Agents":[]}]);
var a3burbs = _.filter(allSuburbsList, function(s) { return s.SuburbAreaID === 3; });
// 3 is a test value. All the test suburbs so far fall under area no. 3.
With this filter, a3burbs comes out as an empty array, []. If I cheat and make the filter:
var a3burbs = _.filter(allSuburbsList, function(s) { return true; });
then a3bubrs comes out an exact copy of allSuburbsList, with all suburbs included. What could I be doing wrong? I'm using the same syntax as indicated on the Underscore.js home page.
Btw, would the way I populate allSuburbsList from a viewmodel array property have anything to do with this:
var allSuburbsList = new Array(#Html.Raw(JsonConvert.SerializeObject(Model.AllSuburbs)));
Just for interest, my first attempt was the hideous code below, but it worked:
var a3burbs = [];
#{for (var i = 0; i < Model.AllSuburbs.Length; i++) {
#:if (allSuburbsList[#i].SuburbAreaID === 3) {
#:a3burbs.push(allSuburbsList[#i]);
};
};
You're creating a new array and passing in an array.
Change it to
var allSuburbsList = [{"SuburbID":1,"SuburbAreaID":3,"SuburbName":"Alberante","SuburbActive":true,"Area":null,"Agents":[]},{"SuburbID":4,"SuburbAreaID":3,"SuburbName":"Alberton North","SuburbActive":true,"Area":null,"Agents":[]}];
Using new Array([{}]), you are creating an array inside another array. Just instantiate that without new Array()

Numerically ordered ID's of data objects in Firebase

I am pretty new to the 'game' and was wondering if it's possible to order newly added data (through a form and inputs) to the Firebase numerically so each new data entry gets the ID (number of the last added data +1).
To make it more clear, underneath you can find a screenshot of how data is currently being added right now. The datapoint 0-7 are existing (JSON imported data) and the ones with the randomly created ID belong to new entries. I would like to have the entries to comply to the numbering inside of my Firebase, because otherwise my D3 bar chart won't be visualised.
var firebaseData = new Firebase("https://assignment5.firebaseio.com");
function funct1(evt)
{
var gameName = $('#nameInput').val();
var medalCount = $('#medalsInput').val();
var bool = $('#boolInput').is(':checked');
firebaseData.push().set({games: gameName, medals: medalCount, summergames: bool});
evt.preventDefault();
}
var submit = document.getElementsByTagName('button')[0];
submit.onclick = funct1;
UPDATE:
function funct1(evt)
{
var gameName = $('#nameInput').val();
var medalCount = $('#medalsInput').val();
var bool = $('#boolInput').is(':checked');
for (var i = 0; i < 10; i++)
{
firebaseData.child('7' + i).set({games: gameName, medals: medalCount, summergames: bool}(i)); };
Problem:
There are two ways to generate ids for your document nodes.
Calling .push() on your reference will generate that unique id.
Calling .set() on your reference will allow you to use your own
id.
Right now you're using .push().set({}), so push will generate an new id and the set will simply set the data.
// These two methods are equivalent
listRef.push().set({user_id: 'wilma', text: 'Hello'});
listRef.push({user_id: 'wilma', text: 'Hello'});
Using .set() without .push() will allow you to control your own id.
Using .push():
When managing lists of data in Firebase, it's important to use unique generated IDs since the data is updating in real time. If integer ids are being used data can be easily overwritten.
Just because you have an unique id, doesn't mean you can't query through your data by your ids. You can loop through a parent reference and get all of the child references as well.
var listRef = new Firebase('https://YOUR-FIREBASE.firebaseio.com/items');
// constructor for item
function Item(id) {
this.id = id;
};
// add the items to firebase
for (var i = 0; i < 10; i++) {
listRef.push(new Item(i));
};
// This will generate the following structure
// - items
// - LGAJlkejagae
// - id: 0
// now we can loop through all of the items
listRef.once('value', function (snapshot) {
snapshot.forEach(function (childSnapshot) {
var name = childSnapshot.name();
var childData = childSnapshot.val();
console.log(name); // unique id
console.log(childData); // actual data
console.log(childData.id); // this is the id you're looking for
});
});
Within the childData variable you can access your data such as the id you want.
Using .set()
If you want to manage your own ids you can use set, but you need to change the child reference as you add items.
for (var i = 0; i < 10; i++) {
// Now this will create an item with the id number
// ex: https://YOUR-FIREBASE.firebaseio.com/items/1
listRef.child('/' + i).set(new Item(i));
};
// The above loop with create the following structure.
// - items
// - 0
// - id: 0
To get the data you can use the same method above to loop through all of the child items in the node.
So which one to use?
Use .push() when you don't want your data to be easily overwritten.
Use .set() when your id is really, really important to you and you don't care about your data being easily overwritten.
EDIT
The problem you're having is that you need to know the total amount of items in the list. This feature is not implemented in Firebase so you'll need to load the data and grab the number of items. I'd recommend doing this when the page loads and caching that count if you really desire to maintain that id structure. This will cause performance issues.
However, if you know what you need to index off of, or don't care to overwrite your index I wouldn't load the data from firebase.
In your case your code would look something like this:
// this variable will store all your data, try to not put it in global scope
var firebaseData = new Firebase('your-firebase-url/data');
var allData = null;
// if you don't need to load the data then just use this variable to increment
var allDataCount = 0;
// be wary since this is an async call, it may not be available for your
// function below. Look into using a deferred instead.
firebaseData.once('value', function(snapshot) {
allData = snapshot.val();
allDataCount = snapshot.numChildren(); // this is the index to increment off of
});
// assuming this is some click event that adds the data it should
function funct1(evt) {
var gameName = $('#nameInput').val();
var medalCount = $('#medalsInput').val();
var bool = $('#boolInput').is(':checked');
firebaseData.child('/' + allDataCount).set({
games: gameName,
medals: medalCount,
summergames: bool
});
allDataCount += 1; // increment since we still don't have the reference
};
For more information about managing lists in Firebase, there's a good article in the Firebase API Docs. https://www.firebase.com/docs/managing-lists.html

arranging elements in to a hash array

I am trying to break a javascript object in to small array so that I can easily access the innerlevel data whenever I needed.
I have used recursive function to access all nodes inside json, using the program
http://jsfiddle.net/SvMUN/1/
What I am trying to do here is that I want to store these in to a separate array so that I cn access it like
newArray.Microsoft= MSFT, Microsoft;
newArray.Intel Corp=(INTC, Fortune 500);
newArray.Japan=Japan
newArray.Bernanke=Bernanke;
Depth of each array are different, so the ones with single level can use the same name like I ve shown in the example Bernanke. Is it possible to do it this way?
No, you reduce the Facets to a string named html - but you want an object.
function generateList(facets) {
var map = {};
(function recurse(arr) {
var join = [];
for (var i=0; i<arr.length; i++) {
var current = arr[i].term; // every object must have one!
current = current.replace(/ /g, "_");
join.push(current); // only on lowest level?
if (current in arr[i])
map[current] = recurse(arr[i][current]);
}
return join;
})(facets)
return map;
}
Demo on jsfiddle.net
To get the one-level-data, you could just add this else-statement after the if:
else
map[current] = [ current ]; // create Array manually
Altough I don't think the result (demo) makes much sense then.

Javascript and HTML data model and presentation model design question

so I've been working on a project in Javascript that takes in objects the user provides and represents them in HTML. Right now they are represented in memory as an array, and in the display as a separate array. After integrating some code changes, problems have arisen in that the display array seems to be having troubles removing it's contents, thus things that should be removed don't disappear from the view.
Declaring lists:
this.divList = gDocument.getElementById( element );
this.objectList = [];
Adding an object to the lists:
addObject = function (address, type){
var newDiv = gDocument.createElement("div");
this.divList.appendChild( newDiv );
var d = this.createObject( newDiv, address, type );
if (undefined != d)
{
this.objectList.push(d);
}
}
The divList accurately reflects the objectList until any changes are made to the objectList at runtime. When restarted, the lists are in sync once again. When I tried to fix it, things were very complicated. I'm wondering if there is a better way to design such an idea (the object model and the graphical representation). Any comments would be helpful, thanks.
Question vagueness aside, my recommendation would be to store one list, not two, in memory. Each list element is an object with all the necessary data you need for that particular abstract "object" (the ones that "the user provides"). Something like this:
this.divList = gDocument.getElementById(element);
this.masterList = [];
var i,
len = this.divList.length;
for (i = 0; i<len; i++)
{
this.masterList.push({
elt: this.divList[i],
obj: /* however you'd create the object in this.objectList */
});
}
Edit: your addObject function would be changed to something like this:
addObject = function (address, type)
{
var newDiv = gDocument.createElement("div"),
newObj = {elt: newDiv,
obj: this.createObject(newDiv, address, type)};
this.masterList.push(newObj);
this.divList.appendChild(newDiv);
}
You should store a reference to the HTML element that you're appendChild()ing to. You're already doing this - but when you need to manipulate the individual elements (say, remove one), use the masterList instead:
removeObject = function (i)
{
var toRemove = this.masterList.splice(i, 1);
if (toRemove)
{
this.divList.removeChild(toRemove.elt);
}
}
See also Array.splice().

Categories

Resources