meaning of :: in angular formly - javascript

i'm trying to learn how to work with angular firmly and i'm having trouble understanding some of the syntax used in the guides and examples on the official website.
when defining a button form control i saw this template:
<div><button type="{{::to.type}}" class="btn btn-{{::to.btnType}}" ng-click="onClick($event)">{{to.text}}</button></div>
my question is: what is the meaning of "::" before the "to.type" and "to.btnType"?
how is it being used?
how is that different from defining it like this:
<a ng-class="{'btn-primary': to.isPrimary, active: to.isActive}" class="btn, btn-default"/>

It is a one-time binding expression, it stops the proliferation of watchers which can often cause performance issues.
Here is some useful reading: http://blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html

This is the one-time binding expression.
In your case, when to.type will have a value set, it will be updated in the HTML template. Then, if the value to.type changes again, the HTML template won't be updated with the new value.
More information can be found on AngularJS website at https://docs.angularjs.org/guide/expression#one-time-binding.

Related

What makes an Angular.JS directive require curly braces around it?

I've been doing a codeschool tutorial on Angular.JS and one section in particular confused me. Here's the code snippet I'm referring to:
<section class="tab" ng-controller="TabController as tabs">
<ul class="nav nav-pills">
<li ng-class="{active:tabs.isSet(1)}">
<a href ng-click="tabs.setTab(1)">Description</a></li>
<li ng-class="{active:tabs.isSet(2)}">
<a href ng-click="tabs.setTab(2)">Specs</a></li>
<li ng-class="{active:tabs.isSet(3)}">
<a href ng-click="tabs.setTab(3)">Reviews</a></li>
</ul>
</section>
This line in particular needs curly braces in the directive to be correct by CodeSchool standards:
<li ng-class="{active:tabs.isSet(1)}">
My question is, how come this other line's directive comes through correctly without the need of curly braces:
<a href ng-click="tabs.setTab(1)">Description</a></li>
To me, it would seem that since I'm accessing the function tabs.setTab() I would need to wrap it in the same way as before. Can anyone explain why this is the case?
Here's the TabController JS code by the way for reference:
app.controller('TabController', function(){
this.tab = 1;
this.setTab = function(newValue){
this.tab = newValue;
};
this.isSet = function(tabName){
return this.tab === tabName;
};
});
First of all, you should understand that these conventions are completely arbitrary. They were decided by the angular authors. They could have easily used some other character, like an exclamation point for example...
ng-class="!active: true, otherClass: false!"
These values are not code. They are just strings (plain text). There is some javascript in angular that parses these strings and makes sense of them. It loops over every character and looks out for certain key characters, like curly braces.
That being said, there is a reason they chose the characters they did. They are trying to mimic javascript in html. The values passed to ng-click represent a function call in javascript. Not coincidentally, this is how you call a function in javascript...
tabs.setTab(1)
The value passed to ng-class, however, is not mimicking a function call. It is mimicking an object. This is how you declare an object in javascript...
{ active: tabs.isSet(1) }
This represents an object with a key of active and a value of tabs.isSet(1), which evaluates to a boolean telling angular whether that class should be applied
It depends on the directive you're using. If you're just trying to set a boolean, a function call or a single boolean controller parameter might suffice. Other directives might be more complicated and have different parameter requirements. You'll find out when you start making your own directives.
In the case of ngClass, it is because you can set multiple classes at once, conditionally. There are several ways of doing this, but the syntax is fairly clear (documentation).
This is an example from the docs:
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
Here's the related plunkr: https://plnkr.co/edit/?p=preview

Knockout.js: how to make a variable available to all viewmodels so it can be used in bindings without explicitly putting it in all viewmodels?

