Alright, so I'm making a recursive list in AngularJS using ng-include and ng-repeat, something like this:
<div id="wrapper">
<div id="text" ng-click="DivClick()">
<ul>
<li id="{{$index}}" ng-repeat="data in layer" ng-include="'recursion.html'"></li>
</ul>
</div>
<script type="text/ng-template" id="recursion.html">
<textarea myd-keypress cols="63" rows="1">{{data.content}}</textarea>
<input type="text" myd-numbers value="{{data.price}}"></input>
<ul>
<li ng-repeat="data in data.layer" ng-include="'recursion.html'" id="{{$parent.$attr('id') + '-' + $index}}"></li>
</ul>
But of course, it doesn't work. So basically what I need is that every < li> element in dom has id that corresponds like this:
0
1
1-0
1-1
1-1-0
1-2
1-3
2
2-0
so that every new < li> has id that equals "id of its parent" + "-$index".
What I know that will work is that scope.layer[] and its layer descendants contain a field that will save ID of the < li> and display it, but I want to avoid saving that extra field in the DB because solution to this is really simple (as shown in the example), but I can't get over syntax. Any suggestions?
Before passing data to $scope.label you could loop over it with function that recursively loops branches and adds an id property
var data = [{
name: 'foo',
children: [{
name: 'foo child',
children: [{
name: 'foo child grandchild'
}, {
name: 'foo child grandchild2'
}]
}, {
name: 'foo child2'
}]
}, {
name: 'bar'
}]
function createID(arr, parentID) {
for (var i = 0; i < arr.length; i++) {
var id = (i + 1).toString();
var thisID = parentID ? parentID + '-' + id : id;
arr[i].id = thisID;
if (arr[i].children) {
createID(arr[i].children, thisID)
}
}
}
createID(data, null)
DEMO http://jsfiddle.net/z4RPz/
What I think you're wanting to do is reach up and grab the $index from the parent element.
You can do that using $parent.$index - or in this case, it may be $parent.$parent.$index, as you're using ng-repeat -> ng-include -> ng-repeat
See this answer for more information on a similar situation:
https://stackoverflow.com/a/15257501/317180
Related
I am trying to create a cards game but I am stuck at the point of returning cards with MongoDB: player has 30 cards, when clicking on a card the card does an animation with translate and rotate to reveal the value of the card , and since I only need to reveal the value of 3 cards, I need them to end up in the order in which they were chosen, but when I choose them, it returns them to me sorted by the value that the card has; so, for example, if I choose 1, 2, 3; no problem, but if I choose 2, 1, 3; it returns 1, 2, 3.
I've tried sort() and it doesn't work, because as I said I need it to return them in the chosen order, neither ascending nor descending (anyway it is sorting the cards without sort). I have tried with Express Handlebars, but apparently it creates an array, so when I put for example cards.[0].number; in the case of 2,1,3 it still returns 1 and not 2.
This is my code:
router.post('/cards', (req, res) =>{
let zahl1 = req.body.zahl1;
let zahl2 = req.body.zahl2;
let zahl3 = req.body.zahl3;
cards.find({"zahl": {$in:[zahl1, zahl2, zahl3]}}, { _id: 0}, function(err, cards) {
return res.render('cardsGame', {
cards: cards
});
});
});
Since I am having this issue I am working with a simple HTML to find out how to solve this issue:
</form>
<form method="post" action="/cards" id="bilden">
<input type="text" id="hallo1" class="w-25">
<input type="text" id="hallo2" class="w-25">
<input type="text" id="hallo3" class="w-25">
<a id="funktioniert" onclick="hinrichten()"><input type="submit" value="cards" class="btn btn-primary"></a>
<input type="text" name="zahl1" id="zahl1" target="zahl1" class="w-25">
<input type="text" name="zahl2" id="zahl2" target="zahl2" class="w-25">
<input type="text" name="zahl3" id="zahl3" target="zahl3" class="w-25">
</form>
<script>
let newTextInput1 = document.getElementById('hallo1');
let newTextInput2 = document.getElementById('hallo2');
let newTextInput3 = document.getElementById('hallo3');
let newAncla = document.getElementById('funktioniert');
let inputResult1 = document.getElementById('zahl1');
let inputResult2 = document.getElementById('zahl2');
let inputResult3 = document.getElementById('zahl3');
function hinrichten(){
inputResult1.value = newTextInput1.value;
inputResult2.value = newTextInput2.value;
inputResult3.value = newTextInput3.value;
}
</script>
Can someone help me find a way to do this? Thanks!
A single MongoDB node returns documents in the order they are encountered. If an index is used to optimize the query, the documents will be encountered in the sorted order of the index.
If you need documents in the order they were inserted into the database, you could hint the $natural index, but that would mean every such query would be a collection scan.
To get them in the order they appear in the request, you will need to sort them on the client side.
Perhaps something like:
router.post('/cards', (req, res) =>{
let zahl1 = req.body.zahl1;
let zahl2 = req.body.zahl2;
let zahl3 = req.body.zahl3;
let zahls = [zahl1, zahl2, zahl3];
cards.find({"zahl": {$in:zahls}}, { _id: 0}, function(err, cards) {
let cardsSort = cards.sort(function(a,b){
aidx = zahls.indexOf(a.zahl);
bidx = zahls.indexOf(b.zahl);
return (aidx - bidx);
});
return res.render('cardsGame', {
cards: cardsSort
});
});
});
I am having trouble displaying data much like what is described here, but my object is not conveniently labelled in numbers: How do I iterate over an unknown object in a Meteor Spacebars template?
Data maybe be nested more than once, and might differ but always end in an array.
I am open to new data structures too.
Sample Data:
{
"Category A":
{
"Sub-Category1":
{
"Sub-Sub-Category1": ['Value1', 'Value2']
},
"Sub-Category2":
{
"Sub-Sub-Category2": ['Value3'],
"Sub-Sub-Category3": ['Value4']
}
},
"Category B":
{
"Sub-Category1": ['Value5']
}
}
You're going to need a recursive template to handle arbitrary nesting, a helper that enumerates the keys of an object, and a helper that gets the value of the parent object corresponding to a key.
html:
<template name="nest">
{{#if isArray}}
{{#each this}}
{{this}} <!-- we're assuming that array elements aren't themselves objects -->
{{/each}}
{{#elseif isObject}}
{{#each keys}}
{{#with value}}
{{> nest }}
{{/with}}
{{/each}}
{{else}} <!-- catch the case of a scalar key value (no array) -->
{{this}}
{{/if}}
</template>
js:
Template.nest.helpers({
isArray(){
return typeof(this) === "object" && this.length && this.length > 0;
},
isObject(){
return typeOf(this) === "object" && !this.length;
},
keys(){
return Object.keys(this); // returns an array of strings
},
value(){
return Template.parentData()[this]; // look up the hierarchy to get the parent object then select the data for the current key
}
});
I have a collection with this item:
{
"item1": ["foo", "bar", "baz"],
"item2": ...
}
I made a helper function to repeat a template for each item in item1
Template.temp.helpers({
items: function() {
return Col.find({},{item1:1});
}
});
and this is the template
<template name="temp">
{{#each items}}
{{> anotherTemplate}}
{{/each}}
</template>
But I get back an empty array. Why doesn't it work?
Try with {{#each items.item1}}
Because, Col.find({},{item1:1}) just remove all other field from result.
Limit Fields to Return from a Query
It's like SELECT item1 FROM table in SQL
If you need repeat your template for each item in item1, you need to precise which array should be use.
<template name="temp">
{{#each items.item1}}
{{> anotherTemplate}}
{{/each}}
</template>
Meteor mongo methods are a little different you cannot use.
return Col.find({},{item1:1});
you can use it this way:
return Col.find({},{fields:{'item1':1}});
Maybe you want:
{{#each items}}
{{#each item1}}
{{> anotherTemplate}}
{{/each}}
{{/each}}
Or this:
Template.temp.helpers({
items: function() {
return Col.find({},{item1:1}).map(function(item){
return item.item1
})
}
});
That way items will return the item1 array. Originally it was returning an array of objects with just one element, the item1 arrays of each object:
[{
item1: ["foo","bar","baz"]
},
{
item1: ["lah","nah","jah"]
},
{
item1: ["see","gee","bee"]
}]
this way you'll get an array of arrays: [["foo","bar","baz"], ["lah","nah","jah"], ["see","gee","bee"]]
I think you need to do return something like this from helper
return Col.findOne({}).item1
It should work now
Thanks
I'm listing all the Meteor users in a template:
<template name="userList">
<ul>
{{#each users}}
<li>
{{_id}}<br>
Emails:
{{#each emails}}
{{address}},
{{/each}}
</li>
{{/each}}
</ul>
</template>
Here's my helper:
Template.userList.helpers({
users : function() {
return Meteor.users.find({});
}
});
This works, but since I'm not using usernames, I only want to list the first email address and not have to handle it with an {{#each}} in the template. Ideally, I'd have a value for users.primaryEmail, so I changed the helper to this:
Template.userList.helpers({
users : function() {
var rawUsers = Meteor.users.find({});
var users = [];
_.each(rawUsers, function(user) {
user.primaryEmail = user.emails[0].address;
users.push(user);
})
return users;
}
});
...and updated my template code to output {{primaryEmail}}, but it doesn't seem to return any users at all now. What am I missing?
Figured out that I needed to use .fetch() in order to get the results as an array & make users.emails[0] work:
var rawUsers = Meteor.users.find({}).fetch();
How can I query a field in an object? My html retrieves all the objects in array called 'postcards'
Meteor.user.profile.postcards [
{
_id: 84fh83f,
field_one: "a name",
field_two: " winter whether",
field_three: " lost more writing"
},
{
_id: 6jsf58s,
field_one: "another name",
field_two: " topical issues ",
field_three: " lost more writing"
}
]
Note: I used random.Id() so each object in the array can be uniquely identified.
Setting a session value to this._id when the user is focused on the input field will retrieve this unique id, however, I would like to query the actual field in focus. The value in these fields are projected within the text input area by using the spacebars syntax within the html.
Can I somehow assign the name within the curly braces of the value attribute to a variable? Then query?
Is there a whole new way to achieve this?
I want to update that specific field in this object instead updating the entire object.
HTML:
{{#with currentUser.profile}}
{{#each postcards}}
<input id="one" value="{{field_one}}" type="text">
<input id="two" value="{{field_two}}" type="text">
<input id="three" value="{{field_three}}" type="text">
{{/each}}
{{/with}}
client.js
Within events, I would like to update the field on focus upon keyup.
Templates.myTemplate.events({
'keyup input[type=text]': _.throttle(function(event) {
Meteor.users.update(this._id, {$set: {**fieldbeingedited**: event.target.value}});
}, 500);
});
What you want to have is an ES6 capability named 'Computed property names'.
This is what is looks like :
var x = 'hello',
obj = {
[x]: 'world'
};
console.log(obj); // Object {hello: "world"}
You have two options :
- you use the meteor harmony package to transpile your es6 to es5 (https://github.com/mquandalle/meteor-harmony)
- you build your object first
To build you object first :
var obj = {};
obj[ this.targetField ] = event.target.value
Meteor.users.update(this._id, {$set: obj});