Override dependency object prototype in angularjs - javascript

I have a set of angular $resource defined in a module called 'App.API' in a single file which I cannot touch because it is generated. (With loopback-angular, a tool to generate angular $resource from server side model definitions)
Let's take the Product dependency as en example, later in the app, I want to override its prototype, like this :
module('App.NewModule', ['App.API']).run(['Product', function(Product) {
Product.prototype.getTitle = function() {
return 'Product name is ' + this.name;
};
// From now on I can use p.getTitle() on every Product $resource
});
It works.
The thing is, I have many different files, each containing modules, and I am experiencing a dependency injection issue : I can access the getTitle function inside NewModule, but not inside other modules.
Question : How can I override a dependency object prototype and make it available to other modules ?
I tried to define the prototype functions in this way instead, thinking that Product prototype would be modified. Maybe not early enough :
module('App.API').run(['Product', function(Product) {
Product.prototype.getTitle = function() {
return 'Product name is ' + this.name;
};
});
It does not work : using getTitle in another module (using App.API/Product as a dependency) on a Product instance still throws a undefined is not a function error, even while Product object is correctly injected.

Actually, I just messed up the dependency definitions / orders.
I have three files :
app.js for module App (dependant on module App.API)
api.js for module App.API
product.js containing Product prototype
As stated in the question, I was doing :
// in product.js
module('App.API').run(['Product', function(Product) { ... }]);
// in app.js
var appModule = module('App', ['App.API']);
But the App.API module was defined in another file, which is a bit messed up because you never know for sure which one will load first, unless dealing with in in the js loader and loosing parallel downloads.
So I explicitly specified the modules and dependencies, at the expense of adding more dependency to declare in my app (but it works and is more stable) :
// in product.js
module('ApiProduct', ['App.API']).run(['Product', function(Product) { ... }]);
// in app.js
var appModule = module('App', ['App.API', 'ApiProduct']);
Note : In my first attempt, I defined the prototype in a new module in a .config() block, but it was not working, maybe because App.API services were not loaded yet. With .run() it works and my getTitle prototype is available everywhere I need Product provider.

Related

RequireJS loading a file that needs current file as dependency

I am currently building a base for AngularJS in combination with RequireJS and so far I got everything working. there's just a little thing that I do not understand at this point. I have a file which creates the angular module, when this module is created it requires a controller and assigns it to the module. The strange thing though, the controller needs the module as dependency while in the module's file the module has not been returned yet because the require statement is executed before the return statement. This somehow seems to work but it has a bad smell to it.
Module file:
// Home is defined here and can later be used in controllers (and Services)
define('home', ['require', 'angular'], function(require, angular) {
var homeModule = angular.module('AngularBase.home', ['AngularBase.core']);
homeModule.config(['$controllerProvider', '$provide', '$compileProvider', function($controllerProvider, $provide, $compileProvider) {
// We need this in order to support lazy loading
homeModule.controller = $controllerProvider.register;
homeModule.factory = $provide.factory;
// And more, not relevant at this moment
}]);
// It loads the controller that depends on this module here
require(['modules/home/controllers/homeController'], function() {
// Dependencies loaded
});
// Yet in my mind controllers that need this module can only use it when the following return statement is called.
return homeModule;
});
Controller File:
// As you can see this controller depends on home while home hasn't returned its module yet
// Yet it seems to work just fine
define(['home'], function(home) {
home.controller('homeController', ['$scope', 'homeService', function($scope, homeService) {
$scope.title = 'Home controller';
}]);
});
I assume that it is not a good approach to do it like this and therefore I need some suggestions on how to make this happen in a clean way. I thought about grabbing the AngularBase.home module via angular.module('AngularBase.home') in the controller file and defining my controller on this. This however no longer allows me to insert a mockModule for testing in this controller via RequireJS's map function.
map: {
'*' : {
'home' : 'mock-module'
}
}
Any suggestions on how to refactor this into a more clean solution?
I have found the solution to my problem. In the end it seems to be just fine to do it the way I am currently doing it. When a file is called and has a define statement in it it will wait until all dependencies are available until the function is executed. This means that the controller will actually wait for the module to finish initializing before calling its function to register itself.
The way I am doing it above is just fine.
Source: http://www.slideshare.net/iivanoo/handlebars-and-requirejs (slides 11 till 24)

Linking a javascript file to my current Javascript file

I am currently developing a web application where we are using the Model View Controller method for organizing our information and code. In our case we are combining the View and Controller into one Javascript file and the Model is separate.
My question comes here. I've got prototype objects in my model, but I want to instantiate instances of these objects in my viewcontroller Javascript file. How do I get them talking to each other?
There are some ways to achieve that. Today, the simplest one would be:
<script src='model.js'></script>
<script src='view-controller.js'></script>
So, since your model.js will be loaded first, you can use it inside the view/controller.
Another way is by using modules. The most used today is RequireJS. E.g:
require(['model'], function(model) {
// This function is called when model.js is loaded.
// If model.js calls define(), then this function is not fired until
// model's dependencies have loaded, and the model argument will hold
// the module value for model.js.
});
ECMAScript 6 (the next version of Javascript), will have native modules, so you'll be able to do:
import * as model from './model'
var x = model.variable; // etc
You might also want to look into using Browserify if you are familiar with Node and RequireJS as you an also use NPM modules in the front-end.
http://browserify.org/
Browserify allows you to export your JS code from one file and require it in another (simplified idea).
file 1: myfunc.js
var myFunc = function(){
console.log("I'm an exported function that's in another file");
};
module.exports = myFunc;
file 2: app.js
var myFunc = require('./myfunc.js');
myFunc(): // logs "I'm an exported function that's in another file"

'ShouldWorkController' is not a function. Got undefined

When I try to bind a controller to a template using the angular-ui-router $stateProvider, I run into the following error:
'ShouldWorkController' is not a function. Got undefined.
However, when I declare the controller inside the template using ng-controller, everything works fine. What could be wrong here?
app.ts
module App {
var dependencies = [
MyControllers
]
function configuration($stateProvider: ng.ui.IStateProvider) {
$stateProvider
.state("shouldWork", {
url: "/shouldWork",
templateUrl: "app/shouldWork.html"
controller: "ShouldWorkController" // does not work
});
}
}
shouldWorkController.ts
module App.MyControllers {
interface IShouldWorkViewModel {
}
class ShouldWorkController implements IShouldWorkViewModel {}
}
ShouldWork.html
<div ng-controller="ShouldWorkController as viewModel" us-spinner spinner-key="spinner-1">
^ --- this works nicely
That message means, that such controller "ShouldWorkController" is not loaded int he main angular module. Be sure that you do call register at the end:
module App.MyControllers {
...
class ShouldWorkController implements IShouldWorkViewModel {}
}
// we have to register this controller into some module (MyControllers)
// which is also referenced in the main app module
angular.module('MyControllers')
.controller('ShouldWorkController', App.MyControllers.ShouldWorkController );
I realise this is old, but I came here via Google with the same issue, not for the firs time. Things to check include:
Export statement for your controller class. From the code you posted I see that you are missing an export statement for your ShouldWorkController class. This may not be the issue in your case, but it is something you should check. I can reproduce this error by removing the export statement from my controller classes.
Check your HTML template exists (if using UI-Router). As described in the UI-Router documentation: "The controller will not be instantiated if template is not defined."
Register your controllers in the same file as the controller. Some tutorials do demonstrate controllers being registered in the module file. While this does work, I personally have found it more error prone than directly registering the controller within the controller file.
Check your typescript references. Make sure that you add typescript references (e.g. ///<reference path="../app/services/shouldWorkService.ts">) to typescript files that contain any types that you reference
Check the name of your controller matches that declared in your $stateProvider configuration.

How to share objects/methods between controllers without circular references?

Pretty straightforward question. Currently, what I do when I need to access objects' methods throughout most of the application, I do this in app.js
Ext.define('Utils', {
statics: {
myMethod: function() {
return 'myResult';
}
}
});
This works, but when I build the application, I get a warning about a circular reference (app.js of course needs all the other controller classes, but then said classes refer back to app.js).
I thought of using a package including a .js file that has all the objects/methods, but sometimes, within these methods I'll need access to the Ext namespace so that won't work.
Is there any better way to do this ?
Thanks!
You should not define the class Utils inside app.js. Each Ext.define statement should be in it's own file. Also the classname should be prefixed with your app name.
Ext.define('MyApp.Utils', {
statics: {
myMethod: function() {
return 'myResult';
}
}
});
should therefore be found in the file app/Utils.js. When compiling your sources into a compiled app.js, Sencha Cmd will take care of the proper ordering in the final file. The requires and uses directives give you enough flexibility to define any dependences between your classes.
Read all the docs about MVC to get a clear understanding.

Undefined object being passed via Requirejs

I'm using Requirejs to load the JavaScript in our web app. The issues is that I'm getting an undefined object being passed to a module which, when used in other modules, is instantiated perfectly fine.
OK, here's the setup. My main.js file which requirejs runs on startup:
require.config({
baseUrl: "/scripts",
paths: {
demographics: "Demographics/demographics",
complaints: "Complaints/complaints",
}
});
require(["templates", "demographics", "complaints", "crossDomain"], function (templates, demographics, complaints) {
"use strict";
console.log("0");
console.log(demographics === undefined);
demographics.View.display();
});
A lot of the config has been stripped to just the core files in this problem.
Here's Demographics.js:
define(["ko", "templates", "complaints", "globals", "underscore"], function (ko, templates, complaints, globals) {
// Stuff removed.
return {
View: view
};
});
and Complaints.js
define([
"demographics",
"ko",
"templates",
"complaints",
"visualeffects",
"globals",
"webservice",
"underscore",
"typewatcher",
"imagesloaded"],
function (demographics, ko, templates, complaints, visualeffects, globals, webservice) {
"use strict";
console.log("1");
console.log(demographics === undefined);
return {
View: view
};
});
The problem is this - in Complaints.js the demographics parameter passed via the define config is undefined. The console log out tells me that "demographics === undefined" is true.
However, when the main.js file executes, the demographics parameter passed to it is not undefined, it is, as expected, an instantiated object.
Now I'm stuck since I can't see why in complaints.js that demographics variable is undefined. Can anyone spot what I'm missing please?
You have a circular dependency. The demographics module depends on complaints and complaints depends on demographics. As per the documentation:
If you define a circular dependency (a needs b and b needs a), then in this case when b's module function is called, it will get an undefined value for a.
The solution, if you can't remove the circular dependency, is to asynchronously require one of the two modules within the other on demand (say when the view is instantiated instead of when the module that defines the view is executed). Again, the docs cover this topic fairly well.
Another case is when you accidentally type require instead of define when defining a module, took me some time to notice this.
I had a similar problem. In my case, when defining a module, I'd written:
define('some_dependency', ...
instead of
define(['some_dependency'], ...
Another possible reason is implementing the module's interface (AMD, CommonJS), but forgetting to return anything. I just did that.
I just encountered another reason:
define(function () {
return {};
}()); // <-- notice the '()' typo.
This "typo" causes no JS errors for this one and can make it confusing to figure out especially in a complicated application with many potential circular dependancies.
The reason, of course, is the "typo" is valid JS that simply calls the function you define thereby passing its result to define() rather then the function as intended.
One other possible reason that may look obvious in hindsight is an error in your module's code. In my case, I was trying to get an attribute from an undefined variable. The error is logged in the console, but for some reason I was not seeing it/mistaking it for the undefined module error.

Categories