Coffeescript / Zappajs how to pass array to be rendered - coffeescript

This is a zappajs/coffeescript newbie question, I try to write a script that collects from mysql and renders results with div, not tables, works so far with just one row. My question how do I pass on the whole result array for output with the template? Code example below, any criticism welcome, thanks:
#post '/', (req, res) -> #POST FOR SHOWSTOCK
MysqlConnection = mysql.createConnection(
...
)
MysqlConnection.connect()
....
else
sql = "select art.artcode,art.artname, art.artsonst, groessen.colcode,groessen.artgrb,groessen.artwidth,groessen.artist,date_format(artoffdate,'%d-%m-%y') as mydate, groessen.soreason from groessen,art where groessen.artcode = art.artcode and art.artcode = " + MysqlConnection.escape(req.body.art.artcode)
MysqlConnection.query sql, (err, rows) ->
throw err if err
...
exports.art = rows
...
MysqlConnection.end
#redirect '/showstock' # 'back' or 'home'= "/"
#get '/': ->
#render index: {
}
#view index: ->
h1 img src: '/BW_AL_Logo_Blue.jpg', align:'left', alt:'Logo' , width:240, height:120, top:25, left:370 ,position:'absolute'
h2 style: "font-family:sans-serif;;position: absolute; top: 10px; width: 250px; left: 350px; height: 25px; background-color: white", 'XYZ Stock Information'
form method: "post", action: "/", ->
div "#Artcode", style: "position: absolute; top:60px; left:680px; width: 121px; height: 21px;", ->
input type: "text", name:"art[artcode]"
div "#Submit", style: "position: absolute; top:60px; left: 840px; width: 121px; height: 21px;", ->
input type: "submit", value: "Submit"
#get '/showstock': ->
#render showstock: {
artname0: exports.art[0].artname
...
}
#view showstock: ->
h1 img src: '/BW_AL_Logo_Blue.jpg', align:'left', alt:'Logo' , width:240, height:120, top:25, left:370 ,position:'absolute'
...
div style: "position: absolute; left: 10px; top: 195px; height: 400px; width:1200px; padding: 2px; border:2px solid gray;",->
div "#colcode", style: "height: 3px;width:90px;float:left;",->
b "COLOUR"
br()
#colcode0
div ...

