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
Related
I'm learning Angular Meteor and I have a question:
What is the difference between using $reactive and getReactively?
If you take a look at the API reference you get this for $reactive (http://www.angular-meteor.com/api/1.3.2/reactive):
A service that takes care of the reactivity of your Meteor data, and updates your AngularJS code.
This service wraps context (can be used with this or $scope) - so you can use it with any context as you wish
And this for getReactively (http://www.angular-meteor.com/api/1.3.2/get-reactively):
Use this method to get a context variable and watch it reactively, so each change of this variable causes the dependents (autorun, helper functions and subscriptions) to run again.
The getReactively method is part of the ReactiveContext, and available on every context and $scope.
As far as I could understand $reactive will make everything reactive ('this', '$scope' and son on as long as you apply it to them) and getReactively will make only that particular variable, or object reactive.
So if I make this:
controller: function ($scope, $reactive) {
var vm = $reactive(this).attach($scope);
vm.sort = {
name: 1
};
this.helpers({
parties: () => {
return Parties.find({}, { sort : vm.sort });
}
});
this.subscribe('parties', () => {
return [{
sort: vm.sort
},
this.getReactively('searchText')
]
});
});
Why don't I get the same result as if I was doing this:
controller: function ($scope, $reactive) {
var vm = $reactive(this).attach($scope);
vm.sort = {
name: 1
};
this.helpers({
parties: () => {
return Parties.find({}, { sort : this.getReactively('sort') });
}
});
this.subscribe('parties', () => {
return [{
sort: this.getReactively('sort')
},
this.getReactively('searchText')
]
});
});
If $reactive takes care of reactivity I was expecting to see anything inside this and $scope to be reactive and, like in getReactively, whenever something is changed, to cause its dependents to run again.
So: what am I missing?
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
Hi, I'm about 2 weeks into building my application with ember.js, and the time has come to pull together my project layout into its final shape. To that end, I started looking into using Ember's register / inject mechanism instead of just creating global singletons and attaching them to my App object (for an excellent description of dependency injection in Ember, see here)
I'm stuck with standard dependency injection dilemma - circular references.
Let's say, I have two manager-like classes that I need available throughout application. Let's call them AuthManager and DataManager.
App.AuthManager = Ember.Object.extend({
logIn: function (user) {
var promise = this.dataManager.post("/session/new", user);
//...
}
});
App.DataManager = Ember.Object.extend({
getJSON: function (url) {
if (!this.authManager.get("isLoggedIn")) {
return false;
}
//...
}
});
So, as you see dataManager needs access to authManager and vice-versa.
My naive take at a solution was something like this:
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
application.inject("my:authManager", "dataManager", "my:dataManager");
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
application.inject("my:dataManager", "authManager", "my:authManager");
}
});
Predictably, this results in a dead loop. I was hoping dependency injection system would try some crafty juggle, like node's require does, but no.
I have tried:
Moving the first inject into the second initializer, after my:authManager is registered.
Moving the first inject into its own initializer, after the first two
Putting any combination of these into the Ember.onLoad('Ember.Application', ...) from the linked article
Unfortunately, everything I tried ended in stack overflow (pun intended :-)).
Am I missing something? The documentation is pretty sparse in this area.
Of course, I can always manually lookup instance after the 'official' injection, but I was hoping for some more elegant solution.
You definitely have a circular dependency, and if you were using a different language I'd tell you to use the inversion of control pattern, but it's a little difficult using your problem and the container.
Solution 1
If you're fine adding them under a namespace such as manager or something like that then here's a solution (it's tightly coupled, but the code is tightly coupled already, almost enough that they could be together or a mixin on the other).
App.Manager = Ember.Object.extend({
init: function(){
// late fake injection
this.authManager.dataManager = this.dataManager;
this.dataManager.authManager = this.authManager;
}
});
App.initializer({
name: "manager",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
application.register("my:manager", application.Manager);
application.inject("my:manager", "dataManager", "my:dataManager");
application.inject("my:manager", "authManager", "my:authManager");
application.inject("controller", "manager", "my:manager");
application.inject("route", "manager", "my:manager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
And an example:
http://emberjs.jsbin.com/mopaquko/2/edit
Solution 2
On another note, this creates a new instance on each route/controller. If you only need one instance. You can do it like so, much easier and doesn't need the namespace.
App.initializer({
name: "joinManagers",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
var dataManager = container.lookup('my:dataManager'),
authManager = container.lookup('my:authManager');
authManager.dataManager = dataManager;
dataManager.authManager = authManager;
application.register("my:jointDataManager", dataManager, {instantiate:false});
application.register("my:jointAuthManager", authManager, {instantiate:false});
application.inject("controller", "dataManager", "my:jointDataManager");
application.inject("controller", "authManager", "my:jointAuthManager");
application.inject("route", "dataManager", "my:jointDataManager");
application.inject("route", "authManager", "my:jointAuthManager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
http://emberjs.jsbin.com/mopaquko/3/edit
Solution 3
As was pointed out, Ember's container create's singletons by default, you can eagerly create the copies then allow ember to still resolve based on the original namespace.
App.initializer({
name: "joinManagers",
after:['dataManager', 'authManager'],
initialize: function (container, application) {
var dataManager = container.lookup('my:dataManager'),
authManager = container.lookup('my:authManager');
authManager.dataManager = dataManager;
dataManager.authManager = authManager;
application.inject("controller", "dataManager", "my:dataManager");
application.inject("controller", "authManager", "my:authManager");
application.inject("route", "dataManager", "my:dataManager");
application.inject("route", "authManager", "my:authManager");
}
});
App.initializer({
name: "dataManager",
initialize: function (container, application) {
application.register("my:dataManager", application.DataManager);
}
});
App.initializer({
name: "authManager",
initialize: function (container, application) {
application.register("my:authManager", application.AuthManager);
}
});
http://emberjs.jsbin.com/mopaquko/7/edit
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');
});
});
I am new in SPA's with backbone and I am trying to develop a small app by using backbone and requireJs.
The problem I faced is that I can't extend a view by passing a collection.
Well, this is the view with name MenuView.js
define([
'Backbone'
], function (Backbone) {
var MenuView = Backbone.View.extend({
tagName: 'ul',
render: function () {
_(this.collection).each(function (item) {
this.$el.append(new MenuListView({ model: item }).render().el);
}, this);
return this;
}
});
return new MenuView;
});
and this is the router.js in which the error is appeared
define([
'Underscore',
'Backbone',
'views/menu/menuView',
'views/createNew/createNew',
'collections/menu/menuCollection',
], function (_, Backbone, MenuView, CreateNewView,Menucollection) {
var AppRouter = Backbone.Router.extend({
routes: {
'index': 'index',
'action/:Create': 'Create'
},
index: function () {
CreateNewView.clear();
//----------- HERE IS THE PROBLEM ------------
$('#menu').html(MenuView({ collection: Menucollection.models }).render().el);
},
Create: function () {
CreateNewView.render();
}
});
var initialize = function () {
var appRouter = new AppRouter();
Backbone.history.start();
appRouter.navigate('index', { trigger: true });
};
return {
initialize: initialize
};
});
The error message is "object is not a function". I agreed with this since the MenuView is not a function. I tried to extend the MenuView (MenuView.extend({collection:Menucollection.models})) and the error message was "objet[object,object] has no method extend".
I suppose that the way I am trying to do this, is far away from the correct one.
Could anyone suggest how to do this?
Thanks
#Matti John's solution will work, but it's more of a workaround than a best practice IMHO.
As it is, you initializing your view just by requiring it, which:
Limits you to never accept arguments
Hits performance
Makes it really hard to unit-test if you relay on assigning properties ater constructing an instance.
A module should be returning a 'class' view and not an instance on that view.
In MenuView.js I would replace return new MenuView with return MenuView; and intitalzie it when required in router.js.
Your MenuView.js returns an initialized MenuView, so you could just do:
MenuView.collection = Menucollection
Note I haven't selected the models - I think it's better if you don't use the models as a replacement for your view's collection, since it would be confusing to read the code and not have a Backbone collection as the view's collection. You would also lose the method's contained within the collection (e.g. fetch/update).
If you do this, then you would need to update your loop (each is available as a method for the collection):
this.collection.each(function (item) {
this.$el.append(new MenuListView({ model: item }).render().el);
}, this);