I have a multiselect dependency where I display cities and their areas respectively.
The problem is that, both for loops in the function getArea only get called upon the second select of the cities option element.
Note: In debugger it works fine. I think its a scope problem and I tried using the foreach function but to no avail.
I have indicated below the line where the issue occurs.
//Global Variables
var allAreas = new Array();
$(document).ready(function() {
getCities();
$("#sel-city").bind("change", getAreas);
});
//get Cities on reload
function getCities() {
$.getJSON("cities.json", function(json) {
var citySelect = $("#sel-city");
for (var i in json) {
$("#sel-city")
.append($('<option>', {value: json[i].id})
.text(json[i].name));
}
});
}
function getAreas() {
var parentID = $(this).val();
console.log(parentID);
if (allAreas.length == 0) {
$.getJSON("areas.json", function(json) {
for (var i in json) {
allAreas.push(json[i]);
}
});
}
$('option', $("#sel-area")).remove();
var areasByParentID = new Array();
//Loop here
for (var i in allAreas) {
if (allAreas[i].city_id == parentID) {
areasByParentID.push(allAreas[i]);
}
}
console.log(areasByParentID);
//Loop here
for (var k in areasByParentID) {
$("#sel-area")
.append($('<option>', {value: areasByParentID[k].id})
.text(areasByParentID[k].name));
}
}
With the information you gave us and without sample data everything looks fine. But you have some minor mistakes which could lead to bad results. I decided to have a look on it, make some corrections and get it workin correctly with some sample data.
What you should take care of?
use for loops instead of for-in for arrays
you should avoid the new Array() operation
you should cache jquery variables to reduce siteload
for the areas to be set correctly on siteload you should just define init functions
What here could cause an error?
var a = [];
a[5] = 5;
for (var x in a) {
// Shows only the explicitly set index of "5", and ignores 0-4
}
The use of the for-in statement is to enumerate over object properties and will even inherited properties. Depending on your data it could give wrong results as shown in my example.
here is a jsfiddle with sample data.
Related
I am trying to port some of my Firebase database calls to an IOT board that does not have jQuery, just good old JavaScript.
In my code I originally had a jQuery $.each(tripData, function(index, element)
... loop to iterate through my results.
I have switched this to:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var tripData = response.val();
tripData.forEach(function (index, element) {
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
});
... but, I am getting the following error:
forEach is not a function
I am not sure how to resolve this.
for(let index in tripData){
element = trimpData[index];
}
not realy foreach, but works exactly like it
but you also can use map functions
You should really figure out if your response is an Array or Object.
$.each() iterates over arrays AND objects, thats why it works.
you should use for...in statement if you really want to iterate over this object tripData.
for(let prop in tripData)
{
if (tripData.hasOwnProperty(index))
{
item = tripData[prop];
// do stuff
}
}
lear about for...in statement here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
While the answer by #TypedSource will iterate over the resulting children, the order in which it iterates is undetermined - and very likely not going to be by timestamp.
A snapshot that you get as the result for a query contains three pieces of information for each child: its key, its value, and its position relative to the other children. When you call .val() on the snapshot, you lose the relative ordering.
To maintain the order, use the built-in forEach() method of the snapshot:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var index = 0;
response.forEach(function (child) {
var element = child.val();
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
index++;
});
});
Array.of(response.val()).forEach should work if it is just an array-like object missing its iterator
I'm having trouble trying to get a number from each item in a knockout observable array and add the numbers together and assign it to another computed variable. Here's what I have right now...
Semesters: ko.observableArray([
{
semesterName: "Fall",
semesterCode: "300",
PlannedCourses: ko.observableArray([]),
totalCredits: ko.computed(function(){
var total = 0;
ko.utils.arrayForEach(this.PlannedCourses, function (course) {
total += course.MinHours();
});
return total;
}),
},
...
What I'm trying to do is, in the totalCredits variable, I'm trying to iterate through the PlannedCourses array and get the MinHours variable for each item and add them together in the total variable. Then I return it to the totalCredits item in the Semesters array. The issue I'm having is getting the PlannedCourses variable in the ko.utils.arrayForEach part. I'm getting an undefined on it and I'm not sure why. I think it's a simple syntax error but I can't see what's wrong.
The PlannedCourses observable array is a dynamic object that is getting the list of PlannedCourses properly. It's defined in the context of itself but I'm not passing it to the totalCredits computed function properly.
I hope this is clear enough. Thank you for your help!
Note: All the rest of the code is working as intended. The only part that isn't working is the totalCredits computed function. I'm not sure if anything within the ko.utils.arrayForEach is working as I haven't been able to get that far.
You're going to need to change the way you populate your Semesters observable array to use a constructor function in order to get a reference to the correct scope for this:
function semester(name, code) {
this.Name = name;
this.Code = code;
this.PlannedCourses = ko.observableArray([]);
this.totalCredits = ko.computed(function(){
var total = 0;
ko.utils.arrayForEach(this.PlannedCourses(), function (course) {
//Note the change to "this.PlannedCourses()" above to get the underlying array
total += course.MinHours();
});
return total;
}, this); //now we can pass "this" as the context for the computed
}
See how we can now pass in an object to the second argument for ko.computed to use as the context for this in the inner function. For more information, see the knockout docs: Managing 'this'.
You then create new instances of semester when populating your array:
Semesters: ko.observableArray([
new semester("Fall", "300"),
new semester(...)
]);
This approach also means you have a consistent way of creating your semester objects (the computed is only defined once for one thing), rather than possibly incorporating typos etc in any repetition you may originally have had.
As others already mentioned your this is not what you think it is. In your case the context should be passed to the computed as follows:
totalCredits: ko.computed(function() {
// Computation goes here..
}, this)
Another approach could be to store the correct this to some local variable during the object creation (ex. var self = this; and then use self instead of this).
However, ko.utils.arrayForEach doesn't work with observable arrays but works on pure JavaScript arrays, so you should unwrap the observable array to access the elements of the underlying array:
ko.utils.arrayForEach(this.PlannedCourses(), function(course) {
// ...
});
// Or
ko.utils.arrayForEach(ko.unwrap(this.PlannedCourses), function(course) {
// ...
});
The scope (this) isn't what you think it is.
See http://knockoutjs.com/documentation/computedObservables.html
try adding your context, like the following:
Semesters: ko.observableArray([
{
semesterName: "Fall",
semesterCode: "300",
PlannedCourses: ko.observableArray([]),
totalCredits: ko.computed(function(){
var total = 0;
ko.utils.arrayForEach(this.PlannedCourses, function (course) {
total += course.MinHours();
});
return total;
}, this), // new context passed in here
},
...
Doing this passes in the context of the array item itself into your computed function.
Edit:
you may need to access the Semesters object inside you loop, and add some way to reference the current item:
Semesters: ko.observableArray([
{
semesterName: "Fall",
semesterCode: "300",
PlannedCourses: ko.observableArray([]),
totalCredits: ko.computed(function(){
var total = 0;
for( var i = 0, len = Semesters().length; i < len; i++ ) {
// check current array item, possibly add an id?
if( Semesters()[i].semesterName === "Fall" &&
Semesters()[i].semesterCode === "300" ) {
ko.utils.arrayForEach(Semesters()[i].PlannedCourses, function (course) {
total += course.MinHours();
});
break; // done searching
}
}
return total;
})
},
I need some help with a nested for loop in protractor and converting/understanding promises correctly. In ‘test’ below the functionality works with all values, but as soon as I try to put in the nested for loops things go south. Any chance someone has a clean suggestion on this? I have tried the forEach which some indicate handle the promise issue inherently, but I seem to get the same results.
My Test data looks like:
objectPage.chartValues = {
[['chart','ChartID01'],['title','TitleText01'],['Name01','value01'],['Name02','Value02']],
[[‘chart','ChartID02'],['title','TitleText02'],['Name01','value01'],['Name02','Value02']],
[[‘chart','ChartID03'],['title','TitleText03'],['Name01','value01'], [‘Name02’,'Value02'],['Name03','Value03']]
}
it ('test', function (){
for (chartNumber = 0; chartNumber < objectPage.chartValues.length; chartNumber++) {
for (chartEntry = 1; chartEntry < ObjectPage.chartValues[chartNumber].length; chartEntry++) {
//for readability of next call pulled out here
chart = objectPage.chartValues[chartNumber][0][1];
name = objectPage.chartValues[chartNumber][chartEntry][0];
value = objectPage.chartValues[chartNumber][chartEntry][1];
pageObject.getbackgroundcolor(chart, name).then(function (color) {
expect(pageObject.getElementFromTable(chart, name, color).getText())
.toEqual(value);
});
}
}
});
//function calls in pageobject the call for get background is straight forward.
this.getbackgroundcolor = function (chartName, valueName) {
return element(by.id(chartName)).element(by.cssContainingText('.dxc-item', valueName)).element(by.tagName('rect')).getAttribute('fill');
//get element is very similar.
this.getElementFromTable = function(chartName, valueName, colorname) {
some searching stuff..
return element(by.css(‘tspan'));
My results seem to indicate the look executes, but not returning from the actual expect. Finally trying to find a value for an item with background color of null. I know this is not true as I have run all values individually and in sequence without issue. Hopefully I avoided cut and past/generalization errors.
Thank you.
Update:
it('Verify Charts on page ', function () {
myChartlength = objectPage.chartValues.length;
for (chartNumber = 0; chartNumber < myChartlength; chartNumber++) {
(function (chartNumber) {
myEntrylength = objectPage.chartValues[chartNumber].length;
chartValues = objectPage.chartValues[chartNumber];
for (chartEntry = 2; chartEntry < myEntrylength; chartEntry++) {
(function (chartEntry) {
//pulled these out for readablility of next call.
chart = chartValues[0][1];
name = chartValues[chartEntry][0];
value = chartValues[chartEntry][1];
console.log('chart: ' + chart + ', name: ' + name + ', value: ' + value);
page.getbackgroundcolor(chart, name).then(function (color) {
expect(objectPage.getElementFromTable(chart, name, color).getText()).toEqual(value);
});
})(chartEntry);
};
})(chartNumber);
};
});
Yeah, if I'm understanding your question correctly, your problem is async. It's firing through the loops before any promises are returned.
To loop tests, the best solution I've found is to use an IIFE (Instantly Invoked Function Expression). In which, you create your loop, create the iife, and pass in the index.
Here's a basic example:
describe('to loop tests', function() {
var data = ['1', '2', '3'];
for(var i = 0; i < data.length; i++) {
// create your iife
(function(i) {
it('pass in the index to an iife', function() {
console.log('i is: ' + i);
expect(data[i]).toBe(true);
});
})(i); // pass in index
}
});
This works great for data driving tests from say a data file, or whatever. And if you need multiple loops, like in your example code, you'll just make multiple iifes.
You shouldn't use for loops with protractor or you will have a bad time.
Due to the asynchronous nature of Protractor, if you need loops, I see async's map https://github.com/caolan/async as one good and clean solution.
Other option is to use ES5's map when you need loops in Protractor, such as:
[1,3,5,7].map(function(index,key){
expect(element.all(by.css('.pages-list')).get(index).isDisplayed()).toBeFalsy()
})
In your case, I see that you need a for loops to produce array, that you later can map over it.
You can have this array with function, that uses for loops inside and returns the needed array to a callback.
Simple example with one for loop
function returnIndexes(callback){
var exitArray = [];
for (var i = 0; i < someArray.length; i++) {
if(someArray[i].length > 12){
exitArray.push(someArray[i]);
}
if(i==someArray.length-1){
callback(exitArray);
}
}
I'm trying to make the {{#each}} helper to iterate over an object, like in vanilla handlebars. Unfortunately if I use #each on an object, Ember.js version gives me this error:
Assertion failed: The value that #each loops over must be an Array. You passed [object Object]
I wrote this helper in attempt to remedy this:
Ember.Handlebars.helper('every', function (context, options) {
var oArray = [];
for (var k in context) {
oArray.push({
key : k,
value : context[k]
})
}
return Ember.Handlebars.helpers.each(oArray, options);
});
Now, when I attempt to use {{#every}}, I get the following error:
Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.
This seems like a basic feature, and I know I'm probably missing something obvious. Can anyone help?
Edit:
Here's a fiddle: http://jsfiddle.net/CbV8X/
Use {{each-in}} helper. You can use it like like {{each}} helper.
Example:
{{#each-in modelWhichIsObject as |key value|}}
`{{key}}`:`{{value}}`
{{/each-in}}
JS Bin demo.
After fiddling with it for a few hours, I came up with this hacky way:
Ember.Handlebars.registerHelper('every', function(context, options) {
var oArray = [], actualData = this.get(context);
for (var k in actualData) {
oArray.push({
key: k,
value: actualData[k]
})
}
this.set(context, oArray);
return Ember.Handlebars.helpers.each.apply(this,
Array.prototype.slice.call(arguments));
});
I don't know what repercussions this.set has, but this seems to work!
Here's a fiddle: http://jsfiddle.net/CbV8X/1/
I've been after similar functionality, and since we're sharing our hacky ways, here's my fiddle for the impatient: http://jsfiddle.net/L6axcob8/1/
This fiddle is based on the one provided by #lxe, with updates by #Kingpin2k, and then myself.
Ember: 1.9.1, Handlebars: 2.0.0, jQuery 2.1.3
Here we are adding a helper called every which can iterate over objects and arrays.
For example this model:
model: function() {
return {
properties: {
foo: 'bar',
zoo: 'zar'
}
};
}
can be iterated with the following handlebars template:
<ul class="properties">
{{#every p in properties}}
<li>{{p.key}} : {{p.value}}</li>
{{/every}}
</ul>
every helper works by creating an array from the objects keys, and then coordinating changes to Ember by way of an ArrayController. Yeah, hacky. This does however, let us add/remove properties to/from an object provided that object supports observation of the [] property.
In my use case I have an Ember.Object derived class which notifies [] when properties are added/removed. I'd recommend looking at Ember.Set for this functionality, although I see that Set been recently deprecated. As this is slightly out of this questions scope I'll leave it as an exercise for the reader. Here's a tip: setUnknownProperty
To be notified of property changes we wrap non-object values in what I've called a DataValueObserver which sets up (currently one way) bindings. These bindings provide a bridge between the values held by our internal ArrayController and the object we are observing.
When dealing with objects; we wrap those in ObjectProxy's so that we can introduce a 'key' member without the need to modify the object itself. Why yes, this does imply that you could use #every recursively. Another exercise for the reader ;-)
I'd recommend having your model be based around Ember.Object to be consistent with the rest of Ember, allowing you to manipulate your model via its get & set handlers. Alternatively, as demonstrated in the fiddle, you can use Em.Get/Em.set to access models, as long as you are consistent in doing so. If you touch your model directly (no get/set), then every won't be notified of your change.
Em.set(model.properties, 'foo', 'asdfsdf');
For completeness here's my every helper:
var DataValueObserver = Ember.Object.extend({
init: function() {
this._super();
// one way binding (for now)
Em.addObserver(this.parent, this.key, this, 'valueChanged');
},
value: function() {
return Em.get(this.parent, this.key);
}.property(),
valueChanged: function() {
this.notifyPropertyChange('value');
}
});
Handlebars.registerHelper("every", function() {
var args = [].slice.call(arguments);
var options = args.pop();
var context = (options.contexts && options.contexts[0]) || this;
Ember.assert("Must be in the form #every foo in bar ", 3 == args.length && args[1] === "in");
options.hash.keyword = args[0];
var property = args[2];
// if we're dealing with an array we can just forward onto the collection helper directly
var p = this.get(property);
if (Ember.Array.detect(p)) {
options.hash.dataSource = p;
return Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
}
// create an array that we will manage with content
var array = Em.ArrayController.create();
options.hash.dataSource = array;
Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
//
var update_array = function(result) {
if (!result) {
array.clear();
return;
}
// check for proxy object
var result = (result.isProxy && result.content) ? result.content : result;
var items = result;
var keys = Ember.keys(items).sort();
// iterate through sorted array, inserting & removing any mismatches
var i = 0;
for ( ; i < keys.length; ++i) {
var key = keys[i];
var value = items[key];
while (true) {
var old_obj = array.objectAt(i);
if (old_obj) {
Ember.assert("Assume that all objects in our array have a key", undefined !== old_obj.key);
var c = key.localeCompare(old_obj.key);
if (0 === c) break; // already exists
if (c < 0) {
array.removeAt(i); // remove as no longer exists
continue;
}
}
// insert
if (typeof value === 'object') {
// wrap object so we can give it a key
value = Ember.ObjectProxy.create({
content: value,
isProxy: true,
key: key
});
array.insertAt(i, value);
} else {
// wrap raw value so we can give it a key and observe when it changes
value = DataValueObserver.create({
parent: result,
key: key,
});
array.insertAt(i, value);
}
break;
}
}
// remove any trailing items
while (array.objectAt(i)) array.removeAt(i);
};
var should_display = function() {
return true;
};
// use bind helper to call update_array if the contents of property changes
var child_properties = ["[]"];
var preserve_context = true;
return Ember.Handlebars.bind.call(context, property, options, preserve_context, should_display, update_array, child_properties);
});
Inspired by:
How can I make Ember.js handlebars #each iterate over objects?
http://mozmonkey.com/2014/03/ember-getting-the-index-in-each-loops/
https://github.com/emberjs/ember.js/issues/4365
https://gist.github.com/strathmeyer/1371586
Here's that fiddle again if you missed it:
http://jsfiddle.net/L6axcob8/1/
I have a variable in a JavaScript constructor that appears to be set to the correct value when stepped through using breakpoints. However, when run without breakpoints, the variable (supposed to be an array that I give it), comes up as an empty array in the console. I don't know whether or not using the get/set property of prototype, as described here. Also-- I'm working in webkit, so if someone could help explain to me why it isn't working there, I'd appreciate it. Thanks!
function Box(inElement){
var self = this;
this.element = inElement;
this.boxes = (function () {
var boxes = [];
for (var i = 0; i < inElement.childNodes.length; ++i) {
if (3 !== inElement.childNodes[i].nodeType) {
boxes.push(inElement.childNodes[i]);
}
}
return boxes;
})();
this.rotation = [-40,-20,0,20,40];
}
Box.prototype =
{
get rotation(){
return this._rotation;
},
set rotation(rotArray){
console.log('rotArray');
console.log(rotArray);
var thisrot;
this._rotation = rotArray;
for(var i=0; i<this.boxes.length; i++){
thisrot = rotArray.shift();
this.boxes[i].style.webkitTransform = 'rotateY(' + thisrot + 'deg) translateZ(170px)';
}
}
}
function loaded()
{
new Box(document.getElementById('area'));
}
window.addEventListener('load',loaded, true);
So, after some fiddling, I discovered that boxes.push(inElement.childnodes[i] is the problematic line. When commented out, the value comes out as expected.
You are removing all elements from your array in the loop inside of set rotation using shift. Arrays are passed by reference in JavaScript, not by value. If you want to create a copy of your array, you will have to use Array.slice:
this._rotation = rotArray.slice();