AngularJS: render table columns before content is received - javascript

How do I make AngularJS render table columns correctly before the intended content for them is received?
I'm using ng-if to show the content, since I need different elements depending on the value that I get back from an API call, see below. This is the only reason for not using ng-bind, which I assume would be a more legitimate solution.
<td ng-if="someArray.length > 0"><a ng-href="someLink">Yes</a></td>
<td ng-if="someArray.length == 0">No</td>
Using this, due to obvious reasons the particular column is not shown at all while someArray is not initiated. I could check for undefined in the second ng-if, but I would rather have the column be completely empty while waiting for a value.
I am still quite new to AngularJS and I assume that there is a best-practice here.

You can use ng-if in internal dom elements
<td>
<a ng-if="someArray.length > 0" ng-href="someLink">Yes</a>
<p ng-if="someArray.length == 0">NO</p>
</td>

You can move the ng-if into the elements within the td:
<td>
<a ng-if="someArray.length > 0" ng-href="someLink">Yes</a>
<span ng-if="someArray.length == 0">No</span>
</td>

Related

Nested ng-repeat with dynamic input

I am trying to display a JSON response in a table using ng-repeat. The problem is that not all objects recieved are the same. All of them have a date, short message and long message. There are also ones with an additional value list, differing in length. This list should be diplayed underneath the long message within its own table or list. I use the alert.slice().reverse() because I want the newest entries to be on top. The new objects are inserted using .push({values}).
<tbody class="AlTbody" ng-repeat="alerts in alerts.slice().reverse()" ng-class="className">
<tr class="Altr Aldate">
<td ng-show="{{alerts.Date}}"><b>{{alerts.Date}}:</b>
</td>
</tr>
<tr class="Altr Alshort " ng-click="toggleDetail($index)">
<td>{{alerts.S}}</td>
</tr>
<tr class="Altr " ng-show="activePosition == $index">
<td class="msgL">{{alerts.L}}
<!-- 1) <p ng-show="{{item.List}}"><br><ul><li>Previous values:</li> <li ng-repeat="vals in ValueList">{{vals.value}}</li></ul> </p>-->
<!-- 2) <p ng-show="{{List.txt}}"> <br><ul><li>Previous values:</li> <li ng-repeat="List in alerts.List">{{List.txt}}</li></ul> </p>-->
</td>
</tr>
</tbody>
I already tried two approaches as seen in the code. The first one displayed the list correct however it was displayed underneath every long message instead of only the one it belongs to. I used a new variable.
var l=valList.length;
scope.List=true;
while(l>-1){
scope.ValueList.push({value: valList[l]});
l--;
}
The second approach did not work at all because I could not find an index.
var l=valList.length;
var indexV= jQuery.inArray(currdate,scope.alerts.Date);
while(l>-1){
scope.alerts[indexV].List.push({txt: valList[l]});
l--;
}
edit:
This is the current output. There you can see two objects( date, short message and long message) and both of them have the previous values section. However only the upper object is supposed to diplay the list of previous values.
What you are trying to achieve is entirely possible, my advice is to start at a known point and work from there.
I have put together a jsfiddle to show you how nested ng-repeats will work. try and work from that point. As a side note it looks like your JSON structure is overly complex, if you can simplify that down I would.
https://jsfiddle.net/roscorcoran/wyu7tgxm/
<div ng-app="App" ng-controller="Ctrl1">
<div ng-repeat="alert in alerts">
<a ng-if="alert.Date">{{alert.Date}}</a>
<p ng-repeat="val in alert.L.vals">
<a ng-if="val && val.value">{{val.value}}</a>
</p>
<p ng-repeat="item in alert.List">
<a ng-if="item && item.txt">{{item.txt}}</a>
</p>
</div>
</div>
You can try to add a ng-if statement inside the ng-repeat loop:
<p ng-if="{{values.list}}"><br><ul><li>Previous values:</li> <li ng-repeat="vals in values.list">{{vals.value}}</li></ul> </p>-->
Okay so this works for me now. It still always displays "Previous Values:" but the values only display when they actually belong to the message.
<ul ng-if="alerts.List.vals"><li>Previous values:</li> <li ng-repeat="val in alerts.List.vals" >{{val.value}}</li></ul>
This might not be the best and most elegant solution but it works.
if(valList){
scope.alerts.push({Date:currdate,S:msgSn,L:msgLn, List:{ vals:[{value:valList[0]},{value:valList[2]},{value:valList[4]},{value:valList[6]}] } });
}else{
scope.alerts.push({Date:currdate,S:msgSn,L:msgLn });
}
I only display the even indexes of the value array because the list of values was a string which I split and every uneven entry is "):" which I don't need to display.

