Using namespaces in Angular - javascript

I am new to Angular, but the first thing I wanted to use was namespaces (as use them elsewhere)
I have read a number of posts regarding namespaces in Angular, and wanted to get an opinion on an approach I was going to perhaps take...
I was thinking of structuring very similar to what is suggested elswhere (ie have a main app.js, and then divide by features or "views".
For example, if I wanted to use a namespace such as TestModule, I would have each Feature/View and it's associated Controller in a separate js file...
eg such a file will look something like...
var TestModule = TestModule || {};
TestModule.View1 = function () {
// Private helpers
function cellTemplate(cellElement, cellInfo) {
var div = document.createElement('div');
......
};
function controller($scope) {
$scope.testText = "umm, hello";
$scope.gridData = [
{ col1: 'data 1', col2: 'data 2' },
{ col1: 'data 3', col2: 'data 4' },
];
$scope.cellTemplate = cellTemplate;
}
return {
// Controller
Controller: controller
}
}
And then in the main app.js. have something like..
"use strict";
var TestModule = TestModule || {};
var ngGridApp = angular.module("ngTestApp", ['dx']);
ngGridApp.controller("gridController1", TestModule.View1().Controller);
ngGridApp.controller("listController1", TestModule.View2().Controller);
etc
These are just my initial ideas, perhaps as I learn more I may find this approach won't be a good one, but it seems ok to me? I would be interested in any feedback on this 2 cents worth :-)
Cheers

Related

ExtJS: settings properties via a function on the Prototype: is it a safe pattern?

I am working on a large ExtJS codebase written around ExtJS3 which has a lot of the following initComponent() pattern:
Ext.define('PVE.form.BackupModeSelector', {
extend: 'PVE.form.KVComboBox',
alias: ['widget.pveBackupModeSelector'],
initComponent: function() {
var me = this;
me.comboItems = [
['snapshot', gettext('Snapshot')],
['suspend', gettext('Suspend')],
['stop', gettext('Stop')]
];
me.callParent();
}
now I have started to set this properties directly on the prototype doing things like:
Ext.define('PVE.form.BackupModeSelector', {
extend: 'PVE.form.KVComboBox',
alias: ['widget.pveBackupModeSelector'],
comboItems: [
['snapshot', gettext('Snapshot')],
['suspend', gettext('Suspend')],
['stop', gettext('Stop')]
],
initComponent: function doStuff() {
console.log('something we really need to do stuff here' + this.comboItems);
}
});
This works with ExtJS5, but is it a safe pattern ? can I be sure comboItems is already set when I call initComponent ?
I know about the https://docs.sencha.com/extjs/5.1/core_concepts/classes.html#Configuration config Object but this seems overkill.
In the first way, you are forcing comboItems value.
In the second, comboItems become available in config, with a default value. That means that you can override it.
var ms = Ext.create('PVE.form.BackupModeSelector', {
comboItems: [
// ... someting else
]
});
It depends if you want this value exposed to change or not.

Extending a base class in an Angular service

I have a base class that I would like to extend in a service to help get data in to the angular scope. I have searched around the net for a solution, but have not found one that I like. I have a base class that is used to access the File systems of devices
the class structure:
var cOfflineStorageBase = Class.extend({
init: function(){
},
CreateFolderDir: function(){
},
DeleteAll: function(){
},
DeleteDirectories: function(){
},
DeleteItem: function(){
},
GetFiles: function(){
},
FileExists: function(){
},
GetPath: function(){
},
GetObject: function(){
},
SaveObject: function(){
},
});
I would like to be able to extend this class in several different angular services (ie offlineCart, offlineCustomLists, ect...) where each service would be able to use the storage base to store the various different data types. I am looking for the best, most appropriate way to do this in angular. In vanilla JavaScript one would just do something like this:
var newClass = cOfflineStorageBase.extend({
//add new stuff here
});
but I want to do this same thing the angular way.
The approach I have been considering are to use the angular.extend functionality, but I am not sure this is appropriate or would something like this be a more appropriate approach:
app.factory('someChild', ['$http' , 'cOfflineStorageBase',
function($http, cOfflineStorageBase){
var SomeClass = cOfflineStorageBase.extend({
init: function(){
this._super.init()
},
//Add more stuff here
});
return SomeClass;
}]);
I would like some advice if theses approaches are correct or if there might be another that is better for what I am wanting to accomplish. I would also like or rather need to use promises in much of this code as it would be async.
I pulled off this trick recently.
I will start by defining a plain JavaScript constructor. This does not need to be an angular service. What I do is that, later, the extending constructors can pass any necessary injections by parameter. So, this will be the base "class" of my angular services. This is where I would expose anything I want all angular services to inherit.
function ParentService($http) {
this.$http = $http;
}
ParentService.prototype.foo = function () {
alert("Hello World");
};
Then I will proceed to define a child constructor using prototypal inheritance. This constructor will indeed be an angular service (you can tell by my use of $inject at the end).
function ChildService($http) {
Parent.call(this, $http);
}
ChildService.prototype = new ParentService();
ChildService.prototype.baz = function() {
return this.$http.get('/sample/rest/call');
}
ChildService.$inject = ['$http'];
Then I will proceed to register the services à la carte in the corresponding angular modules:
var app = angular.module('SampleApp', []);
app.service('child', ChildService);
Finally, in my controller I will simply inject my service, which will be an instance of my ChildService constructor, which in turn extends my ParentService constructor:
app.controller('MainCtrl', ['$scope', 'child', function ($scope, child) {
child.foo(); //alert("Hello World")
var promise = child.bar();
}]);
You can see a JSFiddle here
Also there is an interesting video in Youtube from ngConf called Writing A Massive Angular App which covers some of these topics and a few other ideas on code reusability with angular.
This question was asked, and answered, 18 months ago. I recently went through the same issue on a project. I wanted to have a base Model defined that I could use to build factories off of. Angular has a very simple Provider to assist with this called the Value Provider, which Angular implements using the Value Recipe.
I'm not sure what version of Angular you may have been using at the time, but this dates back (AFAIK) to version 1.3.0. (As of this writing, current stable is 1.4.8)
I'm also using John Resig's Simple Inheritance Script.
http://ejohn.org/blog/simple-javascript-inheritance/
Here's a snippet of my code (with most of the application specific logic removed).
var MyApp = angular.module( 'MyApp',['ngResource','ngAnimate','ngSanitize'] );
/* ==================================================================================== */
// - Base Model Class -------------------------------------------------------------
/* ==================================================================================== */
MyApp
/**
* BaseModel - Value Provider
*
*/
.value( 'BaseModel',Class.extend({
attribs: {},
init: function(){
var self = this;
_active = true;
_new = true;
_origs = {};
_loadByObject = function( obj ){ ... }
},
get: function( key ){ ... },
set: function( key,val ){ ... },
isNew: function(){ ... },
keep: function(){ ... },
remove: function(){ ... },
load: function( obj ){ ... }
verify: function(){ ... },
save: function(){ ... },
}))
.factory( 'UserFactory',
[ '$http', '$q', 'BaseModel',
function( $http, $q, BaseModel ){
var UserFactory = BaseModel.extend({
init: function(){
this._super( false );
_fields = [
'first', 'last', 'email',
'phone', 'password', 'role'
];
_permitted = [
'first', 'last', 'email',
'phone', 'password', 'role'
];
_required = [
'first', 'last', 'email', 'role'
];
_resource = "users";
_api = "users";
}
});
return UserFactory;
}])
I'd love to hear anyone's feedback, too.
Here's the Angular
Docs:
https://code.angularjs.org/1.3.0/docs/guide/providers

How to unit test views in ember.js?

We are in the process of learning Ember.js. We do all our development TDD, and want Ember.js to be no exception. We have experience building Backbone.js apps test-driven, so we are familiar with testing front-end code using Jasmine or Mocha/Chai.
When figuring out how to test views, we ran into a problem when the template for the view uses has a #linkTo statement. Unfortunately we are unable to find good test examples and practices. This gist is our quest to get answers how to decently unit-test ember applications.
When looking at the test for linkTo in Ember.js source code, we noticed it contains a full wiring of an ember app to support #linkTo. Does this mean we cannot stub this behaviour when testing a template?
How do you create tests for ember views using template renders?
Here is a gist with our test and a template that will make the test pass, and a template that will make it fail.
view_spec.js.coffee
# This test is made with Mocha / Chai,
# With the chai-jquery and chai-changes extensions
describe 'TodoItemsView', ->
beforeEach ->
testSerializer = DS.JSONSerializer.create
primaryKey: -> 'id'
TestAdapter = DS.Adapter.extend
serializer: testSerializer
TestStore = DS.Store.extend
revision: 11
adapter: TestAdapter.create()
TodoItem = DS.Model.extend
title: DS.attr('string')
store = TestStore.create()
#todoItem = store.createRecord TodoItem
title: 'Do something'
#controller = Em.ArrayController.create
content: []
#view = Em.View.create
templateName: 'working_template'
controller: #controller
#controller.pushObject #todoItem
afterEach ->
#view.destroy()
#controller.destroy()
#todoItem.destroy()
describe 'amount of todos', ->
beforeEach ->
# $('#konacha') is a div that gets cleaned between each test
Em.run => #view.appendTo '#konacha'
it 'is shown', ->
$('#konacha .todos-count').should.have.text '1 things to do'
it 'is livebound', ->
expect(=> $('#konacha .todos-count').text()).to.change.from('1 things to do').to('2 things to do').when =>
Em.run =>
extraTodoItem = store.createRecord TodoItem,
title: 'Moar todo'
#controller.pushObject extraTodoItem
broken_template.handlebars
<div class="todos-count"><span class="todos">{{length}}</span> things to do</div>
{{#linkTo "index"}}Home{{/linkTo}}
working_template.handlebars
<div class="todos-count"><span class="todos">{{length}}</span> things to do</div>
Our solution has been to essentially load the whole application, but isolate our test subjects as much as possible. For example,
describe('FooView', function() {
beforeEach(function() {
this.foo = Ember.Object.create();
this.subject = App.FooView.create({ foo: this.foo });
this.subject.append();
});
afterEach(function() {
this.subject && this.subject.remove();
});
it("renders the foo's favoriteFood", function() {
this.foo.set('favoriteFood', 'ramen');
Em.run.sync();
expect( this.subject.$().text() ).toMatch( /ramen/ );
});
});
That is, the router and other globals are available, so it's not complete isolation, but we can easily send in doubles for things closer to the object under test.
If you really want to isolate the router, the linkTo helper looks it up as controller.router, so you could do
this.router = {
generate: jasmine.createSpy(...)
};
this.subject = App.FooView.create({
controller: { router: this.router },
foo: this.foo
});
One way you can handle this is to create a stub for the linkTo helper and then use it in a before block. That will bypass all the extra requirements of the real linkTo (e.g. routing) and let you focus on the contents of the view. Here's how I'm doing it:
// Test helpers
TEST.stubLinkToHelper = function() {
if (!TEST.originalLinkToHelper) {
TEST.originalLinkToHelper = Ember.Handlebars.helpers['link-to'];
}
Ember.Handlebars.helpers['link-to'] = function(route) {
var options = [].slice.call(arguments, -1)[0];
return Ember.Handlebars.helpers.view.call(this, Em.View.extend({
tagName: 'a',
attributeBindings: ['href'],
href: route
}), options);
};
};
TEST.restoreLinkToHelper = function() {
Ember.Handlebars.helpers['link-to'] = TEST.originalLinkToHelper;
TEST.originalLinkToHelper = null;
};
// Foo test
describe('FooView', function() {
before(function() {
TEST.stubLinkToHelper();
});
after(function() {
TEST.restoreLinkToHelper();
});
it('renders the favoriteFood', function() {
var view = App.FooView.create({
context: {
foo: {
favoriteFood: 'ramen'
}
}
});
Em.run(function() {
view.createElement();
});
expect(view.$().text()).to.contain('ramen');
});
});

Can you share data across multiple templates?

I sometimes find myself declaring the same data to multiple templates. For example:
Template.auction_page.auctionDurations = function () {
return [ 30, 60, 120 ];
};
Template.auction_editor.auctionDurations = function () {
return [ 30, 60, 120 ];
};
I can make it better by using a global:
Template.auction_page.auctionDurations = function () {
return global.auctionDurations;
};
Template.auction_editor.auctionDurations = function () {
return global.auctionDurations;
};
But is there any way to get rid of the declarations altogether? In other words, is there any way to share some global data to multiple templates by default?
Found a good solution (with the help of a Helper!).
Your global:
global = _.extend({}, {
regions: [ "Americas", "Europe", "Asia" ]
}
The helper:
Handlebars.registerHelper("global", function(name) {
return global[name];
});
Now all your templates can make use of it:
<select>
{{#each global "regions"}}
<option>{{this}}</option>
{{/each}}
</select>
The use of a helper function is a pretty good general purpose solution. For completeness, you can also do a simple assignment:
Template.auction_page.auctionDurations = Template.auction_editor.auctionDurations;
You can use Session for this:
Template.auction_page.auctionDurations = function() {
return Session.get("auctionDurations");
}
Template.auction_editor.auctionDurations = function() {
return Session.get("auctionDurations");
}
A nice bonus of using Session is that, since it's a reactive data source, setting it will cause all Templates that depend on it to be re-rendered. So your auction durations will update as soon as you call Session.set("auctionDurations", [ 30, 60, 120 ]);

Using custom attributes in ExtJS Component

My application has been growing for the last year or so and I have finally started to split out common components by extending existing ones. I've found this to be really helpful - increasing speed of development and maintenance. My problem is that I haven't grasped the concept of using custom parameters on my components and was hoping that someone could offer some assistance in the following example. Specifically I don't seem to be able to access the custom parameter (myCustomParam) inside the proxy declared in the initComponent function:
MyEmployeeGrid = Ext.extend(Ext.grid.GridPanel, {
myCustomParam: "CompanyX",
initComponent:function() {
//Store Reader
var myReader = new Ext.data.JsonReader({
root : 'objectsToConvertToRecords',
idProperty: 'id',
fields : [
{name: 'id'},
{name: 'employee', allowBlank:false},
{name: 'department', allowBlank:false}
]
});
//Store Proxy
var dwrProxy = new Ext.ux.data.DwrProxy({
apiActionToHandlerMap : {
read : {
dwrFunction : EmployeeService.readCompanyEmployees,
getDwrArgsFunction: function(request, recordDataArray, oldRecordDataArray) {
return [myCustomParam];
}
}
}
});
//Setup Params for the grid
Ext.apply(this, {
store: new Ext.data.Store({
proxy: dwrProxy,
reader: myReader,
autoLoad : true,
autoSave: true
}),
columns: [{header: "Employee", dataIndex: 'employee'},
{header: "Department", dataIndex: 'department'}]
});
MyEmployeeGrid.superclass.initComponent.apply(this, arguments);
} // eo function initComponent
,onRender:function() {
MyEmployeeGrid.superclass.onRender.apply(this, arguments);
} // eo function onRender
});
Ext.reg('myemployeegrid', MyEmployeeGrid);
myCustomParam is a property of the object in question. It is never declared as a variable in its own right. You can see this by running the code through jslint.
You want this.myCustomParam.
If you're trying to pass in parameters, however, you might want to try this way instead:
MyGrid = Ext.extend(Ext.grid.GridPanel, {
constructor: function(cfg) {
cfg = cfg || {};
// ... do stuff ...
MyGrid.superclass.constructor.call(this, cfg);
}
});
I have a strong feeling this is to do with scope around the point at which getDwrArgsFunction is called. It possibly doesn't know what myCustomParam is (despite the fact this should possibly be this.myCustomParam, and that you'd want to set this after you'd applied any config) because it's not declared in the DwrProxy object and has no reference back to its owning MyEmployeeGrid object.
I'd debug/attempt to fix in the following way:
Rename myCustomParam to
this.myCustomParam and test
Set scope and test
Create getDwrArgsFunction using
createDelegate
(http://dev.sencha.com/deploy/dev/docs/source/Function.html#method-Ext.util.Functions-createDelegate)
and test
DISCLAIMER: Some of those will be no good depending on your DwrProxy etc (which we can't see the code for)
The proxy cannot access the variable because it is not defined in the scope of the proxy itself. I have been struggling with things like this in the past, I thought that components would act like closures and keep track of the variable, but they dont.
You can solve your problem by handing the scope of the grid to the handler function explicitly by making it a delegate, what you do have to change for that is making the variable you would like to access an instance variable instead of just a local variable.
Changes:
MyEmployeeGrid = Ext.extend(Ext.grid.GridPanel, {
myCustomParam: "defaultValue",
initComponent:function() {
...
//Store Proxy
var dwrProxy = new Ext.ux.data.DwrProxy({
apiActionToHandlerMap : {
read : {
dwrFunction : EmployeeService.readCompanyEmployees,
getDwrArgsFunction: function(request, recordDataArray, oldRecordDataArray) {
return [this.myCustomParam];
}.createDelegate(this)
}
}
});
var myGridInstance = new MyEmployeeGrid({ myCustomParam: 'ValueForThisInstance' });
Hope this helps you, good luck

Categories