Displaying link conditionally in Angular.js - javascript

Basically, I have this code in my template:
<tr ng-repeat="entry in tableEntries">
<td ng-switch="entry.url == ''">
<span ng-switch-when="false">{{entry.school}}</span>
<span ng-switch-when="true">{{entry.school}}</span>
</td>
...
</tr>
As you can see I'm trying to display a clickable URL when entry.url is not empty and a plain text otherwise. It works fine, but it looks quite ugly. Is there a more elegant solution?
Another way I can think of doing it is using ng-if:
<td>
<span ng-if="entry.url != ''">{{entry.school}}</span>
<span ng-if="entry.url == ''">{{entry.school}}</span>
</td>
But then I would be repeating almost the same comparison twice, which looks even worse. How would you guys approach this?

You can try.
<div ng-show="!link">hello</div>
<div ng-show="!!link">hello</div>
But the ngSwitch which you are using should be fine.

Use double negation, it cast into boolean thus !!entry.url will return true if string is not empty.
<td ng-switch="!!entry.url">
<span ng-switch-when="true">{{entry.school}}</span>
<span ng-switch-when="false">{{entry.school}}</span>
</td>
A good read What is the !! (not not) operator in JavaScript? and Double negation (!!) in javascript - what is the purpose?

You can create a custom directive that hides the complexity:
HTML
<tr ng-repeat="entry in tableEntries">
<td>
<link model="entry"></link>
</td>
...
</tr>
JS
app.directive('link', function() {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<a ng-if="model.url != ''" href="{{model.url}}">{{model.school}}</a><span ng-if="model.url == ''"> {{ model.school }}</span>'
}
});

I would recommend having an ng-class="{'className': whenEntryURLisWhatever}" in your td, and make it change the css styles accessed, e.g:
td span{ /*#normal styles#*/ }
.className span{ /*#styles in the case of added classname (when it is a link)#*/
text-decoration: underline;
cursor: pointer;
}
Then just change what happens on ng-click within a function defined in your javascript code.
$scope.yourFunction = function(url){
if(!!url){
window.location = YourURL;
}
}
This would cut down on code repetition, as your html could now be:
<tr ng-repeat="entry in tableEntries">
<td ng-class="{'className': !!entry.url}">
<span ng-click="yourFunction(entry.url)">{{entry.school}}</span>
</td>
...
</tr>

Related

CSS binding not working in Knockout JS

I am new to Knockout JS, therefore need your help to fix one small issue. I am trying to bind css style with a table row in CSHTML page, based on a condition. I have added 2 rows but displaying only one for each item using 'visible' attribute. Following is my cshtml code:
<table class="listing">
<tbody class="no-wrap" data-bind="foreach: searchResultsListing.pagedItems, select: searchResultsListing">
<tr class="selectable" data-bind="visible: !$root.isMatchedCase($data), css: { selected: $root.searchResultsListing.isSelected($data) }">
<td class="check"><span></span></td>
--
--
<tr/>
<tr class="selectablematch" data-bind="visible: $root.isMatchedCase($data), css: { selected: $root.searchResultsListing.isSelected($data) }">
<td class="check"><span></span></td>
--
--
<tr/>
Underlying Typescript: Inside app.listing.ts File:
isSelected(item: T) {
return this.selectedItems.indexOf(item) >= 0;
}
As you can see, based on the result of isMatchedCase() method (which returns a boolean), I am displaying either of the tr (selectable or selectablematch). The problem is the css on the tr is getting binded only for the first tr, i.e. with the class selectable, and not getting binded with the selectablematch tr. The method 'isSelected($data)' is not getting called when the checkbox in the first td is clicked for 'selectablematch' tr. Can you guys please let me know what I am missing here?
I am little confused as to why you need to have 2 tr to begin with. What you could do is have a computed which would return the correct class for you and have only one row which will be always visible. Not need to deal with hide/show etc.
Look at this article on the css binding and how it is done. Here is what I am suggesting:
<table class="listing">
<tbody class="no-wrap" data-bind="foreach: searchResultsListing.pagedItems, select: searchResultsListing">
<tr data-bind="css: { rowClass($data), selected: $root.searchResultsListing.isSelected($data) }">
<td class="check"><span></span></td>
<tr/>
</tbody>
</table>
And your pureComputed (wrapped in a function so we can pass the $data):
var rowClass = function(data) {
return ko.pureComputed(function(){
return isMatchedCase(data) ? 'selectablematch' : 'selectable')
)}
}
I think that should get you going in the right direction.

Nested ng-include not working when element containing the ng-include attribute is itself added using ng-include attribute in angularjs

