#each with call to helper ignores first entry in array - coffeescript

I'm trying to generate a menu with handlebars, based on this array (from coffeescript):
Template.moduleHeader.modules = -> [
{ "moduleName": "dashboard", "linkName": "Dashboard" }
{ "moduleName": "roomManager", "linkName": "Raumverwaltung" }
{ "moduleName": "userManager", "linkName": "Benutzerverwaltung" }
]
The iteration looks like this (from the html code):
{{#each modules}}
<li {{this.isActive this.moduleName}}>
<a class="{{this.moduleName}}" href="#">{{this.linkName}}</a>
</li>
{{/each}}
{{this.isActive}} is defined like this (coffeescript code again):
Template.moduleHeader.isActive = (currentModuleName) ->
if currentModuleName is Session.get 'module'
"class=active"
Based on Session.get 'module' the appropriate menu item is highlighted with the css class active.
On reload, the Session-variable module contains 'dashboard', which is the first entry in that array, however, it does not get the active-class. If I add an empty object as the first item to the modules-array, the 'dashboard'-item is properly highlighted.
The strange thing is, that the first item (dashboard) is rendered fine, but it does not have the active-class, which raises the impression, that the function this.isActive is not called for the first item.
Am I doing it wrong?

Really going out on a whim here but my traditional approach would be below. I don't know whether it will work but it was too big for a comment, yet again it's not a guaranteed fix. Let me know how it goes:
Replace the HTML with
{{#each modules}}
<li {{isActive}}>
<a class="{{moduleName}}" href="#">{{linkName}}</a>
</li>
{{/each}}
Replace the CS with
Template.moduleHeader.isActive = () ->
currentModule = this
currentModuleName = currentModule.moduleName
if currentModuleName is Session.get 'module'
"class=active"

If its rendering, the each block is working correctly. There must be an issue in the isActive helper. Most likely the if block isn't working as you think it should be. Put a console.log in there and see if it is entered, and put another inside the if (printf debugging). I find this is usually the easiest way to do quick debugging of handlebars helpers.
A useful helper in js for inspecting values inside of a context is as follows
Handlebars.registerHelper("debug", function(optionalValue) {
console.log("Current Context");
console.log("====================");
console.log(this);
if (optionalValue) {
console.log("Value");
console.log("====================");
console.log(optionalValue);
}
});
Then you can call this inside an each block and see all the values the current context/a specific value has.

Related

Cypress UI - get an element inside double within block

I have the following scenario: I find some text to navigate to a section in my HTML. Then I find some other text to navigate to a subsection. Inside this subsection I have a button when clicked displays a modal dialog (which is placed outside both sections that I'm currently in. If I try to grab the modal dialog from within the sections it does not work. If I go outside the sections, it works.
cy.contains("Some text").parent().within(() => {
cy.contains("Some other text").parent().within(() => {
cy.find("Button that triggers a modal dialog").click();
//does not work
cy.getModalDialog().within(() => {
cy.contains("OK").click();
})
})
})
cy.contains("Some text").parent().within(() => {
cy.contains("Some other text").parent().within(() => {
cy.find("Button that triggers a modal dialog").click();
})
})
//works
cy.getModalDialog().within(() => {
cy.contains("OK").click();
})
Is there a better way how to grab this modal, without going outside the double within blocks?
Cypress provides an option withinSubject that can remove the effect of .within() for that particular command.
Element to search for children in. If null, search begins from root-level DOM element
cy.get("div#1").within(() => {
cy.get("span").within(() => {
// cy.get("div#2"); // ❌ fails
cy.get("div#2", { withinSubject: null }); // ✅ passes
})
})
Test page
<body>
<div id="1">
<span>one</span>
</div>
<div id="2">
<span>two</span>
</div>
</body>
Note this feature was broken in v12.0.2 and fixed again in v12.1.1
Although Cypress does allow things like cy.document() from which I presume you can go down from there, in general whenever I find a pattern where I "find a thing, then inside the thing, find another thing and do stuff", I'm better off never using .within at all. Instead I combine all the "find the thing" into one very large, very specific selector for a single .get
This does mean I need to repeat the long selector multiple times, and the .get is very heavy which seems backward, but it helps me avoid descending into callback heck.
In my current level of understanding, I do believe .within is a trap.

Adding Bootstrap 3 popover breaks JQuery Validation Plugin

I have a form, which I'm validating using JQuery Validation plugin. Validation works file until I add a Bootstrap 3 popover to the text field with name "taskName" (the one being validated) (please see below) . When I add the popover to this text field, error messages are repeatedly displayed every time the validation gets triggered. Please see the code excerpts and screenshots below.
I've been trying to figure out what is happening, with no success so far. Any help will be greatly appreciated. Thanks in advance!
HTLM Excerpt
The popover content
<div id="namePopoverContent" class="hide">
<ul>
<li><small>Valid characters: [a-zA-Z0-9\-_\s].</small></li>
<li><small>Required at least 3 characters.</small></li>
</ul>
</div>
The form
<form class="form-horizontal" role="form" method="post" action="" id="aForm">
<div class="form-group has-feedback">
<label for="taskName" class="col-md-1 control-label">Name</label>
<div class="col-md-7">
<input type="text" class="form-control taskNameValidation" id="taskName" name="taskName" placeholder="..." required autocomplete="off" data-toggle="popover">
<span class="form-control-feedback glyphicon" aria-hidden="true"></span>
</div>
</div>
...
</form>
JQuery Validate plugin setup
$(function() {
//Overwriting a few defaults
$.validator.setDefaults({
errorElement: 'span',
errorClass: 'text-danger',
ignore: ':hidden:not(.chosen-select)',
errorPlacement: function (error, element) {
if (element.is('select'))
error.insertAfter(element.siblings(".chosen-container"));
else
error.insertAfter(element);
}
});
//rules and messages objects
$("#aForm").validate({
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
});
$('.taskNameValidation').each(function() {
$(this).rules('add', {
required: true,
alphanumeric: true,
messages: {
required: "Provide a space-separated name."
}
});
});
});
Bootstrap 3 popover setup
$('[data-toggle="popover"]').popover({
trigger: "focus hover",
container: "body",
html: true,
title: "Name Tips",
content: function() { return $('#namePopoverContent').html();}
});
The screenshots
First Edit
It seems I did not make my question clear, so here it goes my first edit.
I'm not using the popover to display the error messages of the validation. The error messages are inserted after each of the fields that fail validation, which is precisely what I want. Hence, this question does not seem to be a duplicate of any other question previously asked.
Regarding the popover, I just want to add an informative popover that gets displayed whenever the user either clicks the text field "taskName" or hovers the mouse over it. Its role is completely independent of the validation.
The question is, then, why adding the (independent) popover is making the validation plugin misbehave, as shown in the screenshots.
I had the very same issue a few days ago and the only solution I found was to use 'label' as my errorElement:.
Change the line errorElement: 'span' to errorElement: 'label' or simply removing the entire line will temporarily fix the issue. ('label' is the default. )
I am not completely sure what the JQ validate + BS popover conflict is, but I will continue to debug.
After some debugging I think I found the issue.
Both jQuery validate and bootstrap 3 popovers are using the aria-describedby attribute. However, the popover code is overwriting the value written by jQuery validate into that attribute.
Example: You have a form input with an id = "name", jQuery validate adds an aria-describedby = "name-error" attribute to the input and creates an error message element with id = "name-error" when that input is invalid.
using errorElement:'label' or omitting this line works because on line 825 of jquery.validate.js, label is hard-coded as a default error element selector.
There are two ways to fix this issue:
Replace all aria-describedby attributes with another attribute name like data-describedby. There are 4 references in jquery.validate.js. Tested.
or
Add the following code after line 825 in jquery.validate.js. Tested.
if ( this.settings.errorElement != 'label' ) {
selector = selector + ", #" + name.replace( /\s+/g, ", #" ) + '-error';
}
I will also inform the jQuery validate developers.
The success option should only be used when you need to show the error label element on a "valid" element, not for toggling the classes.
You should use unhighlight to "undo" whatever was done by highlight.
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
unhighlight: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
(The success option could also be used in conjunction with the errorPlacement option to show/hide tooltips or popovers, just not to do the styling, which is best left to highlight and unhighlight.)
Also, I recommend letting the Validate plugin create/show/hide the error label element, rather than putting it the markup yourself. Otherwise, the plugin will create its own and ignore the one you've created.
In case you were unaware, you cannot use the alphanumeric rule without including the additional-methods.js file.

Storing appended elements to localStorage

I'm a teacher and creating a page to organize my lesson plans. There should be the ability to add new lessons (li) and new weeks (ul). The lessons are sortable between each of the weeks. Each newly added item will then be saved to localStorage.
So far, I'm able to create the lessons and new weeks. The sortable function works. The save function works... except that it will not save any of the new weeks (ul). When I refresh, the new lessons (li) are still on the page, but the new weeks (ul) are gone.
$("#saveAll").click(function(e) {
e.preventDefault();
var listContents = [];
$("ul").each(function(){
listContents.push(this.innerHTML);
})
localStorage.setItem('todoList', JSON.stringify(listContents));
});
$("#clearAll").click(function(e) {
e.preventDefault();
localStorage.clear();
location.reload();
});
loadToDo();
function loadToDo() {
if (localStorage.getItem('todoList')){
var listContents = JSON.parse(localStorage.getItem('todoList'));
$("ul").each(function(i){
this.innerHTML = listContents [i];
})
}
}
I created a fiddle here.
You can click the "Add New Week" button and then click the "Create Lesson" button and drag the new lesson into one of the weeks. After clicking "Save All", only the first week is saved.
I can't seem to figure out what's missing.
It's saving correctly, but since the page only has one <ul> element initially, that is the only one that gets populated in loadToDo(). (listContents has more than one element, but $("ul").each(...) only iterates over one element.)
There is a quick band-aid you can use to resolve this. Refactor your #new-week-but click handler into a named function:
function addNewWeek() {
var x = '<ul class="sortable weeklist"></ul>';
$(x).appendTo('.term').sortable({connectWith: '.sortable'});
}
$('#new-week-but').click(addNewWeek);
Then add this block after you fetch the array from storage but before you enumerate the <ul> elements:
var i;
for (i = 2; i < listContents.length; ++i) {
addNewWeek();
}
This will add the required number of <ul> elements before attempting to populate them.
I chose to initialize i to two because this creates two fewer than the number of elements in listContents. We need to subtract one because there is a <ul> in .term when the page loads, and another because the <ul id="new-lesson-list"> contents also get saved in listContents. (Consider filtering that element out in your #saveAll click handler.)
(Note that this requires merging all of your $(document).ready() functions into one big function so that addNewWeek() is visible to the rest of your code.)
Suggestions to improve code maintainability:
Give each editable <ul> a CSS class so that they can be distinguished from other random <ul> elements on the page. Filter for this class when saving data so that the "template" <ul> doesn't get saved, too.
Remove the one default editable <ul> from the page. Instead, in your loadToDo() function, add an else block to the if block and call addNewWeek() from the else block. Also, call it if listContents.length == 0. This will prevent duplicating the element in the HTML source (duplication is bad!) and having to account for it in your load logic.
If you implement both of these then you can initialize i to 0 instead of 2 in my sample code, which is a lot less weird-looking (and less likely to trip up future maintainers).

Row and column method missing on element.all

I have the following setup, where I want Protractor to click on a row (or the checkbox in a row, either is fine):
<li data-ng-repeat="room in chatRooms | ***filters***">
<div ng-click="toggleJoin(room.id)">
<input type="checkbox" value="{{room.id}}" ng-checked="isChecked(room.id)" />
<span>{{room.name}}</span>
</div>
</li>
And I want to do this with my Page Object:
var PageObject = function() {
this.lstChatRooms = element.all(by.repeater('room in chatRooms'));
this.clickChatRoom = function(index) {
this.lstChatRooms.row(index).column('{{room.id}}').click();
};
};
But when I try to call clickChatRoom with some index in my test, I get an error saying the object has no method 'row', and I've seen the same behavior with 'column'. I'm not calling anything on the list of chat rooms prior to this in my test, so the promise should not be resolved at that point. What am I doing wrong?
Update: The issue may be caused by this bug. Unless anyone can see that I'm doing something wrong with the API or something else.
i can't test it right now, but this should work for you:
this.lstChatRooms.then(function(rooms) {
rooms[index].findElement(by.tagName('input')).click();
});

infinite loop when trying to retrieve data from controller in view using angular $resource.Query() method

I'm building a small sails.js + angular.js app.
Here is a fiddle that roughly shows what my code looks like: http://jsfiddle.net/WEk3F/
index: function(req, res, next) {
Food.find({}, function foundFoods(err, foods) {
if (err) return next(err);
var data = {
name1: "test1",
name2: "test2"
}
res.view(
'food/index', {
foods: data
});
});
},
<div ng-app="myApp">
<div ng-controller="FoodController">
<ul>
<li ng-repeat="food in foods">
{{food.name}}
</li>
</ul>
<form>
<label for="name">Name:</label>
<input name="name" ng-model="editableFood.name" />
</form>
</div>
</div>
My problem is, that whenever i try to retrieve the data from my controller, i don't get those 2 items but instead it renders more and more items and just doesn't stop. even the page gets slow and unresponsive and almost freezes.
When i say
$scope.foods = [{"name": "test1"},{"name": "test2"}];
instead of
$scope.foods = Food.query();
it works. but i want the data to be coming from the backend via the controller.
the other methods (add, update etc) of the angular.js $resource module work fine for me.
/food maps to the index action of my FoodController and just returns some fixed test data
i found out what the problem was.
the angular.js $resource should only be used in a restful way, so the GET request to my food/index should return an array of objects.
in my case this wasn't the case. instead i used the index action of my controller to render a view and the result was therefor html.
so my options are to define a new route, that the $resource takes for the Query() command or define a new action that i use for the view/html stuff and use the index action again for pure restful response.
thx #sam hunter for pointing me to the right direction
the infinite loop that i received is explained here: One dimensional array of strings being parsed to 2d by angular resource
i love stack overflow