How can I use ngClass map syntax with the && and ! boolean operators?
<div class="tbTileContent"
ng-class=
"{'highlight': ((obj.Count == 1 &&
setActionType != 1 && setActionType != 20 )},[obj.BackgroundColor,obj.ColumnColor]">
Does not work. Is this possible some other way?
Use equality checks with reference to the rootScope to simplify debugging, and use the developer tool to check CSS specificity:
.foo { color: red; }
.bar { color: blue; }
.baz { color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
function bar($rootScope)
{
$rootScope.conditionA = true;
$rootScope.conditionB = function() { return true; };
}
angular.module('foo',[]);
angular.module('foo').run(bar);
</script>
<div ng-app="foo">
<div class="existing" ng-class="{baz: conditionA && conditionB(), foo: conditionA, bar: conditionB()}">
Hi
</div>
</div>
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the services have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.
References
AngularJS Developer Guide: Modules - Run Blocks
AngularJS Source: ngClassSpec.js - 'should support adding multiple classes conditionally via a map of class names to boolean'
Angular.js Source: parseSpec.js - 'should give higher precedence to equality than to the logical and operator'
Related
Creation of a complex formula blot, such as the one below, is successful using an Embed, but it works only in a single instance. All other instances are truncated. Attempts to create it as an Inline or Block resulted in errors.
I am developing a graphic formula editor, with a dialog and toolbars of its own, and it is successfully getting the delta from an existing blot. Unfortunately, saving more than one formula fails, and replacing an existing formula fails. When saving more than one formula, only the top level spans are saved for all but the first.
It isn't clear from the documentation if the tag hierarchy can be used as an Inline or Block element.
As a side note, I am building a complete Quill npm module for Angular 7+ apps that includes a number of other quill modules as well, translated to Typescript. The npm module will support WCAG compliance with large buttons, etc. that can be toggled.
Example tag hierarchy to be used as a blot:
<span class="pl3-quill-formula-blot" data-id="pl3qfml_1560288135645" data-editors="[{"id":1560288133767,"parentId":null,"depth":0,"position":0,"fElement":{"name":"cube root","text":"∛","type":"O","shape":"R","latex":null,"values":["x"],"nValues":1}},{"id":1560288133768,"parentId":0,"depth":0,"position":1,"fElement":{"name":"-EMPTY-","text":"","type":"S","shape":"C","latex":null,"latexCb":null,"values":[""],"nValues":1}}]">
<span contenteditable="false">
<span class="katex">
<span class="katex-mathml"><math><semantics><mrow><mroot><mi>x</mi><mn>3</mn></mroot></mrow><annotation encoding="application/x-tex">\sqrt[3] {x} </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height: 1.04em; vertical-align: -0.23972em;"></span><span class="mord sqrt"><span class="root"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height: 0.658556em;"><span class="" style="top: -2.83634em;"><span class="pstrut" style="height: 2.5em;"></span><span class="sizing reset-size6 size1 mtight"><span class="mord mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.80028em;"><span class="svg-align" style="top: -3em;"><span class="pstrut" style="height: 3em;"></span><span class="mord" style="padding-left: 0.833em;"><span class="mord mathdefault">x</span></span></span><span class="" style="top: -2.76028em;"><span class="pstrut" style="height: 3em;"></span><span class="hide-tail" style="min-width: 0.853em; height: 1.08em;"><svg width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,
-10,-9.5,-14c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54c44.2,-33.3,65.8,
-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10s173,378,173,378c0.7,0,
35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429c69,-144,104.5,-217.7,106.5,
-221c5.3,-9.3,12,-14,20,-14H400000v40H845.2724s-225.272,467,-225.272,467
s-235,486,-235,486c-2.7,4.7,-9,7,-19,7c-6,0,-10,-1,-12,-3s-194,-422,-194,-422
s-65,47,-65,47z M834 80H400000v40H845z"></path></svg></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height: 0.23972em;"><span class=""></span></span></span></span></span></span></span>
</span>
</span>
</span>
Blot Code:
import {Pl3QuillFormulaResult} from './pl3-quill-formula.component';
const Quill = require('quill');
const Embed = Quill.import('blots/embed');
class Pl3QuillFormulaBlotComponent extends Embed {
static blotName = 'pl3-quill-formula';
static className = 'pl3-quill-formula-blot';
static tagName = 'span';
constructor(domNode: Element, value: any) {
super(domNode);
}
static create(result: Pl3QuillFormulaResult) {
const node = super.create(result.id);
node.setAttribute('data-id', result.id);
node.setAttribute('data-editors', result.editorsAsString);
node.appendChild(result.domElement);
return node;
}
/**
* Quill uses this to return a Delta with the attributes in the return.
*/
static formats(domNode: Element) {
return {
editors: domNode.getAttribute('data-editors'),
id: domNode.getAttribute('data-id')
};
}
static value(domNode: HTMLElement) {
return {
id: domNode.dataset.id,
editors: domNode.dataset.editors
};
}
public remove() {
if (this.prev == null && this.next == null) {
this.parent.remove();
}
else {
super.remove();
}
}
}
Quill.register('formats/pl3-quill-formula', Pl3QuillFormulaBlotComponent);
This is the call to insert the formula that is not working for subsequent instances, although it works for the first formula:
if (result) {
const range = this.quill.getSelection(true);
this.quill.insertEmbed(range.start, 'pl3-quill-formula', result, Quill.sources.API);
this.quill.setSelection(range.length + 1);
}
Formula Editor UI Snapshot
The expected results are to be able to insert the formula within the editor, inline, and to be able to replace it when the formula is updated.
I am trying to implement a hierarchical structure in the form of a tree in a complex angular.js web app. I am using nested ng-repeats to render the structure but I am getting significant performance related issues in IE 10 and minor performance issues in chrome. Data that will be used will contain as many as 5,000 entries at the final level.
Based on my research I think following could be the reasons behind it:
Large number of watcher elements.
To tackle this I have already implemented one time data binding and number of watchers is not that high.
Browser repaint time:
ng-repeat adds the elements to the DOM one by one. This could result in overloading of browser engine to render complex HTML multiple number of times causing large amount of lags.
To tackle this I have applied lazy loading sort of technique by rendering the child only when one node is collapsed. Still I am experiencing an observable delay in rendering of nodes where the number of nodes to be rendered is large.
CSS classes:
I tried implementing the tree structure by stripping off all the classes from node elements. This resulted in significant improvement but removing classes is not really an option. Also if I give inline style to the elements then it also results in better performance.
Performance issues of Angular material:
Angular material is the integral part of my web app. After looking into the issues submitted by angular material users for large amount of ng-repeats to which fixes have already been rolled out. But upgrading to latest version didn't help either.
Please refer to this image for the table design. Template used for the creation of tree is as follows:
<li ng-repeat="item in ::item.childLevelDetails" >
<div >
<a ng-click="toggle(this)" class="icon icon-stream-add-2">
<span></span>
</a>
<div class="unitTextDiv">{{::item.title}}</div>
</div>
<ol ng-include= "'tree_node'">
</li>
Request you to suggest any possible solutions to this problem.
angular-ui-grid can be considered as one option. It uses virtualization and renders only the rows that are visible. So, it performs well with huge number of rows too. It comes with a great documentation and examples http://ui-grid.info/docs/#/tutorial
Refer to the grouping example to make sure that this helps for your use case http://ui-grid.info/docs/#/tutorial/209_grouping
Time to use stick with this potion and finalize according to black magic!
You can try this recursive sample I did for you.
Using ng-if for showing/hidding element will reduce the amount of watches.
Here find the Fiddler
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', ['$scope','$timeout', 'getWatchCount' , function ($scope ,$timeout, getWatchCount ){
$scope.tree = [
{title:'Element level 1',
elements: [
{ title: 'Element level 1.1'},
{ title: 'Element level 1.2',
elements: [
{ title: 'Element level 1.2.2'},
{ title: 'Element level 1.2.2'},
]}
]},
{title:'Element level 2'}
]
//NEXT CODE ONLY USED FOR COUNTING WATCHES//
$scope.countWatches = function(){
$scope.numberOfWatches = getWatchCount();
}
$timeout(function(){$scope.countWatches()} , 0 );
// I return the count of watchers on the current page.
function getWatchCount() {
// Keep track of the total number of watch bindings on the page.
var total = 0;
// There are cases in which two different ng-scope markers will actually be referencing
// the same scope, such as with transclusion into an existing scope (ie, cloning a node
// and then linking it with an existing scope, not a new one). As such, we need to make
// sure that we don't double-count scopes.
var scopeIds = {};
// AngularJS denotes new scopes in the HTML markup by appending the classes "ng-scope"
// and "ng-isolate-scope" to appropriate elements. As such, rather than attempting to
// navigate the hierarchical Scope tree, we can simply query the DOM for the individual
// scopes. Then, we can pluck the watcher-count from each scope.
// --
// NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service to access the DOM
// (Document Object Model). But, in this case, we're not really building a true AngularJS
// service, so we can break the rules a bit.
angular.forEach(
document.querySelectorAll( ".ng-scope , .ng-isolate-scope" ),
countWatchersInNode
);
return( total );
// ---
// PRIVATE METHODS.
// ---
// I count the $watchers in to the scopes (regular and isolate) associated with the given
// element node, and add the count to the running total.
function countWatchersInNode( node ) {
// Get the current, wrapped element.
var element = angular.element( node );
// It seems that in earlier versions of AngularJS, the separation between the regular
// scope and the isolate scope where not as strong. The element was flagged as having
// an isolate scope (using the ng-isolate-scope class); but, there was no .isolateScope()
// method before AngularJS 1.2. As such, in earlier versions of AngularJS, we have to
// fall back to using the .scope() method for both regular and isolate scopes.
if ( element.hasClass( "ng-isolate-scope" ) && element.isolateScope ) {
countWatchersInScope( element.isolateScope() );
}
// This class denotes a non-isolate scope in later versions of AngularJS; but,
// possibly an isolate-scope in earlier versions of AngularJS (1.0.8).
if ( element.hasClass( "ng-scope" ) ) {
countWatchersInScope( element.scope() );
}
}
// I count the $$watchers in the given scope and add the count to the running total.
function countWatchersInScope( scope ) {
// Make sure we're not double-counting this scope.
if ( scopeIds.hasOwnProperty( scope.$id ) ) {
return;
}
scopeIds[ scope.$id ] = true;
// The $$watchers value starts out as NULL until the first watcher is bound. As such,
// the $$watchers collection may not exist yet on this scope.
if ( scope.$$watchers ) {
total += scope.$$watchers.length;
}
}
}
}]);
myApp.factory(
"getWatchCount",
function() {
// I return the count of watchers on the current page.
function getWatchCount() {
var total = 0;
// AngularJS denotes new scopes in the HTML markup by appending the
// class "ng-scope" to appropriate elements. As such, rather than
// attempting to navigate the hierarchical Scope tree, we can simply
// query the DOM for the individual scopes. Then, we can pluck the
// watcher-count from each scope.
// --
// NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service
// to access the DOM (Document Object Model). But, in this case,
// we're not really building a true AngularJS service, so we can
// break the rules a bit.
angular.element( ".ng-scope" ).each(
function ngScopeIterator() {
// Get the scope associated with this element node.
var scope = $( this ).scope();
// The $$watchers value starts out as NULL.
total += scope.$$watchers
? scope.$$watchers.length
: 0
;
}
);
return( total );
}
// For convenience, let's serialize the above method and convert it to
// a bookmarklet that can easily be run on ANY AngularJS page.
getWatchCount.bookmarklet = (
"javascript:alert('Watchers:'+(" +
getWatchCount.toString()
.replace( /\/\/.*/g, " " )
.replace( /\s+/g, " " ) +
")());void(0);"
);
return( getWatchCount );
}
);
ul{
list-style-type: none;
}
li{
font-size:13px;
}
.arrow{
width: 0;
height: 0;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-right: 7px solid transparent;
cursor: pointer;
margin-left: 5px;
border-left: 7px solid #000;
display: inline-block;
transition:all 0.3s;
}
.arrow.expand {
transform: rotate(45deg);
transform-origin: 20% 50%;
margin-top: 0;
}
.arrow.none {
border-left: 7px solid #ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl" >
<p>
<strong>Watch Count:</strong> {{ numberOfWatches }}
</p>
<script type="text/ng-template" id="elementTree">
<li>
<div class="arrow"
ng-class="{expand:element.isOpen,none:!element.elements}"
ng-click="$apply(element.isOpen = !element.isOpen) ; countWatches()">
</div>
{{element.title}}
</li>
<div ng-if="element.isOpen">
<ul
ng-repeat="element in element.elements"
ng-include="'elementTree'">
</ul
</div>
</script>
<ul ng-repeat="element in tree"
ng-include="'elementTree'">
</ul>
</div>
This is a question about Paged Media.
I want to mix a string-set with running elements. It works in PrinceXML but not in PDFReactor. My question, is this possible in PDFReactor?
HTML
<p class="head">
<span class="first">Repeatable title</span>
<span class="divider">|</span>
<span class="last"></span>
</p>
CSS
p.head {
position: running(heading);
font-size: 8pt;
margin: 0;
padding: 0;
}
#page {
size: A4;
#top-left {
content: element(heading);
}
}
So far everything is peachy. But when I try to define a string-set from a H1 and try to write this into span.last this isn't working.
h1 {
string-set: doctitle content();
}
p.head span.last {
content: string(doctitle);
}
This is possible with PDFreactor as well. Just the syntax is a bit different. PDFreactor does not support the content() function for the string-set property with Named Strings. Instead it uses the self value which works like content() or content(text) (see http://www.pdfreactor.com/product/doc_html/index.html#NamedStrings )
There is a second issue. You are setting the content property on the span element itself. Usually in CSS, creating generated content with the content property is actually only allowed for page margin boxes and pseudo elements such as ::before and ::after. This is also how browsers support it. Not sure why this works in Prince.
So basically you just have to make 2 minor adjustments to your style sheet to make this work in PDFreactor:
h1 {
string-set: doctitle self;
}
p.head span.last::before {
content: string(doctitle);
}
I'm a bit of an Angular novice an I'm struggling with something I thought would be very simple.
I have a template that is used in a read only version of a form in my app. This template displays a status field that would be styled (text/background colour) based on its value, e.g
'New issue' - orange
'In progress' - green
'Overdue' - red
etc...
My css is something like;
.status-bar {padding: 3px; border: solid 1px #333}
.new-issue {background-color: orange; color: #000}
.in-progress {background-color: green; color: #fff}
.overdue {background-color: red; color: #fff}
The issue status field is available through the controller and i use code something like this to display it.
<div class="status-bar">{{issue.status}}</div>
All works fine.
I then naively tried to simply insert the class name as an expression, e.g
<div class="status-bar {{issue.status}}">{{issue.status}}</div>
Thinking that would give me a this kind of output..
<div class="status-bar overdue">overdue</div>
But it doesn't work. I've looked at the ng-class directive, and other options but can't work this out.
Ideally I need a solution that would allow me to append/insert a class name based on a value (not a true/false like ng-class). So I'd like my oputput HTML to be like the following...
<div class="status-bar in-progress">In Progress</div>
OR
<div class="status-bar new-issue">New Issue</div>
OR
<div class="status-bar overdue">overdue</div>
etc...
The range of status values may change so my class names must be calculated as per the above pattern
e.g,
<div class="status-bar another-status">Another Status</div>
So I need a way to hyphenate and lowercase my issue.status value and then add it as a class name. I presume a directive is the way forward, which would be ideal so I can use it in other views.
This is so easy to do after the fact in jQuery etc..., but I can't see the 'Angular' way to do it?!
Many thanks upfront for any help provided...
ng-class with a custom filter is what you are looking for...
HTML
<h1 ng-class="class1">{{class1 | titleCase}}</h1>
JS
app.filter('titleCase', function () {
return function (input) {
var words = input.split('-');
for (var i = 0; i < words.length; i++) {
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
}
return words.join(' ');
}
});
and here is working PLUNKER...
ng-class should work :
<div class="status-bar" ng-class="issue.status">{{issue.status}}</div>
Working fiddle : http://jsfiddle.net/w2SFr/2/
What You are looking for is probably http://docs.angularjs.org/api/ng/directive/ngClass. You may also want to look at AngularJS ngClass conditional
How can I count element by class name in angularJs?
I have tried with:
$scope.numItems = function() {
$window.document.getElementsByClassName("yellow").length;
};
Plunkr: http://plnkr.co/edit/ndCqpZaALfOEiYieepcn?p=preview
You have defined your function correctly, but made a mistake in showing its results: it should have been...
<p>{{numItems()}}</p>
... instead of plain {{numItems}}. You want to display the return value of the function, and not the function itself (that's meaningless), that's why you should follow the standard JS syntax for a function invocation.
Note that you can send arguments into this expression too: for example, I've rewritten that method like this:
$scope.numItems = function(className) {
return $window.document.getElementsByClassName(className).length;
};
... and then made three different counters in the template:
<p>Yellow: {{numItems('yellow')}}</p>
<p>Green: {{numItems('green')}}</p>
<p>Red: {{numItems('red')}}</p>
Plunker Demo.
But here's the real problem: numItems() result, used in one View, is based on DOM traversal - in other words, on another View. Not only that goes against Angular philosophy in general, it tends to break. In fact, it DOES break since this commit, as old as 1.3.0:
Now, even when the ngAnimate module is not used, if $rootScope is in
the midst of a digest, class manipulation is deferred. This helps
reduce jank in browsers such as IE11.
See, changes in classes are applied after digest - and that's after numItems() is evaluated, hence the delay in demo mentioned by #Thirumalaimurugan.
A quick-and-dirty solution is using another attribute for selector in numItems (in this plunker, it's data-color). But I would strongly advise you against it. The proper approach would be adding the data rendered by numItems() -using component into the model. For example:
app.js
// ...
var colorScheme = {
'toggle': {true: 'yellow', false: 'red'},
'toggle2': {true: 'green', false: 'red'},
'toggle3': {true: 'green', false: 'red'},
'toggle4': {true: 'red', false: 'green'}
};
$scope.getColor = function getColor(param) {
return colorScheme[param][$scope[param]];
};
$scope.countColor = function(color) {
return Object.keys(colorScheme).filter(function(key) {
return colorScheme[key][$scope[key]] === color;
}).length;
};
index.html
<p ng-class="getColor('toggle')">{{name}}</p>
<!-- ... -->
<p ng-repeat="color in ['Yellow', 'Green', 'Red']"
ng-bind="color + ' ' + countColor(color.toLowerCase())">
Demo.