Please check this demo plunker to understand my problem better.
In my main page I've a table. Each table row is followed by an empty row which is hidden initially. When clicked on first row, I am injecting html within the empty row below it using directive
Main Page Index.html:
<tr ng-repeat-start="student in Students" class="rowDirctive">
<td>{{student.FirstName}}</td>
<td>{{student.LastName}}</td>
</tr>
<!--Empty row-->
<tr class="empty-row" style="display:none" id="{{student.Id}}" ng-repeat-end>
<td colspan="2"></td>
</tr>
Directive used for injecting html on click of first row:
appRoot.directive('rowDirctive', function ($compile) {
return {
restrict: 'C',
replace: false,
link: function (scope, element) {
element.bind("click", function () {
var rowId = "#"+scope.student.Id;
var innerhtml = angular.element($compile('<span class="" ng-include="\'_StudentDetailsTabs.html\'">' +
'</span>')(scope));
if ($(rowId + " td span").length === 0) {
$(rowId + " td").html(innerhtml);
}
if ($(rowId).is(':hidden')) {
$(rowId).slideDown("slow");
} else {
$(rowId).slideUp("slow");
}
});
}
};
});
Now when click on first row, the span is included within the empty row which itself contains ng-include="_StudentDetailsTabs.html". The _StudentDetailsTabs.html partial is included properly, I've no issue with it. But the _StudentDetailsTabs.html partial itself contains another three ng-include attributes which are not working.
_StudentDetailsTabs.html:
<tabset>
<tab heading="Personal Details" ng-click="PersonalDetails()">
<span ng-include="_PersonalDetails.html" ng-controller="StudentDetailsCtrl"></span>
</tab>
<tab heading="Educational Details" ng-click="EducationalDetails()">
<span ng-include="_EducationalDetails.html" ng-controller="StudentDetailsCtrl"></span>
</tab>
</tabset>
<br><br>
<span ng-include="_OtherDetails.html" ng-controller="StudentDetailsCtrl"></span>
Now when the above partial _StudentDetailsTabs.html is included into the empty row, why the ng-includes within it (_StudentDetailsTabs.html) not working. Why its not including the partials?
Edit: Here is the demo plunker. Now I don't know why, when I created this plunker the _StudentDetailsTabs.html is also not being included.
Your ng-includes are wrong, they should be with quotes, as in you should be passing a string
ngInclude | string
angular expression evaluating to URL. If the source is a string constant, make sure you wrap it in single quotes, e.g. src="'myPartialTemplate.html'".
so you need to change
<span ng-include="_PersonalDetails.html" ...></span>
to
<span ng-include="'_PersonalDetails.html'" ...></span>
and so on
Your plunker reads <span ng-include="'/_FileName.html'">. It should read <span ng-include="'_FileName.html'">, without those forward slashes.

ng-if in ng-repeat angularjs

I need to define different style by the value of a champ in ng-repeat, i try this :
<thead>
<tr><th>#</th><th>Nom</th><th>Ville</th><th style="width: 50%;">Lignée</th></tr>
</thead>
<tbody>
<tr ng-repeat="alternate1 in alternateViewText1.features | orderBy:'fiche.id*1'">
<td>{{alternate1.fiche.id}}</td>
<td ng-if="alternate1.properties.statut_activite == 'yes'" style="color: green;">{{alternate1.properties.nom}}</td>
<td ng-if="alternate1.properties.statut_activite == 'no'" style="color: red;">{{alternate1.properties.nom}}</td>
<td>{{alternate1.properties.ville}}</td>
<td>{{alternate1.properties.lignee}}</td>
</tr>
</tbody>
it's not working (nothing show)
how can i define automatically a style with a particular value ?
You can create a class for particular style and use ng-class like this :
JS FIDDLE
css:
.green
{
color:green;
}
.red
{
color:red;
}
html:
<td data-ng-class="{'green' : alternate1.properties.statut_activite == 'actif', 'red': alternate1.properties.statut_activite == 'inactif'}">{{alternate1.properties.nom}}</td>
You use the wrong value in your test
jsFiddle
Replace yes by actif and no by inactif
Example :
<td ng-if="alternate1.properties.statut_activite == 'actif'" style="color: green;">{{alternate1.properties.nom}}</td>
<td ng-if="alternate1.properties.statut_activite == 'inactif'" style="color: red;">{{alternate1.properties.nom}}</td>
PS:
The answer of ssilas777 is the best way to execute this switch of css class

onmouseout not triggering but onmouseover is

