As I read through the Angular tutorials, I really like a lot of it, but isn't "ng-click" the equivalent of an inline onClick? My understanding was that the JavaScript community had determined inline JavaScript event handlers in your HTML was "bad practice."
<img ng-src="{{img}}" ng-click="setImage(img)">
It would be great to know why this is no longer considered "incorrect" when using Angular.
Source: http://docs.angularjs.org/tutorial/step_10
Note: Using Inline JavaScript is a bad practice and it is not recommended. It can be used for demonstration purposes so that the demonstrator doesn't have to deal with 2 separate files at a time. It is recommended to write JavaScript code in a separate .
Having inline js on different pages will make it difficult to maintain for you and others as the project scale increases. Moreover using separate js files you can encourage reusability and modular code design. keeps your html clean and you know where to look when any js error occurs instead of multiple templates.
A script tag without a src (ie. with code directly in the HTML document) is referred to as an inline script. An onclick="..." attribute is called an inline event handler. Follow this answer to receive notifications.
Advantages of External Javascript:It helps in the reusability of code in more than one HTML file. It makes HTML and JavaScript easier to read and maintain. The length of the code reduces as only we need to specify the location of the js file. Designers can work along with coders parallel without code conflicts.
Really, it all comes down to the fact that your view code has to be hooked into your application logic somehow. Best practices for AngularJS generally state that you should write your own models--objects that represent your business domain--and attach them to the scope. Imagine some code like this:
<img ng-src="{{img}}" ng-click="myProfile.setMainImage(img)">
myApp.controller("ProfileController", function($scope, myProfile) { $scope.myProfile = myProfile; });
The view says "when this image is clicked, it will call setMainImage() on myProfile." The business logic is inside myProfile
, where it can be tested, etc. The view is just a hook.
In a more "traditional" or "vanilla" jQuery setup, you'd have to write something like the following:
$("#someId img").on('click', function() { var img = $(this).attr('src'); myProfile.setMainImage(img); // where does myProfile come from here? // how does it update the view? });
Of course, the JavaScript community has determined that writing large applications in this fashion isn't really tenable, in part because of the disconnect between the views and the model objects (as indicated by the comments in the code snippet), which is why we have frameworks like Angular in the first place.
So, we know this native jQuery code is not ideal, but we're still not sure about the whole ngClick
thing. Let's compare it to another very popular JavaScript framework that provides an MV* architecture, Backbone. In a recent RailsCasts episode on AngularJS, someone asked a very similar question:
Is it just me, or AngularJS looks so a bad idea? Ryan, don't get me wrong, the episode was great, but I'm not convinced by the framework.
All that
ng-show
,ng-repeat
,ng-class
are looking like the old Java's JSF, and similar frameworks. It also enforces obtrusive JS withng-submit
andng-click
.So my point is: your view will easily become cluttered and totally dependent on it. The advantage of other frameworks like Backbone, is to have a separation of concerns between the presentation and the behavior (less or no dependencies), and a structured client side application (MVVM).
My response is applicable here as well:
In a framework like Backbone, you'd have something like the following code (taken from the Backbone website, minus a few lines):
var DocumentView = Backbone.View.extend({ events: { "dblclick" : "open", "click .icon.doc" : "select", "contextmenu .icon.doc" : "showMenu", "click .show_notes" : "toggleNotes", "click .title .lock" : "editAccessLevel", "mouseover .title .date" : "showTooltip" }, open: function() { window.open(this.model.get("viewer_url")); }, select: function() { this.model.set({selected: true}); }, });
In this object which is a view, you are setting up event handlers on various elements. These event handlers call functions on the view object, which delegate to models. You also set up callbacks on various model events (such as
change
) which in turn call functions on the view object to update the view accordingly.In Angular, the DOM is your view. When using
ng-click
,ng-submit
, etc., you are setting up event handlers on these elements, which call functions that should delegate to model objects. When usingng-show
,ng-repeat
, etc. you are setting up callbacks on model events that change the view.The fact that AngularJS sets up these [hooks and] callbacks behind the scenes for you is irrelevant; the only difference between this and something like Backbone is that Angular lets you write your view declaratively--you describe what your view is--rather than imperatively--describing what your view does.
So, in the end,
<a ng-click="model.set({selected: true})">
really adds no more dependencies thanevents: { 'click a': 'select' }, select: function() { this.model.set({selected: true}); }
...but it sure is a hell of a lot less code. ;)
(Note: really, the Angular version should be <a ng-click="select()">
, and the select
method on the scope would be like the select
method in the view in the Backbone example.)
Now, perhaps a legitimate concern is that you don't like the event hooks in your markup. Personally, I greatly prefer the declarative nature of Angular's views, where your markup describes what the view is and you have two way binding between events (whether they be user generated or simply changes in the model) and your views--I find I write far less boilerplate code to hook up events (especially changes in the view driven by changes in the models), and I think it's easier to reason about the view in general.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With