I am refactoring code base based on Knockout.js and have stumped into a problem.
The code uses templates as interpolated strings in typescript eg.
var template = `<div title='${Resources.string1}'> ${Resources.string1} </div>`
The problem is that the strings can contain a single quote which sometimes break the proper HTML parsing after it has been compiled to javascript.
var template = `<div title='I have a ' single quote'> I have a ' single quote</div>`
I want to expose the Resources object so that it is visible in all the view models without explicitly adding the resource object to every view model so that I can use it like this
<div data-bind="title: Resource.string1"> ${Resources.string1} </div>
Or is their anything global to which I can bind this resources object and access it from all view models inside my app.
You can refer to a global variable without any problem.
This one works, here is a demo fiddle.
var Resources = {
string1: "something here with ' quote"
}
window.Resources = Resources;
ko.applyBindings({});
<div data-bind="text: Resources.string1">
</div>
However, this feels like kind of a hack. I'd rather use some HTML encoding for the resources. See some possibilities here. I'd add a getResource function which would return the requested resource in a properly encoded form.

Accessing scope params of children directives

I'm new in AngularJS, using it for two months in a project. I've learned how to use directives and theirs scopes (false, true, obj literal), but there's some questions about it...
First of all, we have some ng-repeats and directives with some behaviors, I tried to present a equivalent scenario in this link.
I didn't figure out how to access a function (testfn - within ng-controller) inside a directive child of another directive myItemDirective. But in myStepDirective it's accessible, I tried to pass it like in the first "layer" but didn't work.
PS.1: I created a myStepDirective with a isolated scope for other examples, if you need, just uncomment to test. Both I got a way to access params/functions from parent (controller), but not inside a grandchild.
Why directive's scope params doesn't work with camel case params? I don't remember to read some hint in AngularJS docs... typewithnocase inside myItemDirective works but typeList not.
Thanks!
EDITED]
For your 1. Here is a working fiddle working with limited scope and camel to snake case conversion : https://jsfiddle.net/wu0avqau/
I spend a loooooong time not understanding why it didn't worked but you juste forgot a = in your ng click inside your second directive
ng-click"testfn()"
For your 2. I can refer you to the documentation : https://docs.angularjs.org/guide/directive
Normalization
Angular normalizes an element's tag and attribute name to determine which >elements match which directives. We typically refer to directives by their case->sensitive camelCase normalized name (e.g. ngModel). However, since HTML is case->insensitive, we refer to directives in the DOM by lower-case forms, typically >using dash-delimited attributes on DOM elements (e.g. ng-model).
The normalization process is as follows:
Strip x- and data- from the front of the element/attributes.
Convert the :, -, or _-delimited name to camelCase."
basicaly your myItemDirective would be my-item-directive inside your template but still be myItemDirective inside your js.
Good luck,
Thibaud Lamarche

Other ways to bind HTML to JavaScript

I know two ways to bind HTML to client-side JavaScript code and stay a kind of object-oriented:
Use a lot of IDs (or special CSS class names, or some other distinct HTML attributes) in HTML and do a "harvest" in JS initialization method (or request each DOM object each time, right before use);
Do not write HTML at all. Construct an element at runtime, in initializer, and remember a reference to DOM object (or jQuery object) in a variable.
Are there some other ways that allow to use design-time phase (writhing HTML) which is much more convenient than doing all the work at runtime, and at the same time do not use a lot of identifiers of any kind having to maintain their uniqueness?
AngularJS is the framework you want to use for 2-way data binding.
I used AngularJS for multiple projects now, combined with nodeJS, and I never looked back at jQuery, you keep your code clean with the MVC pattern and manipulating the DOM is made easy and clear.
Example for 2 way data binding:
HTML
<p>{{elementText}}</p>
<input type="text" ng-model="item.value" />
JavaScript/Controller
$scope.item = {
value: ''
};
$scope.elementText = "The text you want to display";
console.log($scope.item.value); //Directly get your values from the scope.
Want to assign values to <select> boxes or fill <table>'s using JSON data? No problem, AngularJS got you covered.
Interesting AngularJS features:
ng-model
ng-repeat
Animations
Custom directives
I hope this will help you!

Why is using onClick() in HTML a bad practice?