Here is a simplified fiddle
I have a table with sub-groupings inside of it. I want these sub-groupings to be hidden until the user clicks the sub-header row, which looks like this:
<tr class="title" name="titleGroup"
onmouseover='this.innerHTML=this.innerHTML.replace("---","+++");'
onmouseout='this.innerHTML=this.innerHTML.replace("+++","---");'
onclick="$('.Group').toggle();">
<td colspan="2">--- Group ---</td>
</tr>
So, onmouseover, should change the row to look like: +++ Group +++
and onmouseout should change it back to: --- Group ---
However, only the onmouseover triggers and I cannot get the text to go back.
I initially had the mouse over/out calling a function, but that has the same result. Also note that this page is dynamically generated so the text is not always "Group".
What am I doing wrong and how can I get onmouseout to reset the text?
Maybe you wanted to use onmouseleave event? :)
The proper use of replace function in your case and onmouseleave event:
<table width="550px">
<tr class="title" name="titleGroup" >
<td
onmouseover='this.innerHTML=this.innerHTML.replace(/-{3}/g,"+");'
onmouseleave='this.innerHTML=this.innerHTML.replace(/\+{3}/g,"-");'
onclick="$('.Group').toggle();"
colspan="2">--- Group ---</td>
</tr>
<tr class="Group" style="display:none;">
<td>
<b>Group</b>
HCS:</td>
<td>
<input type="checkbox" name="did4" />
</td>
</tr>
<tr class="Group" style="display:none;">
<td>
<b>Group</b>
NCD:</td>
<td>
<input type="checkbox" checked="" name="did5" />
</td>
</tr>
</table>
ANOTHER EDIT:
Firefox doesn't support onmouseleave event on TR marks! Move those events deffinition to
<td>
Building off of adeneo's fiddle, using jQuery with mouseleave is the way to go. However, the text could include a - or + so I had to update a bit to specifically look for 3 in a row.
Here is the final jQuery:
$('.title').on('mouseenter mouseleave', function(e) {
$('td', this).text(function(_,txt) {
return txt.replace(/[+]{3}|[-]{3}/g, function(x) {
return x=='---' ? '+++' : '---';
});
});
});
http://jsfiddle.net/jQRR7/29/
Checkout http://jsfiddle.net/jQRR7/30/
This works (at least in webkit browsers, Firefox still to be done)
onmouseover='this.innerHTML=this.innerHTML.replace(/-/g,"+");'
onmouseleave='this.innerHTML=this.innerHTML.replace(/\+/g,"-");'
It contains 2 issues:
a) replace() will replace only 1x, unless you hand over a regular expression (attention: do not surround the regex by quotes!) More about replace() can be found at http://devdocs.io/javascript/global_objects/string/replace DevDocs.io JS replace()
b) mouse over seems to be called, but mouseout just 1x,so only 1 time +++ will be replaced
Can you switch to CSS3 (pseudo elements like :after are supported by all major browser version) or do you need to stick on js?
take a look at http://jsfiddle.net/jQRR7/31/
HTML
<table>
<tr class="title" name="titleGroup" style="background: #c3c3c3" onclick="$('.Group').toggle();">
<td colspan="2">Group with CSS3</td>
</tr>
</table>
CSS
.title td {
font-weight: bold;
text-align: center;
cursor: pointer;
}
td:before{
content: '+++ ';
}
td:after{
content: ' +++';
}
tr:hover td:before{
content: '--- ';
}
tr:hover td:after{
content: ' ---';
}

How to use slideToggle on a set of elements?

I'm attempting to use the jQuery slideToggle() function to minimize a set of table rows. The problem I'm encountering is that while the set of rows are minimized, the animation does not seem to be taking effect. I think it has something to do with the manner in which I'm using slideToggle, calling it on a set of results instead of on a parent element, but am not sure.
I'm using this bit of javascript and jquery to minimize a set of rows.
$('td').on("click", "a.collection-minimize, a.list-minimize", function(event) {
event.stopPropagation();
// Get the class of the following row
var minimize_class = $(this).parent().parent().next().attr('class');
// trim whitespace
minimize_class = minimize_class.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
// Minimize all following rows of the same class.
$(this).parent().parent().siblings('.' + minimize_class).slideToggle();
});
An example set of rows:
<tr class="collection-original">
<td>
<a class="collection-minimize" onclick="return false" href="#">Minimize collection</a>
</td>
</tr>
<tr class=" collection-original" style="display: table-row;"></tr>
<tr class=" collection-original" style="display: table-row;"></tr>
<tr class=" collection-original" style="display: table-row;"></tr>
<tr class=" collection-original" style="display: table-row;"></tr>
A live page where this is being used can be viewed here: http://iodocs.vky.me/whitehat#Application-API-PUT-Modify-an-application.
See this: http://jsfiddle.net/DEPjD/
minimize_class = minimize_class.replace(/^\s\s*/, '').replace(/\s\s*$/, '').trim();
Or:
minimize_class = minimize_class.replace(/^\s\s*/, '').replace(/\s\s*$/, '').replace(/ /g, '');

Categories

Resources