I'm doing Angular JS tutorials. I've no clue why this notation have been used. Can anyone explain what is happening here?
Code in the controller.js file:
var vm = this;
vm.openSideBar = openSideBar; // Did not understand this line
function openSideBar() {
$mdSidenav('left').open();
};
The code that you post is purely stylistic and takes advantage of hoisting to enable the bindable functions to be displayed at the top of the file, and the details (implementations) for those to be kept separate and specified further down the file.
Benefits
The benefits of this approach are more apparent as the number of bindable functions in the controller increases:
Easier to read (what's important). Since the purpose of the controller is to support the view and supply it with bindings, the what is more important than the how i.e. What the view can bind to is more important than how the controller goes about it's work. You can see immediately on opening the file what the controller can do for the view.
Easier to find (the details). Having the bindable functions at the top of the file acts as like a contents page for the file. Most IDE's will highlight matching variables if you place the cursor over them and that helps find the implementation details further down the file.
Encourages single responsibility. Your controller should be supplying bindings for a view, and only one view. You can see at the top of your file if you start to build up lots of unrelated bindable functions that serve different purposes that you might be trying to do too much in one controller.
Encourages named functions. This is particularly useful for debugging as it means that you will have less anonymous functions in your application, which in turn makes reading call stacks in developer tools easier.
Further Reading
Check out John Papa's Angular 1 Style Guide which has been approved by the Angular team. You can read all about the thought behind this approach under the heading Bindable Members Up Top in the Controllers section (although it can be applied to factories/services/directives also).
openSideBar was add to view model. it was aviable to run this func from view like <someElement ng-click="$ctrl.openSideBar()"></someElement>
The VM model or way of doing is quite old... before ES6 came along.
Nowadays, I would suggest you learn ES6, take a look at Todd Motto's AngularJS style guide https://github.com/toddmotto/angularjs-styleguide.
But here an answer to your question:
// OLD WAY
function MyAppController() {
var vm = this;
vm.colors = ['red', 'blue', 'green'];
vm.getBlue() {
return vm.colors[1];
}
vm.handleClickEvent(e) {
// this keyword is now the dom element, not your controller
// this is why the VM pattern came to life.
this.classList.add(vm.getBlue());
}
}
// NEW WAY
class MyAppController {
$onInit() {
this.colors = ['red', 'blue', 'green'];
}
getBlue() {
return this.colors[1];
}
handleClickEvent(e) {
// Here this keyword is still your controller, NOT the dom
// element (provided you used angular's ng-click directive
const elem = e.target;
elem.classList.add(this.getBlue);
}
}
With ES6, it is rare that you have to same a reference to this since you can use arrow functions. You should also get familiar with bind(), call(), and apply() methods to control which context you want to apply to functions.
Related
I'm quite new to Angular, and I'm adapting a simple CRUD app written using standard controllers and ngResource to use the components introduced in 1.5. None of the docs and resources I've found so far discuss how to:
create a new item from scratch
integrate with ngResource
so I'm wondering if anyone can give some pointers on how best to proceed.
My existing app has a simple factory declaring a resource entity, and a single controller that
instantiates a new instance of the resource: $scope.newEntity = new Entity();
populates the $scope with a list of the resources retrieved from the backend: Entity.query(function (data) { $scope.entities = data; });
provides a couple of functions for deleting, updating, and saving the resource to the backend.
In the HTML I have a form that works with $scope.newEntity and the controller saving method to save the new entity to the backend. I also have an ng-repeat that lists the entries stored in $scope.entities, with a couple of additional ng-clicks to perform some editing and deleting.
What I want to do now is implement some inline editing in the list. I know I can do this with my existing approach, but I want to cleanly reuse the form validation functionality I have in the existing entity creation form in the entity editing code, without duplicating. Components seem like a natural fit for that to my (admittedly inexperienced) eyes.
With the component-based approach, I have followed the documentation at https://docs.angularjs.org/guide/component under Example of a component tree, and created an entity-list and entity-detail component. These work okay so far, and I think I can figure out how to wire up the on-delete and on-update events. What I can't figure out is how to approach an on-create event.
Should I use a completely separate controller with my existing simple form to handle the creation event? If so, how can I get the existing list to automatically update? Will that creation event propagate across to the list controller?
Or am I missing something in the existing list controller? Or is the entity creation a special case for the detail controller?
I'm looking specifically for information about how to implement this using Angular components and ngResource, as I'd also like to be ready for Angular 2. Unless components and resources aren't meant to work together please don't post answers about how to achieve this using a completely different approach, or how to reuse HTML code without components. Thanks!
Actually the C in CRUD is realy simple. You were probably expecting an on-create method to be used from your entity-detail. entity-list should take care of the creation of the details however.
Here is the working code
I extended the example from the guide https://docs.angularjs.org/guide/component under Example of a component tree you were reading too and added the create:
(function () {
'use strict';
angular
.module('componentCrud')
.component('heroList', {
templateUrl: "component/hero-list.component.html",
controller : [
HeroListController
]
});
function HeroListController() {
var ctrl = this;
ctrl.list = createHeroes();
ctrl.updateHero = updateHero;
ctrl.deleteHero = deleteHero;
ctrl.createHero = createHero;
function createHero(){
ctrl.list.push({
name : 'Crazy Newling',
location: 'Morgues'
})
}
function updateHero(hero, prop, value) {
hero[prop] = value;
}
function deleteHero(hero) {
var idx = ctrl.list.indexOf(hero);
if (idx >= 0) {
ctrl.list.splice(idx, 1);
}
}
function createHeroes() {
return [{
name : 'Superman',
location: ''
},
{
name : 'Batman',
location: 'Wayne Manor'
}
]
}
}
})();
Then in HTML you just add a create button:
<b>Heroes</b><br>
<hero-detail ng-repeat="hero in $ctrl.list"
hero="hero"
on-delete="$ctrl.deleteHero(hero)"
on-update="$ctrl.updateHero(hero, prop, value)"></hero-detail>
<button ng-click="$ctrl.createHero()">Hire a new Hero</button>
I hope it is going to help you!
I'm relatively new to AngularJS and the problem I'm facing is one of those "I want to inject a Service into an app.config" type of scenarios, which I realise cannot be done. (I'm comfortable with the different between Service and Provider, and why a Service cannot be injected into a .config.)
What I am trying to accomplish is to use angular-schema-form together with angular-translate such that field titles in generated forms are translated.
There is an issue where someone asks how to do this, and the advice given is to take advantage of angular-schema-form's postProcess, which is a property of the Provider. This callback gives you the form object before it is rendered, giving you the opportunity to manipulate it with user code. Therefore translation could be done within here.
The postProcess method is called on the Provider, so it is done within an app.config:
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// within here I can inspect the form object, find all
// properties whose key is "title", and then perform
// language translation on their values.
So, that is apparently the place where I have an opportunity to manipulate control titles and so on.
Over to the angular-translate library, for me to 'manually' translate strings, I can use the $translate service. This provides both synchronous and asynchronous methods to translate a given key string. The synchronous one is $translate.instant(key).
To glue these two together, what I have tried so far (which does work) is to create a 'bridge' method like this:
var app = angular.module('myApplicationName', ['schemaForm', 'pascalprecht.translate']);
....
app.config(function(schemaFormProvider, $translateProvider) {
schemaFormProvider.postProcess(function(form){
// ... code here which iterates over properties
// and finds all control titles ...
key = app.myTranslate(key);
// ....
}
....
});
app.myTranslate = function (key) {
var service = angular.injector(['ng', 'myApplicationName']).get("$translate");
return service.instant(key);
}
This does work, but it seems ugly and unsafe (as presumably there's no guarantee $translate is ready when the callback is first invoked) and the calls to angular.injector(['ng', 'myApplicationName']).get... are presumably expensive.
Is there a better way, or is this the only way I'm going to get it done, considering the constraints of the libraries I'm working with?
I have also considered an alternative approach altogether, which would be to instead perform the translations on the schema or form objects before they are processed by angular-schema-form. This could be done from within Controllers, eliminating the problem of accessing the $translate service. I may end up going down that route, but it would still be nice to understand the best solution for the above scenario.
I am trying to have external modules change my $translateProvider.translation on the main module. see this as a "tranlation plugin" for my app.
it seems like changing translations from the $translate service is not possible.
mymodule.service('MyService', function ($translateProvider) {
var lib = function () {
//EDITED FOR BREVITY
this._registerTranslations = function (ctrl) {
if (!ctrl.i18n) return;
for (var name in ctrl.i18n) {
/////////////////////////////
// THIS IS THE PLACE, OBVIOUSLY PROVIDER IS NOT AVAILABLE!!!!
$translateProvider.translations(name, ctrl.i18n[name]);
//////////////////////////////
}
};
//EDITED FOR BREVITY
};
return new lib();
});
anyone with a bright idea?
So, to answer your question: there's no way to extend existing translations during runtime with $translate service without using asynchronous loading. I wonder why you want to do that anyway, because adding translations in such a way means that they are already there (otherwise you would obviously use asynchronous loading).
Have a look at the Asynchronous loading page. You can create a factory that will load a translation from wherever you want.
I created an Angular constant to hold new translations. If I want to add a new translation, I add it to the constant. Then in my custom loader, I first check the constant to see if the translation exists (either a new one, or an updated one). If so, I load it from the constant. If not, I load it from a .json file (or wherever you load your initial translations from). Use $translate.refresh() to force translations to be reloaded and reevaluated.
Demo here
The demo is pretty simple. You would need to do a little more work if you wanted to just change a subset of the translations, but you get the general idea.
From the AngularJS docs (https://docs.angularjs.org/guide/providers):
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Providers are to be used with the application's .config function. $translateProvider for configuration, $translate for other services and controllers.
Are there any inherent problems with using CoffeeScript to code backbone.js apps? Did you run into some problems that you couldn't fix or had to use some particularly clunky workarounds?
No problems, really. At least, none that aren't easily worked around.
The problems with using CS are the same problems you might get using CS anywhere:
Debugging is still done in the generated JS
CS requires a pre-processing step that can be awkward at times
The rest of your team might not know CS
There are some odd things about CS (they introduce "classes" but they are not real classes)
In addition, since Backbone dev with Coffeescript is "class" based, you find yourself wanting to separate your classes into separate files, in separate folders. Because of this, you can get into a situation where the classes are defined out of order. For instance, your collection might get defined before your model, which can't happen. For this, I recommend using something that can manage dependencies (imports). I use coffee-toaster but there are several other options (Rails has dependency management built in to the asset pipeline, for instance)
It is my preferred way to write Backbone code. In my opinion, Backbone.js development is actually better in CoffeeScript than in Javascript. To me, they go together like chocolate and peanut butter. (Not everyone likes chocolate/peanut butter... not everyone likes BB/CS)
Class semantics
Backbone development relies heavily on extending prototypes and this is something that is built-in with CoffeeScript. So, where you would extend a View in JS:
App.Models.MyModel = Backbone.View.extend({
render: function() {
...
}
});
The CS alternative is a native experience:
class App.Models.MyModel extends Backbone.Model
render: ->
...
Overriding Functions
Some things you do often in Backbone, like overriding functions becomes much more simple. In Javascript:
constructor: function ( attributes, options ) {
this.constructor.__super__.constructor.apply( this, arguments );
...
}
Becomes:
constructor: (attributes, options) ->
super
"this" Context Binding
The "fat arrow" in CS is really useful when you need to declare the context of a function is "this"
Javascript sets 'this' differently when functions get called back. There are several ways to solve it, but out of the box, it is awkward:
initialize: function() {
this.model.bind('reset', this.render);
},
render: function () {
this.$el.html("<ul></ul>");
this.model.each(this.renderItem); // <--- Fails on "reset" because 'this' is wrong
return this;
}
CoffeeScript has the => token that will automatically bind 'this' to the function when it gets called by anybody:
initialize: ->
#model.bind 'reset', #render
render: =>
#$el.html '<ul></ul>'
#model.each #renderItem # The fat arrow fixed it for you
#
When all is said and done, Backbone.js code is easier to write and is much easier to read when written in CS. At least, that is my opinion.
Good luck!
Both CoffeeScript and Backbone.js were written by the same author (Jeremy Ashkenas). The backbone-on-rails gem generates CoffeeScript by default. Though certain plugins (such as Backbone-relational, which you mentioned) may require additional setup, Backbone itself plays very nicely with CoffeeScript.
CoffeeScript is just a syntax layer on top of JavaScript. It essentially is JavaScript. Anything you can do in JavaScript you can reproduce in CoffeeScript.
The plugin model in Backbone.js is really nice, but one thing I'm wondering about is whether it's possible to use multiple plugins without modifying any of the plugin source.
For example, say I've written two plugins for the Collections:
MyBetterCollection = Backbone.Collection.extend({
coolNewFeature: function () {
console.log('This feature is great.');
}
});
MyWayBetterCollection = Backbone.Collection.extend({
wayCoolerNewFeature: function () {
console.log('This feature is even better.');
}
});
I can see some potential issues already, if, for example, both plugins override something like the add method. But having to modify third-party plugins would be a bummer:
MyWayBetterCollection = MyBetterCollection.extend({
...
});
Is there a good approach to handling this situation?
There isn't an easy way to do this. Since there is no traditional inheritance in JavaScript, it's difficult to provide this facility.
You'd have to find plug ins designed to work with each other or fork them and make them compatible.
You could also provide your own extend method that would use the interceptor pattern or some type of monkey patching to provide access to overwritten methods from previous prototypes that were overwritten.
The two collection you've defined above are totally separate from each other. They simply inherit the methods from Backbone.Controller, and if you defined methods with the similar name, then you overwrite those.
Think about Backbone as a class inheritance in other programming languages. Basically, you extend Backbone.Collection as you would do with other languages.
As a result, you can call the superclass, like this
var MyCollection = Backbone.Collection({
toJSON: function() {
var toJSON = this.constructor.__super__.toJSON.call(this);
toJSON.extra = 'my extra value';
return toJSON;
});
Even if you extend your own collection, the logic remains.