I have heard many times that using JavaScript events, such as onClick(), in HTML is a bad practice, because it's not good for semantics. I would like to know what the downsides are and how to fix the following code?
link
You're probably talking about unobtrusive Javascript, which would look like this:
link
with the logic in a central javascript file looking something like this:
$('#someLink').click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
The advantages are
behaviour (Javascript) is separated from presentation (HTML)
no mixing of languages
you're using a javascript framework like jQuery that can handle most cross-browser issues for you
You can add behaviour to a lot of HTML elements at once without code duplication
If you are using jQuery then:
HTML:
<a id="openMap" href="/map/">link</a>
JS:
$(document).ready(function() {
$("#openMap").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
});
This has the benefit of still working without JS, or if the user middle clicks the link.
It also means that I could handle generic popups by rewriting again to:
HTML:
<a class="popup" href="/map/">link</a>
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), 300, 300, 'map');
return false;
});
});
This would let you add a popup to any link by just giving it the popup class.
This idea could be extended even further like so:
HTML:
<a class="popup" data-width="300" data-height="300" href="/map/">link</a>
JS:
$(document).ready(function() {
$(".popup").click(function(){
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
return false;
});
});
I can now use the same bit of code for lots of popups on my whole site without having to write loads of onclick stuff! Yay for reusability!
It also means that if later on I decide that popups are bad practice, (which they are!) and that I want to replace them with a lightbox style modal window, I can change:
popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
to
myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
and all my popups on my whole site are now working totally differently. I could even do feature detection to decide what to do on a popup, or store a users preference to allow them or not. With the inline onclick, this requires a huge copy and pasting effort.
It's not good for several reasons:
it mixes code and markup
code written this way goes through eval
and runs in the global scope
The simplest thing would be to add a name attribute to your <a> element, then you could do:
document.myelement.onclick = function() {
window.popup('/map/', 300, 300, 'map');
return false;
};
although modern best practise would be to use an id instead of a name, and use addEventListener() instead of using onclick since that allows you to bind multiple functions to a single event.
With very large JavaScript applications, programmers are using more encapsulation of code to avoid polluting the global scope. And to make a function available to the onClick action in an HTML element, it has to be in the global scope.
You may have seen JS files that look like this...
(function(){
...[some code]
}());
These are Immediately Invoked Function Expressions (IIFEs) and any function declared within them will only exist within their internal scope.
If you declare function doSomething(){} within an IIFE, then make doSomething() an element's onClick action in your HTML page, you'll get an error.
If, on the other hand, you create an eventListener for that element within that IIFE and call doSomething() when the listener detects a click event, you're good because the listener and doSomething() share the IIFE's scope.
For little web apps with a minimal amount of code, it doesn't matter. But if you aspire to write large, maintainable codebases, onclick="" is a habit that you should work to avoid.
Revision
Unobtrusive JavaScript approach was good in the PAST - especially events handler bind in HTML was considered as bad practice (mainly because onclick events run in the global scope and may cause unexpected error what was mention by YiddishNinja)
However...
Currently it seems that this approach is a little outdated and needs some update. If someone want to be professional frontend developper and write large and complicated apps then he need to use frameworks like Angular, Vue.js, etc... However that frameworks usually use (or allow to use) HTML-templates where event handlers are bind in html-template code directly and this is very handy, clear and effective - e.g. in angular template usually people write:
<button (click)="someAction()">Click Me</button>
In raw js/html the equivalent of this will be
<button onclick="someAction()">Click Me</button>
The difference is that in raw js onclick event is run in the global scope - but the frameworks provide encapsulation.
So where is the problem?
The problem is when novice programmer who always heard that html-onclick is bad and who always use btn.addEventListener("onclick", ... ) wants to use some framework with templates (addEventListener also have drawbacks - if we update DOM in dynamic way using innerHTML= (which is pretty fast) - then we loose events handlers bind in that way). Then he will face something like bad-habits or wrong-approach to framework usage - and he will use framework in very bad way - because he will focus mainly on js-part and no on template-part (and produce unclear and hard to maintain code). To change this habits he will loose a lot of time (and probably he will need some luck and teacher).
So in my opinion, based on experience with my students, better would be for them if they use html-handlers-bind at the beginning. As I say it is true that handlers are call in global scope but a this stage students usually create small applications which are easy to control. To write bigger applications they choose some frameworks.
So what to do?
We can UPDATE the Unobtrusive JavaScript approach and allow bind event handlers (eventually with simple parameters) in html (but only bind handler - not put logic into onclick like in OP quesiton). So in my opinion in raw js/html this should be allowed
<button onclick="someAction(3)">Click Me</button>
or
function popup(num,str,event) {
let re=new RegExp(str);
// ...
event.preventDefault();
console.log("link was clicked");
}
link
But below examples should NOT be allowed
<button onclick="console.log('xx'); someAction(); return true">Click Me</button>
link
The reality changes, our point of view should too
There are a few reasons:
I find it aids maintenence to separate markup, i.e. the HTML and client-side scripts. For example, jQuery makes it easy to add event handlers programatically.
The example you give would be broken in any user agent that doesn't support javascript, or has javascript turned off. The concept of progressive enhancement would encourage a simple hyperlink to /map/ for user agents without javascript, then adding a click handler prgramatically for user agents that support javascript.
For example:
Markup:
<a id="example" href="/map/">link</a>
Javascript:
$(document).ready(function(){
$("#example").click(function(){
popup('/map/', 300, 300, 'map');
return false;
});
})
It's a new paradigm called "Unobtrusive JavaScript". The current "web standard" says to separate functionality and presentation.
It's not really a "bad practice", it's just that most new standards want you to use event listeners instead of in-lining JavaScript.
Also, this may just be a personal thing, but I think it's much easier to read when you use event listeners, especially if you have more than 1 JavaScript statement you want to run.
Your question will trigger discussion I suppose. The general idea is that it's good to separate behavior and structure. Furthermore, afaik, an inline click handler has to be evalled to 'become' a real javascript function. And it's pretty old fashioned, allbeit that that's a pretty shaky argument. Ah, well, read some about it #quirksmode.org
onclick events run in the global scope and may cause unexpected
error.
Adding onclick events to many DOM elements will slow down the
performance and efficiency.
Two more reasons not to use inline handlers:
They can require tedious quote escaping issues
Given an arbitrary string, if you want to be able to construct an inline handler that calls a function with that string, for the general solution, you'll have to escape the attribute delimiters (with the associated HTML entity), and you'll have to escape the delimiter used for the string inside the attribute, like the following:
const str = prompt('What string to display on click?', 'foo\'"bar');
const escapedStr = str
// since the attribute value is going to be using " delimiters,
// replace "s with their corresponding HTML entity:
.replace(/"/g, '"')
// since the string literal inside the attribute is going to delimited with 's,
// escape 's:
.replace(/'/g, "\\'");
document.body.insertAdjacentHTML(
'beforeend',
'<button onclick="alert(\'' + escapedStr + '\')">click</button>'
);
That's incredibly ugly. From the above example, if you didn't replace the 's, a SyntaxError would result, because alert('foo'"bar') is not valid syntax. If you didn't replace the "s, then the browser would interpret it as an end to the onclick attribute (delimited with "s above), which would also be incorrect.
If one habitually uses inline handlers, one would have to make sure to remember do something similar to the above (and do it right) every time, which is tedious and hard to understand at a glance. Better to avoid inline handlers entirely so that the arbitrary string can be used in a simple closure:
const str = prompt('What string to display on click?', 'foo\'"bar');
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => alert(str);
Isn't that so much nicer?
The scope chain of an inline handler is extremely peculiar
What do you think the following code will log?
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Try it, run the snippet. It's probably not what you were expecting. Why does it produce what it does? Because inline handlers run inside with blocks. The above code is inside three with blocks: one for the document, one for the <form>, and one for the <button>:
let disabled = true;
<form>
<button onclick="console.log(disabled);">click</button>
</form>
Since disabled is a property of the button, referencing disabled inside the inline handler refers to the button's property, not the outer disabled variable. This is quite counter-intuitive. with has many problems: it can be the source of confusing bugs and significantly slows down code. It isn't even permitted at all in strict mode. But with inline handlers, you're forced to run the code through withs - and not just through one with, but through multiple nested withs. It's crazy.
with should never be used in code. Because inline handlers implicitly require with along with all its confusing behavior, inline handlers should be avoided as well.

Categories

Resources