In an AngluarJS app, I've a data populated table, and want a cell to not display the raw data but a formatted version of it, actually a link to a web page, with an url using the raw data. In other words, something like:
<td>[RAW_DATA]</td>
I see 4 ways to achieve that:
1: include the link in the template using something like this :
<td>{{raw_data}}</td>
2: using a custom directive, like this :
JS
directive('custom_link', function() {
return{
restrict: 'E',
template:'{{raw_data}}'
}
});
HTML
<td><custom_link /></td>
3: using a custom filter, like this :
JS
filter('custom_link', function() {
return function(raw_data) {
return "http//www.website.com/"+raw_data;
};
})
HTML
<td>{{raw_data}}</td>
4: using a custom directive AND a custom filter, like this:
JS
directive('custom_link', function() {
return{
restrict: 'E',
template:'{{raw_data}}'
}
});
filter('custom_link_filter', function() {
return function(raw_data) {
return "http//www.website.com/"+raw_data;
};
})
HTML
<td><custom_link /></td>
Which "solution" is the most "elegant", or "AngularJS compliant"?
Thanks
I always try and subscribe to the KISS (keep it simple) principle, if you don't need the more complected solutions and a simpler one provides the needed functionality, use it.
Given that each of the above solutions work or rather give you the needed outcome.
I would prefer Solution 1 as it is easier to read and understand.
Solution 2 is basically the same approach as solution 1, but would be a better solution if you need the same functionality in a number of places or you want something more expandable in the future. Directives are great for code reuse.
I personally would not use solutions 3 and 4 as they are more complicated then I feel is needed for what you are describing in the question.
If however you need more complicated data formatting then a filter would be the correct choice. So basically, it comes down to what you are needing to accomplish that determines the correct implementation, but the simplest is usually the best.
1,2 Use a directive for creating reusable HTML components. Always. If reuse is not your goal for this usecase, then try to Keep it simle (KISS)
3,4,5 They all work but make sure that you are gaining something with the added complexity. Added complexity with no added advantage is just not worth the hassle.
Related
I've been attempting to implement a ASP.NET MVC custom validation method. Tutorials I've used such as codeproject explain that you add data-val-customname to the element. Then jQuery.validate.unobtrusive.js then uses the third segment of the attribute
data-val-<customname>
as the name of the rule, as shown below.
$.validator.addMethod('customname', function(value, element, param) {
//... return true or false
});
However I just can't get the customname method to fire. By playing around I have been able to get the below code to work, but according to all the sources I've read Unobtrusive validation should not work like this.
$.validator.addMethod('data-val-customname', function(value, element, param) {
//... return true or false
});
I've posted an example of both methods
jsfiddle example
Any help would be much appreciated
I've updated my question hopefully to make clearer.
I have finally found got there in the end, but still feels like too much hard work and therefore I've probably got something wrong. Initial I was scuppered by a bug in Chrome Canary 62 which refused to allow the adding of a custom method.
My next issue was having to load jQuery, jQuery.validate and jQuery.validate.unobtrusive in the markup and then isolate javascript implementation in a ES6 class. I didn't want to add my adaptors before $().ready() because of my class structure and loading of the app file independent of jQuery. So I had to force $.validator.unobtrusive.parse(document);.
Despite this I was still having issues and finally debugged the source code and found that an existing validator information that is attached to the form was not merging with the updated parsed rules, and essentially ignoring any new adaptors added.
My final work around and admit feels like I've done too much, was to destroy the initial validation information before my forced re-parse.
Here is the working jsfiddle demo
Here is some simplified code
onJQueryReady() {
let formValidator = $.data(document.querySelector('form'), "validator" );
formValidator.destroy();
$.validator.unobtrusive.adapters.add("telephone", [], function (options) {
options.rules['telephone'] = {};
options.messages['telephone'] = options.message;
});
$.validator.unobtrusive.parse(document);
$.validator.addMethod("telephone", this.handleValidateTelephoneNumber);
}
I've found that angular doesn't take advantage of order the directives are defined in, instead it uses static priority field. It does not suite well for all cases.
Example:
<div ng-if="items.length < 50" ng-repeat="item in items"></div>
<div ng-repeat="item in items" ng-if="items.length > 50"></div>
These two lines of code can have different meaning.
Case 1) check if amount of items less than 50, ng-repeat 10000 items
Case 2) ng-repeat 10000 items, then on each element check if amount of items is less than 50
If ng-if had higher priority the first line of code would allow obviously very important optimization...
Obviously there is not much reason for using static "priority" field compared to prioritizing directives with order they are defined in, so my question is:
What steps should be taken to approve this idea and have it implemented?
(I was never looking into angularjs source code, some external help is required to point-out the places that need to be changed in order for providing the subject approach, I would be thankful for any external help in this direction)
Thank you in advance!
Edit
Here's a jsFiddle showing that ng-if is getting executed 20000 times for an array of 10000 items, each ng-if creates a scope which doubles the problem...
http://jsfiddle.net/u840t0dh/17/
I can't find a reason on why angular doesn't take advantage of order the directives are defined in, rather than using static priority field, it does not suite well for all cases.
The idea of angular directives is to extend browser built-in markup languages (tags, attributes, ...). I believe there is no such feature in standard browser markup. I want to stress that directives in angular is declarative programming.
For the problem you pointed out, it looks to me like imperative programming. It's like you're coding your logic in the page with an if and a loop. But in your case, it does not make much sense to use them on the same element => it's very similar to writing if and for loop on the same line in any imperative programming languages (c++, c#, java,...). It makes more sense to write it like this if you follow imperative programming mindset:
<div ng-if="items.length > 50">
<div ng-repeat="item in items"></div>
</div>
I agree that sometimes in order to write UI rendering code, we have to write a bit like imperative programming but it could be kept to the bare minimum. Even in those case, if you follow the mindset of imperative programming (like the one I pointed out), there should not be a problem.
Therefore, the problem you pointed could be a problem but actually not a big problem compared to the advantages that we gain from declarative markups.
Declarative programming and imperative programming has their own pros and cons:
http://en.wikipedia.org/wiki/Declarative_programming
https://softwareengineering.stackexchange.com/questions/32781/declarative-programming-vs-imperative-programming
For the case of directives to extend "markup", it makes more sense with declarative programming which is the way angular directives were designed (markup is really declarative)
The point of declarative programming is about telling "what" you want, not "how" to do it. That comes with the benefit: it's simpler and easier to understand.
From MSDN
In fact, the ease of understanding code—even in an unfamiliar
context—is one of the principal benefits of a declarative style.
The most notable benefit of declarative programming is that programs
specify “what” we want to get as the result rather than “how”. As a
result, it is more important to understand the domain that we’re
working with than every detail of the language.
With your suggestion, it's more about "how" you want to get a task done. The downside of your suggestion is that when we use directives, we need to understand how they work "internally".
For example: when you use many directives on the same element, the person who uses the directives need to understand what is the order to put the directives (that's should be the concern of the person who implements the directives). That's something declarative programming tries to avoid because it's complex for users of the directives
To answer your question, there are several possible use cases for Priority. I am not saying they are common, but they are possible and potentially very powerful…
Here are two examples:
Use case A) Wrapping any directive.
By using priority you can get custom code running before or after a given directive.
Suppose you have an existing angular application with thousands of ng-click directives, and want to run something before every single ng-click. You can do something like this:
angular.module('app', [])
.directive('ngClick', function($rootScope) {
return {
restrict: 'A',
priority: 100,
link: function(scope, element, attr) {
element.bind('click', function(ev) {
alert("custom click running");
})
}
}
})
.controller('MyCtrl', function($scope) {
$scope.alert = function() {
alert('built-in click running!')
}
})
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
</head>
<body ng-controller="MyCtrl">
<a ng-click="alert()">Click me</a>
</body>
</html>
Here your custom click will run before angular build in click.
Use case B) terminating execution
By using priority and terminal: true, you can prevent other directives from executing. For example on all delete actions you can use a custom directive to ask for confirmation. in this case using DOM defined order will be dangerous because you are deleting records.
var app = angular.module('terminal', []);
app.controller('MainCtrl', function($scope) {
$scope.deleteIt = function() {
window.alert('Delete called!');
}
});
app.directive('confirmationNeeded', function() {
return {
priority: 10,
terminal: true,
link: function(scope, element, attr) {
var msg = attr.confirmationNeeded || "Are you sure?";
var clickAction = attr.ngClick;
element.bind('click', function() {
if (window.confirm(msg)) {
scope.$eval(clickAction)
}
});
}
};
});
<!doctype html>
<html ng-app="terminal">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
</head>
<body ng-controller="MainCtrl">
Delete with custom message
</body>
</html>
I'm trying to make a blurred background efect with angular on a div, something like the image above, for this i'm using blur.js and everything work fine with jquery but the real question is this posible with angularjs, and what is the best way?
I'm really newbie with angular
thanks in advance
here another example of using blurjs http://www.designedbyaturtle.co.uk/2013/blur-a-background-image/
SOLVED (with help of gtramontina):
you can download the code here demo-blurjs-angular
The result (with my images)
This demo contain a issue (that is really of blur.js) like Edd Turtle mention on his post
Important Note: This technique doesn’t work with local files and has to run through a server, likewise if the background is hosted on the Amazon S3 service you will need to enable cross-domain calls (CORS)..
I suggest you create a directive, restricted to attribute maybe, and have that applying your effect.
Something like this (not tested - and assuming you've included, in this order, jquery, blur.js, then angular;
angular.module('myApp', []).
directive('blurred', function () {
var directive = { restrict: 'A' };
directive.compile = function compile (tElement) {
// taken from blur.js homepage
tElement.blurjs({
source: 'body',
radius: 7,
overlay: 'rgba(255,255,255,0.4)'
});
};
return directive;
});
Then use it:
<p blurred>lorem ipsum</p>
The point with the order I mentioned above, is that if you include jquery before angular, then angular uses it to wrap its dom elements, otherwise it'll use jqlite.
You need to write a angularjs directive for blur.js. Here is an example of how to write a directive for a query plugin:
http://amitgharat.wordpress.com/2013/02/03/an-approach-to-use-jquery-plugins-with-angularjs/
I have created a sizable application javascript and jQuery. However my file structure is getting a bit messy!
At the moment I have one large JS file with a if ($('#myDiv').length > 0) { test at the top to only execute the code on the correct page, is this good practice?
There is also a mixture of plain JS functions and jQuery extensions in the same file e.g $.fn.myFunction = function(e) {.
I also have a few bits of code that look like this:
function Product() {
this.sku = '';
this.name = '';
this.price = '';
}
var myProduct = new Product;
Basket = new Object;
My question is for pointers on good practice regarding javascript and jQuery projects.
The code if ($('#myDiv').length > 0) { is not good practice. Instead, make your page specific JS as functions and execute them in the corresponding page . Like this:
var T01 = function(){
// JS specific to Template 01
};
var T02 = function(){
// JS specific to Template 02
};
HTML head of Template 01:
<script type="text/javascript"> $(T01); </script>
Consistency is the golden rule.
You can discuss design patterns back and forth, but if you want to have easily maintainable code where new people can come in and get an overview fairly quickly, the most important part, whatever design patterns you chose, is to have a consistent code base.
It is also the hardest thing to do - keeping your codebase clean and consistent is probably the hardest thing you can do as a programmer, and especially as a team.
Of course the first tip I can give you is to separate the jQuery extensions in their own source files. You can always serve everything together with a minification tool, so you should not worry about performance.
About the code youo mention, it could be simplified to
var Product = {
sku: '',
name: '',
price: ''
}
var myProduct = objectCopy(Product);
var Basket = {};
provided you write a simple objectCopy function which loops through the object own properties and just copies them to a new object (you can make a shallow or a deep copy, according to your needs).
Finally, if you think your code is starting to get messy, you may want to learn some patterns to organize JS code, like the module pattern. Alternatively, if you are familiar with doing this on the backend, you may want to organize your application following the MVC pattern. personal advertisement - I have written myself a tiny library which helps organize your code in this fashion. There are also many other libraries for the same task, often adding other functionality as well.
If you follow the MVC pattern, your page will actually correspond to some action in some controller, and you could just start it with a call like
<script>someController.someAction()</script>
in the head of your document, hence removing the need for the manual check for #myDiv. If you use my library MCV, it will be enough to declare your body like
<body class="mcv:controller/action">
and start the application with
$(document).ready(function() {
mcv.autostart();
});
Yes it's good practice to put as much of your code into a seperate JS file as this could then be compressed before transmission and hence speed up download time. However no you should not have code that looks like
if ($('#myDiv').length > 0) {
on every page. Split your JS code up into manageable functions and call those as-and-when you need to.
I don't see a problem with mixing JS and jQuery functions up in the same file.
i've been playing with MVC for a while now, but since the project i'm on is starting to get wind in its sails more and more people are added to it. Since i'm in charge of hacking around to find out some "best practice", i'm especially wary about the possible misuses of javascript and would like to find out what would be the best way to have our views and partial views play nicely with javascript.
For the moment, we're having code that looks like this (only simplified for example's sake)
<script type="text/javascript">
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
</script>
<%=Html.SubmitButton("submit", Html.ResourceText("submit"), New With {.class = "button", .onclick = "DisableInputsForSubmit(); if ($('#EditParameters').validate().form()) {SetContentArea(GetHtmlDisplay('SaveParameters', 'Area', 'Controller'), $('#Parameters').serialize());} return false;"})%><%=Html.ResourceIcon("Save")%>
Here, we're saving a form and posting it to the server, but we disable inputs we don't want to validate if a checkbox is checked.
a bit of context
Please ignore the Html.Resource* bits, it's the resource management
helpers
The SetContentArea method wraps ajax calls, and GetHtmlDisplay
resolves url regarding an area,
controller and action
We've got combres installed that takes care of compressing, minifying
and serving third-parties libraries and what i've clearly identified as reusable javascript
My problem is that if somebody else defines a function DisableInputsForSubmit at another level (let's say the master page, or in another javascript file), problems may arise.
Lots of videos on the web (Resig on the design of jQuery, or Douglas Crockford for his talk at Google about the good parts of javascript) talk about using the namespaces in your libraries/frameworks.
So far so good, but in this case, it looks a bit overkill. What is the recommended way to go? Should i:
Create a whole framework inside a namespace, and reference it globally in the application? Looks like a lot of work for something so tiny as this method
Create a skeleton framework, and use local javascript in my views/partials, eventually promoting parts of the inline javascript to framework status, depending on the usage we have? In this case, how can i cleanly isolate the inline javascript from other views/partials?
Don't worry and rely on UI testing to catch the problem if it ever happens?
As a matter of fact, i think that even the JS code i've written that is in a separate file will benefit from your answers :)
As a matter of safety/best practice, you should always use the module pattern. If you also use event handlers rather than shoving javascript into the onclick attribute, you don't have to worry about naming conflicts and your js is easier to read:
<script type="text/javascript">
(function() {
// your button selector may be different
$("input[type='submit'].button").click(function(ev) {
DisableInputsForSubmit();
if ($('#EditParameters').validate().form()) {
SetContentArea(GetHtmlDisplay('SaveParameters', 'Area','Controller'), $('#Parameters').serialize());
}
ev.preventDefault();
});
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
})();
</script>
This is trivially easy to extract into an external file if you decide to.
Edit in response to comment:
To make a function re-usable, I would just use a namespace, yes. Something like this:
(function() {
MyNS = MyNS || {};
MyNS.DisableInputsForSubmit = function() {
//yada yada
}
})();