Here's how to pass and use variables to a view function (from http://zappajs.org/docs/crashcourse/):
#get '/': ->
#render index: {foo: 'bar'}
#view index: ->
#title = 'Inline template'
h1 #title
p #foo
As for iterating over the rows in your resultset, you would have to do something like:
#view index: ->
for row in #foo.rows
div '', "#{row.itemno}: #{row.itemname}"
The exact details depend on which template engine you are using (from what you've written, looks like Coffeecup). Keep in mind that Coffeecup templates really are just Coffeescript code with lots of convenience functions (named identically with the HTML tags) and some conventions for rendering a "subtree" (by passing a function that renders the subtree). So for iterating over a data object you simply write Coffeescript code to do that, calling the Coffeecup convenience method for generating the html text.
Assuming your data sets can be large, using a browser redirect (like your code suggests) is not the proper way to pass the data from one view to another.
If you refactor the code in your "showstock" function to be a normal member function, passing along any request/response variables (I see Zappa passes those implicitely, but I do not know enough about Zappa to provide exact answers) and a rows object, just have that function return the rendered text. By calling this method from both your "/" handler and "/showstock" handler, you should be able to handle the different use cases without duplicating the code or trying to pass huge data objects as HTTP data objects.

Related

binding event listeners to ES6 objects [duplicate]

Do getElementsByClassName (and similar functions like getElementsByTagName and querySelectorAll) work the same as getElementById or do they return an array of elements?
The reason I ask is because I am trying to change the style of all elements using getElementsByClassName. See below.
//doesn't work
document.getElementsByClassName('myElement').style.size = '100px';
//works
document.getElementById('myIdElement').style.size = '100px';
Your getElementById code works since IDs have to be unique and thus the function always returns exactly one element (or null if none was found).
However, the methods
getElementsByClassName,
getElementsByName,
getElementsByTagName, and
getElementsByTagNameNS
return an iterable collection of elements.
The method names provide the hint: getElement implies singular, whereas getElements implies plural.
The method querySelector also returns a single element, and querySelectorAll returns an iterable collection.
The iterable collection can either be a NodeList or an HTMLCollection.
getElementsByName and querySelectorAll are both specified to return a NodeList; the other getElementsBy* methods are specified to return an HTMLCollection, but please note that some browser versions implement this differently.
Both of these collection types don’t offer the same properties that Elements, Nodes, or similar types offer; that’s why reading style off of document.getElements…(…) fails.
In other words: a NodeList or an HTMLCollection doesn’t have a style; only an Element has a style.
These “array-like” collections are lists that contain zero or more elements, which you need to iterate over, in order to access them.
While you can iterate over them similarly to an array, note that they are different from Arrays.
In modern browsers, you can convert these iterables to a proper Array with Array.from; then you can use forEach and other Array methods, e.g. iteration methods:
Array.from(document.getElementsByClassName("myElement"))
.forEach((element) => element.style.size = "100px");
In old browsers that don’t support Array.from or the iteration methods, you can still use Array.prototype.slice.call.
Then you can iterate over it like you would with a real array:
var elements = Array.prototype.slice
.call(document.getElementsByClassName("myElement"));
for(var i = 0; i < elements.length; ++i){
elements[i].style.size = "100px";
}
You can also iterate over the NodeList or HTMLCollection itself, but be aware that in most circumstances, these collections are live (MDN docs, DOM spec), i.e. they are updated as the DOM changes.
So if you insert or remove elements as you loop, make sure to not accidentally skip over some elements or create an infinite loop.
MDN documentation should always note if a method returns a live collection or a static one.
For example, a NodeList offers some iteration methods such as forEach in modern browsers:
document.querySelectorAll(".myElement")
.forEach((element) => element.style.size = "100px");
A simple for loop can also be used:
var elements = document.getElementsByClassName("myElement");
for(var i = 0; i < elements.length; ++i){
elements[i].style.size = "100px";
}
Aside: .childNodes yields a live NodeList and .children yields a live HTMLCollection, so these two getters also need to be handled carefully.
There are some libraries like jQuery which make DOM querying a bit shorter and create a layer of abstraction over “one element” and “a collection of elements”:
$(".myElement").css("size", "100px");
You are using a array as an object, the difference between getElementbyId and
getElementsByClassName is that:
getElementbyId will return an Element object or null if no element with the ID is found
getElementsByClassName will return a live HTMLCollection, possibly of length 0 if no matching elements are found
getElementsByClassName
The getElementsByClassName(classNames) method takes a string that
contains an unordered set of unique space-separated tokens
representing classes. When called, the method must return a live
NodeList object containing all the elements in the document that
have all the classes specified in that argument, having obtained the
classes by splitting a string on spaces. If there are no tokens
specified in the argument, then the method must return an empty
NodeList.
https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#getelementsbyclassname
getElementById
The getElementById() method accesses the first element with the specified id.
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
in your code the lines:
1- document.getElementsByClassName('myElement').style.size = '100px';
will NOT work as expected, because the getElementByClassName will return an array, and the array will NOT have the style property, you can access each element by iterating through them.
That's why the function getElementById worked for you, this function will return the direct object. Therefore you will be able to access the style property.
ES6 provides Array.from() method, which creates a new Array instance from an array-like or iterable object.
let boxes = document.getElementsByClassName('box');
setTimeout(() => {
Array.from(boxes).forEach(v => v.style.background = 'green');
console.log(Array.from(boxes));
}, 500);
.box {
width: 50px;
height: 50px;
margin: 5px;
background: blue;
display: inline-block;
}
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
As you can see inside the code snippet, after using Array.from() function you are then able to manipulate over each element.
The same solution using **`jQuery`**.
$('.box').css({'background':'green'});
.box {
width: 50px;
height: 50px;
margin: 5px;
background: blue;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
The following description is taken from this page:
The getElementsByClassName() method returns a collection of all elements in the document with the specified class name, as a NodeList object.
The NodeList object represents a collection of nodes. The nodes can be
accessed by index numbers. The index starts at 0.
Tip: You can use the length property of the NodeList object to determine the number of elements with a specified class name, then you can loop through all elements and extract the info you want.
So, as a parameter getElementsByClassName would accept a class name.
If this is your HTML body:
<div id="first" class="menuItem"></div>
<div id="second" class="menuItem"></div>
<div id="third" class="menuItem"></div>
<div id="footer"></div>
then var menuItems = document.getElementsByClassName('menuItem') would return a collection (not an array) of the 3 upper <div>s, as they match the given class name.
You can then iterate over this nodes (<div>s in this case) collection with:
for (var menuItemIndex = 0 ; menuItemIndex < menuItems.length ; menuItemIndex ++) {
var currentMenuItem = menuItems[menuItemIndex];
// do stuff with currentMenuItem as a node.
}
Please refer to this post for more on differences between elements and nodes.
In Other Words
document.querySelector() selects only the first one element of the specified selector. So it doesn't spit out an array, it's a single value. Similar to document.getElementById() which fetches ID-elements only, since IDs have to be unique.
document.querySelectorAll() selects all elements with the specified selector and returns them in an array. Similar to document.getElementsByClassName() for classes and document.getElementsByTagName() tags only.
Why use querySelector?
It's used merely for the sole purpose of ease and brevity.
Why use getElement/sBy?*
Faster performance.
Why this performance difference?
Both ways of selection has the purpose of creating a NodeList for further use.
querySelectors generates a static NodeList with the selectors thus it must be first created from scratch.
getElement/sBy* immediately adapts the existing live NodeList of the current DOM.
So, when to use which method it's up to you/your project/your device.
Infos
Demo of all methods
NodeList Documentation
Performance Test
You could get a single element by running
document.querySelector('.myElement').style.size = '100px';
but it's going to work for the first element with class .myElement.
If you would like apply this for all elements with the class I suggest you to use
document.querySelectorAll('.myElement').forEach(function(element) {
element.style.size = '100px';
});
It returns Array-like list.
You make that an Array as example
var el = getElementsByClassName("elem");
el = Array.prototype.slice.call(el); //this line
el[0].appendChild(otherElem);
/*
* To hide all elements with the same class,
* use looping to reach each element with that class.
* In this case, looping is done recursively
*/
const hideAll = (className, i=0) => {
if(!document.getElementsByClassName(className)[i]){ //exits the loop when element of that id does not exist
return;
}
document.getElementsByClassName(className)[i].style.visibility = 'hidden'; //hide element
return hideAll(className, i+1) //loop for the next element
}
hideAll('appBanner') //the function call requires the class name
With any browser supporting ES5+ (any browser basically above IE8) you can use the Array.prototype.forEach method.
Array.prototype.forEach.call(document.getElementsByClassName('answer'), function(el) {
el.style.color= 'red';
});
caniuse source
So I was told that this is a duplicate from my question and I should delete mine, which I will do so I can keep the forum clean and keep the right to make questions.
As I think mine and this question are really different I will point out the answer to mine, so I will complete the knowledge in this page and the information will not be lost.
Question
I have a code in the snippet that has a document.getElementsByClassName("close")[0], what the [0] is doing?
I never seen a square brackets being used in getElementsByClassName for what purpose is it used for?
Also, how can I convert it to jQuery?
Answer
The code in the snippet has a [0] it is actually being used as a array and as it is a 0 it is referring to the first time the appointed class is being used.
Same thing above.
I couldn't really do it and no one answered it. In the part of the code that is refering to event. target I can not use $("#myModal") instead of document.getElementById("myModal"), I think they should equivalent, but in this case the jQuery form substituting the standard one will not result in the desired effect.
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];
btn.onclick = function() {
modal.style.display = "block";
}
span.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
body {font-family: Arial, Helvetica, sans-serif;}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
<h2>Modal </h2>
<button id="myBtn">Open Modal</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<p>Some text in the Modal..</p>
</div>
</div>
update
It seems I can't really delete mine question and people are unsatisfied with it, I really don't know what I should do.
Super old school solution:
[].forEach.call(document.getElementsByClassName('myClass'), function (el) {
el.style.size = '100px';
});
An answer for Drenzii's specific case...
You could make a function that will work for any of the word elements and pass in the number of the one you want to transform, like:
// Binds `wordButtons` to an (array-like) HTMLCollection of buttons
const wordButtons = document.getElementsByClassName("word");
// Applies the `slantWord` function to the first word button
slantWord(1);
// Defines the `slantWord` function
function slantWord(wordNumber) {
const index = wordNumber - 1; // Collection index is zero-based
wordButtons[index].style.transform = "rotate(7deg)"; // Transforms the specified button
}
<div class="wordGameContainer">
<button class="word word1">WORD 1</button>
<button class="word word2">WORD 2</button>
<button class="word word3">WORD 3</button>
<button class="word word4">WORD 4</button>
</div>
<div>
<button onclick="moveWord()" class="playButton">PLAY</button>
</div>

ng-class not evaluating the expression

I'm trying to implement ng-class in my angularJs application but for some reason expression in the ng-class is not getting applied, any thoughts?
<span>{{prod.item.count}}</span>
<div ng-class="{'show-error-box' : prod.item.length< 1}" class="hide-error-box">
prod is my controller alias in the view and item is the scope object in my controller
I want the class 'show-error-box' when the number of items is less than 1 otherwise apply the class hide-error-box. As I don't have any test data to test this with 0 length i'm replacing the expression with ng-class="{'show-error-box' : 0< 1}" in which case show-error-box should get applied, but it is not happening.
CSS:
.hide-error-box {
display:none;
}
.show-error-box {
float: left;
width: 100%;
background: #fff;
text-align: center;
padding: 10% 0;
height: 700px;
position: absolute;
}
ng-class does not serve as a replacement to "class". It will add any classes whose name is a key in your object if the associated value is true.
The only two options this div will have as a result are:
class="hide-error-box"
or
class="hide-error-box show-error-box"
To acheive the desired effect you would could put the opposite condition as a value of your hide class.
ng-class="{hide-error-box: prod.item.length >= 1, show-error-box: prod.item.length < 1}"
However, it is often recommended that you keep your templates as free from logic as possible. It may be worth considering placing this in a controller function:
HTML
<div class="{{getSizeClass()}}">
JS
this.getSizeClass = function() {
if (item.length >= 1) {
return 'hide-error-box'
}
return 'show-error-box'
}

Dynamic class names in LESS

I have the following bit of LESS code working
#iterations: 940;
#iterations: 940;
#col:2.0833333333333333333333333333333%;
// helper class, will never show up in resulting css
// will be called as long the index is above 0
.loopingClass (#index) when (#index > -20) {
// create the actual css selector, example will result in
// .myclass_30, .myclass_28, .... , .myclass_1
(~".gs#{index}") {
// your resulting css
width: (#index/20+1)*#col;
}
// next iteration
.loopingClass(#index - 60);
}
// end the loop when index is 0
.loopingClass (-20) {}
// "call" the loopingClass the first time with highest value
.loopingClass (#iterations);
It outputs our grid system as so:
.gs940 {
width: 100%;
}
.gs880 {
width: 93.75%;
}
.gs820 {
width: 87.5%;
}
.gs760 {
width: 81.25%;
}
.gs700 {
width: 75%;
}
etc etc etc
Now what I want to do is some math to the class names to produce the following classes
.gs220-700
.gs280-640
.gs340-580
.gs400-520
.gs460-460
.gs520-400
.gs580-340
.gs640-280
.gs700-220
etc etc etc
basically this would be
.(#index) - (920px minus #index)
But I have no idea if this is possible.
This whole question was very helpful to me. I just wanted to post the solution to my problem as the way to do it has changed since LESS v 1.4. LESS Changelog
Rather than using the ~ sign, you just write out the portion of the name that you want along with the normal # and variable name with {} surrounding it. So: #class#{variable}.
For example, my solution using the same sort of loop became such:
/*Total number of passport inserts*/
#numInserts: 5;
/*Total width of the passport foldouts*/
#passportWidth: 300px;
/*Change in passport insert width per iteration*/
#passportWidthDiff: (#passportWidth / #numInserts);
/*"Array" of colors to select from to apply to the id*/
#passportColors:
blue orange green red yellow
purple white teal violet indigo;
/*The faux loop the doesn't end up in the final css
#index is the counter
#numInserts is the total number of loops*/
.loopingClass (#index) when (#index <= #numInserts){
/*This is the created ID with the index appended to it
You can also do this with classes such as if
we had had ".insert#{index}"*/
#insert#{index}{
/*Here are our properties that get adjusted with the index*/
width: (#passportWidth - (#passportWidthDiff * (#numInserts - #index)));
height: 50px;
background-color: extract(#passportColors, #index);
z-index: (#numInserts - #index);
}
/*Here we increment our loop*/
.loopingClass(#index + 1);
}
/*This calls the loop and starts it, I started from 1
since I didn't want to lead a className starting from 0,
But there is no real reason not to. Just remember to
Change your conditional from "<=" to "<"*/
.loopingClass(1);
And produces the following:
#insert1 {
width: 60px;
height: 50px;
background-color: #0000ff;
z-index: 4;
}
#insert2 {
width: 120px;
height: 50px;
background-color: #ffa500;
z-index: 3;
}
#insert3 {
width: 180px;
height: 50px;
background-color: #008000;
z-index: 2;
}
...
I don't think you're far off. What I've done is create a second variable inside the mixin, called #index2. All this does is find the '920px minus #index' value that you're looking for:
#index2 = (920-#index);
this is then appended to the class name:
(~".gs#{index}-#{index2}") {
This is the complete loop:
.loopingClass (#index) when (#index > 160) {
#index2 = (920-#index);
// create the actual css selector, example will result in
// .myclass_30, .myclass_28, .... , .myclass_1
(~".gs#{index}-#{index2}") {
// your resulting css
width: (#index/20+1)*#col;
}
// next iteration
.loopingClass(#index - 60);
}
// "call" the loopingClass the first time with highest value
.loopingClass (#iterations);
In order to get just the set you are looking for (gs220-700 to gs700-220), just change #iterations to equal 700.
Worth noting that currently, this will create the classes in the reverse order of how you specified them in the question.

Can you use jQuery .css() with .live()?

I have a div with class="centerMessage" . This div is inserted into the DOM at a point after the page is loaded. I would like to change the CSS on this div to center it. I tried the CSS function below, but it did not work. Does anybody know a way to do this?
function centerPopup() {
var winWidth = $(window).width();
var winHeight = $(window).height();
var positionLeft = (winWidth/2) - (($('.centerMessage').width())/2);
var positionTop = (winHeight/2) - (($('.centerMessage').height())/2);
$('.centerMessage').live( function(){
$(this).css("position","absolute");
$(this).css("top",positionTop + "px");
$(this).css("left",positionLeft + "px");
});
}
If my assumption of what you're trying to achieve is correct, you don't need any Javascript to do this. It can be achieved by some simple CSS.
.centerMessage {
position: absolute;
top: 50%;
left: 50%;
margin-top: -150px; /* half of the height */
margin-left: -300px; /* half of the width */
width: 600px;
height: 300px;
background: #ccc;
}
Demo: http://jsbin.com/awuja4
.live() does not accept JUST a function. If you want something to happen with live, it needs an event as well, like click. If you want something to happen always for every .centerMessage, you will need the plugin .livequery()
I believe that the following works in FF & Webkit.
div.centerMessage{
position: absolute;
width: /* width */;
height: /* height */;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
margin: auto;
}
I know this is already answered, but I thought I'd provide a working jsFiddle demo using JavaScript like the OP originally wanted, but instead of using live(), I use setInterval():
First, we need to declare a couple variables for use later:
var $centerMessage,
intervalId;
The OP's issue was that they didn't know when the div was going to be created, so with that in mind we create a function to do just that and call it via setTimeout() to simulate this div creation:
function createDiv() {
$('<div class="centerMessage">Center Message Div</div>').appendTo("body");
}
$(function() { setTimeout("createDiv()", 5000); });
Finally, we need to create a function that will check, using setInterval() at a rate of 100ms, to see if the div has been created and upon creation, goes about modifying the div via jQuery:
function checkForDiv() {
$centerMessage = $('.centerMessage');
if ($centerMessage.length) {
clearInterval(intervalId);
var $window = $(window),
winHeight = $window.height(),
winWidth = $window.width(),
positionTop = (winHeight / 2) - ($centerMessage.height() / 2),
positionLeft = (winWidth / 2) - ($centerMessage.width() / 2);
$centerMessage.css({
"display" : "block",
"position" : "absolute",
"top" : positionTop.toString() + "px",
"left" : positionLeft.toString() + "px"
});
}
}
$(function() { intervalId = setInterval(checkForDiv, 100); });
Try Use this
$('.centerMessage').live('click', function(){
Try this
$('#foo').on('click', function() {
alert($(this).text());
});
$('#foo').trigger('click');
OR
$('#foo').live('click', function() {
alert($(this).text());
});
$('#foo').trigger('click');

Display an icon in jQuery UI autocomplete results

I'm using the jQuery UI Autocomplete plugin (version 1.8), and I'd like to customize the way the suggestions show up. Specifically, I want to display not only some text, but an icon as well. However, when I send the <img> tag, it just gets rendered as plain text in the results list.
Is there some way to change this behavior? Alternatively, can you suggest a different way to include images in the returned results and have them show up in the suggestions?
Taken from here
$("#search_input").autocomplete({source: "/search",
minLength: 3,
select: function (event, ui) {
document.location = ui.item.url;
}
})
.data("autocomplete")._renderItem = function (ul, item) {
//As per recent documemtation above line should be
//.autocomplete( "instance" )._renderItem = function (ul, item) {
return $('<li class="ui-menu-item-with-icon"></li>')
.data("item.autocomplete", item)
.append('<a><span class="' + item.type + '-item-icon"></span>' + item.label + '</a>')
.appendTo(ul);
};
And the CSS:
.ui-menu .ui-menu-item-with-icon a {
padding-left: 20px;
}
span.group-item-icon,
span.file-item-icon {
display: inline-block;
height: 16px;
width: 16px;
margin-left: -16px;
}
span.group-item-icon {
background: url("/image/icons/group.png") no-repeat left 4px;
}
span.product-item-icon {
background: url("/image/icons/product.png") no-repeat left 7px;
}