directive "link" doesn't apply as I want it

Ok I got a page with some input managed with angular x-editable.
fiddle
The following directive:
treeView.directive('selectWhenEditing',function(){
var linkFunction = function(scope,startingelem,attr)
{
console.log(startingelem[0]);
console.log (startingelem[0].querySelector('.editable'));
angular.element(startingelem[0].querySelector('.editable')).on('click',function(){
//console.log(startingelem[0]);
angular.element(startingelem[0].querySelector('.editable-input').select());
});
};
return{
restrict: 'AEC',
link:linkFunction
};}
);
Is supposed to make that when I click on an editable element, the text is selected.
Here is the code of the page:
<tr ng-repeat="properties in elementToEdit" ng-if="fieldsSettings[$index][1]==1">
<td>
{{ fieldsSettings[$index][0] }}
</td>
<td data-select-when-editing>
<span editable-text="properties.prop" onbeforesave="" ng-if="fieldsSettings[$index][2]== 'text'">
{{ properties.prop || 'empty' }}
</span>
<span editable-select="properties.prop" onbeforesave="" ng-if="fieldsSettings[$index][2]== 'select'" e-ng-options="s.value as s.text for s in fieldsSettings[$index][3]">
{{ selectElement(properties.prop,$index) || 'empty' }}
</span>
</td>
</tr>
and that's the relevant compiled HTML:
<tr class="ng-scope" ng-if="fieldsSettings[$index][1]==1" ng-repeat="properties in elementToEdit">
<td class="ng-binding"> Name </td>
<td data-select-when-editing="">
<span class="ng-scope ng-binding editable editable-click" ng-if="fieldsSettings[$index][2]== 'text'" onbeforesave="" editable-text="properties.prop"> Food </span>
</td>
</tr>
The directive is firing as I wish it (eg: the number of times of expect it).
The first console.log logs me the calling TD element as expected. But the second one is null, as it never finds an element ".editable" inside this TD, so obviously the text is never selected (event "on" isn't applied).
I've done the exact same thing in another page/module and it works well. I'm comparing the codes but cannot find a difference.
You guys see something ?
As it says in the docs, jqLite's find() function is "Limited to lookups by tag name", which means you can't use it to select elements by classes - so you need to use querySelector instead. Note that you also need startingelem[0] instead of just startingelem when using it.
I had to use angular.element to get the jqLite object for on() binding (you can use plainJS there as shown below), so it looks a bit ugly:
angular.element(startingelem[0].querySelector(".editable")).on('click',function(){
startingelem[0].querySelector('.editable-input').select();
});
Here is the fully working version: http://jsfiddle.net/ar7znuz5/
Plain JS version using addEventListener:
startingelem[0].querySelector(".editable").addEventListener('click',function(){
startingelem[0].querySelector('.editable-input').select();
});
And the fully working version: http://jsfiddle.net/ar7znuz5/1/
The last version, with selects and a newer original code: http://jsfiddle.net/ar7znuz5/7/

Using CSS or Java to Show or Hide Empty Content Area

I am trying to create a page template that uses section headers and subsequent content that is being dynamically pulled in based on a separate database. Currently I have the page set up to look something like this:
<tr>
<td>
<h3>Product Applications</h3>
{tag_applications}<br />
</td>
</tr>
Where the Product Applications is a formatted header on the page and the {tag_applications} is link through the CMS that is pulling in content from a field defined elsewhere. I am trying to figure out how to hide the entire cell (or div if I need to) with either CSS or a script when the {tag_applications} is empty or blank. I tried to use the 'empty' tag in CSS on the cell and setting the display to hidden, but of course, the cell is not actually empty because of the header.
What is the best way that I can accomplish this without creating separate pages for each item?
Thanks!
Try wrapping your content area in a div, like this:
<tr>
<td>
<h3>Product Applications</h3>
<div class="contentSection">{tag_applications}</div>
</td>
</tr>
Then your script can check if the div is empty or not. (uses jQuery)
$(function()
{
$(".contentSection").each(function(idx, ele)
{
if($(ele).html() == "")
$(ele).parent().hide();
});
});
My apologies if I made any syntax errors...

Avoid unnecessary evaluation of bound values in Angular.js

Have an angular.js directive that renders as a table. Most of the time, the table is small, so performance is not an issue.
But sometimes, the table has many rows (e.g. thousands), so rendering every row is expensive, as each bound value appears to be evaluated twice, and there are a lot of bound values. And Angular seems to evaluate this table a lot, only to find that all of the values in it are unchanged and thus need not be rerendered, paralyzing the application needlessly.
For instance, the entire table appears to be re-revaluated when the value of $scope.showMenu changes on mouseenter / mouseleave.
Is there a way to tell Angular that the entire table is dependent on some other value, say, $scope.checksum thus if that doesn't change, then the entire table doesn't change?
<div class="header" ng-mouseenter="showMenu=true" ng-mouseleave="showMenu=false">
<!-- show dropdown menu only when hovering over the header -->
<span ng-if="showMenu" class="menu dropdown" > ... menu content goes here...</span>
<h2>{{getTitle()}}</h2>
<p>{{description}}</p>
</div>
<table>
<tr ng-repeat="key in rowKeys">
<td title="{{getRowItem(key)|pretty">{{getRowItem(key)|abbreviated}}</td>
<td>{{getRowValue(key)|number</td>
</tr>
enter code here
</tr>
</table>
If you're using Angular 1.3 and the data in your table is not updated in other moment you must try bind once.
<tr ng-repeat="key in ::rowKeys">
<td>{{::key}}</td>
</tr>
Also ng-mouseenter and ng-mouseleave generate more $watchers, I recommend you use CSS rules to make this effect in your menu.

AngularJS - Directives with Angular UI Bootstrap

I'm using Angular UI - Bootstrap, specifically the Typeahead directive. http://angular-ui.github.io/bootstrap/#/typeahead
I'm attempting to use the 'typeahead-template-url' to create custom html templates for my suggestion boxes. After trying multiple techniques, unsuccessfully, I discovered that by purposely messing up my quotation marks 'solved' the display issues. I would love to understand why this is breaking and get it working properly.
For example: this works
<table class="> <!--see class quote error -->
<tr>
<td>
<div ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)">
<a>ID{{ match.model.id }} - {{ match.model.text }}</a>
</div>
</td>
</tr>
</table>
This DOESN'T WORK
<table class="">
<tr>
<td>
<div ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)">
<a>ID{{ match.model.id }} - {{ match.model.text }}</a>
</div>
</td>
</tr>
</table>
FIDDLE is here: http://jsfiddle.net/nicktest222/JXtaZ/24/
Additionally, when you select an item in the results list, it returns the entire object. How can I get it to return a specific property in the object?
Any help would be greatly appreciated.
Thanks
I think it is the way you add your template (columnTwo.html) in JSFiddle.
Look at my demo (which is based on yours): http://jsbin.com/aMOrOra/1/edit?html,js,output
As for the typeahead property:
<input type="text" ng-model="monkey" typeahead-template-url="columnTwo.html" typeahead="suggestion as suggestion.text for suggestion in sample_data | filter: $viewValue" />
Here it means that the suggestion object is used as the selected value, but I want to display only the suggestion.text property in the box. But monkey will still be set to the selected suggestion object.
Just so you know, your current filter will look for the query on every properties of the suggestion object, not only text.
EDIT: To filter only the text property, use the filter like this:
filter:{'text':$viewValue}

Categories