What happens when I use angular.module(...).controller(...).directive(...)?And why? [duplicate] - angularjs

This question already has answers here:
How to implement chained method calls like jQuery?
(4 answers)
Closed 5 years ago.
I think this equals to
var module = angular.module(...);
module.controller(...);
module.directive(...);
But I'm not sure. And I don't know what happens in angular and why I could write code this way.
I try to debug and trace it but it's so confused.

This is called a fluent API.
Each method will return the module instance, so that another method may be called.
To illustrate we can create a class that does something similar.
class Module {
controller() {
console.log('controller');
return this;
}
directive() {
console.log('directive');
return this;
}
}
When each method has finished, it will return the module instance this so that another method can be chained.
So now we can use this class and chain the methods like this:
new Module().controller().directive();
Or
const module = new Module();
module.controller();
module.directive();

What happens when I use angular.module(…).controller(…).directive(…)?And why?
Short answer
This is good way to write your code in one file.
If you want to split Angular project to different files, use 2nd approach:
var app = angular.module(...);
app.controller(...);
app.directive(...);
Long answer
Also take a look on this angular code snippets (took from https://code.angularjs.org/1.5.6/angular.js):
You can see controller, directive, module, filter, factory, value, provider, decorator, animation, config, component ,run returns moduleInstance
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** #type {Object.<string, angular.Module>} */
var modules = {};
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** #type {!Array.<Array.<*>>} */
var invokeQueue = [];
/** #type {!Array.<Function>} */
var configBlocks = [];
/** #type {!Array.<Function>} */
var runBlocks = [];
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** #type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
Why is it better?
Both approaches do the same so developer will decide what is better for his project structure
for efficiency?
There is no efficiency value measurement, both has same efficiency. No performance penalty.
for what?
In project I want to write each directive each controller each ... in separate file so I use app.controller(...);, app.service(...); ,...
However common directives I want to put in one file so I use:
app.directive(…).directive(…).directive(…).directive(…).directive(…)
Hope it will spread the light on your understanding :)

Related

override pivot view _render function in custom module odoo 11

I want to override "_render: function ()" function from pivot_renderer.js file in web but not working in custom module. Here is the code i am implementing in my custom module:-
odoo.define('MY_CUSTOM_MODULE_NAME.renderer', function (require) {
"use strict";
var PivotRenderer = require('web.PivotRenderer');
var field_utils = require('web.field_utils');
var core = require('web.core');
var _t = core._t;
PivotRenderer.include({
init: function(parent, state, params) {
this._super.apply(this, arguments);
},
_render: function () {
if (!this._hasContent()) {
// display the nocontent helper
this.replaceElement(QWeb.render('PivotView.nodata'));
return this._super.apply(this, arguments);
}
if (!this.$el.is('table')) {
// coming from the no content helper, so the root element has to be
// re-rendered before rendering and appending its content
this.renderElement();
}
var $fragment = $(document.createDocumentFragment());
var $table = $('<table>').appendTo($fragment);
var $thead = $('<thead>').appendTo($table).addClass("CLASS_NAME");
var $tbody = $('<tbody>').appendTo($table);
var nbr_measures = this.state.measures.length;
var nbrCols = (this.state.mainColWidth === 1) ?
nbr_measures :
(this.state.mainColWidth + 1) * nbr_measures;
for (var i=0; i < nbrCols + 1; i++) {
$table.prepend($('<col>'));
}
this._renderHeaders($thead, this.state.headers);
this._renderRows($tbody, this.state.rows);
// todo: make sure the next line does something
$table.find('.o_pivot_header_cell_opened,.o_pivot_header_cell_closed').tooltip();
this.$el.html($table.contents());
return this._super.apply(this, arguments);
},
});
});
In the above, i want to add a class in the header for calling my custom css "var $thead = $('').appendTo($table).addClass("CLASS_NAME");" with this syntax but it is not reflecting in my custom module. Although, for testing, I have implemented same class in default web module and it is working fine. The issue is in custom module.
So how to solve this issue? Is there any other way for calling class or i am doing it in a wrong way?
var $thead = $('').addClass("CLASS_NAME").appendTo($table);
This will work in my case. You can try it.

Singleton JS Object in Angular Service

I am trying to add methods to an Object's protoype, which will be used in a singleton service and will be initiated only once when the service is created.
angular
.module('app.steps')
.factory('stepsService', stepsService);
stepsService.$inject = [];
/* #ngInject */
function stepsService() {
var steps = new Steps(1,3);
function Steps(current_step, total_steps) {
this.c_step = current_step;
this.t_step = total_steps;
}
Steps.prototype = {
addSteps: function (num) {
this.c_step += num;
},
setLastStep: function () {
this.lastStep = this.c_step = this.t_step;
}
};
var service = {
steps: steps
};
return service;
}
My problem is that although the object is created and initiated successfully, the methods are not there.
What is missing?
As mentioned in the comments, var steps = new Steps(1,3); should be after Steps.prototype = {....}

Angular - Organise controller, factory and "class"

I would like to understand how to have a nice organisation in my angular project.
[see code below]
Does it makes sense to have the getFireList function into the Factory ? Or should i put it into the controller ?
Does the "class" Fire makes sense ? Should i remove it ? Should i move it to the controller ? Should i move it the the factory ?
If you see anything wrong in this code i'm really interested to learn more.
For now, i've got this :
A class "Fire" to create new object of type Fire.
function Fire (p_power) {
// ATTRIBUTES
this.id = null;
this.power = p_power;
this.position = {
x: null,
y: null
}
// GETTERS/SETTERS
// id
this.getId = function() {
return this.id;
}
this.setId = function(p_id) {
this.id = p_id;
}
// power
this.getPower = function() {
return this.power;
}
this.setPower = function(p_power) {
this.power = p_power;
}
// position
this.getPosition = function() {
return this.position;
}
this.setPosition = function(p_position) {
this.position = p_position;
}
// METHODS
this.increasePower = function(p_plus) {
this.power += p_plus;
}
this.decreasePower = function(p_minus) {
this.power -= p_minus;
}
}
A controller
simuApp.controller('FireController', function($scope, FireFactory) {
// ...
});
And a factory
simuApp.factory('FireFactory', function() {
return {
fire_list: [],
getFireList : function() {
return $http.get(site_url+'fire/fireList').
then(
function(success) {
var data = success.data;
var fires = [];
var fire_tmp;
for (i=0 ; i<data.length ; i++) {
fire_tmp = new Fire( data[i].power );
fire_tmp.setId( data[i].idFire );
fires.push( fire_tmp );
}
fire_list = fires;
return fire_list;
}, function(err) {
// ...
}
);
}
}
});
Thanks for your help.
First, let's get the terminology right. .factory is a method to register a function that generates an instance of the service - hence "factory". What it generates, though, is a singleton service instance.
So, the service you create would be more properly named as FireSvc (as opposed to FireFactory), whereas the function that creates it could have the word "factory" in it (although, in the case below, that function name is not really needed - it could just be an anonymous function):
.factory("FireSvc", function FireSvcFactory(){
});
It is a good practice to use a Service to abstract away any domain/business logic from the controller. Keep the controller thin, responsible only to define the ViewModel, and react to events by changing the ViewModel or invoking functions on the Model.
So, having FireSvc.getFireList() makes sense.
Now, whether the list is a collection of plain objects, or instances of Fire is completely independent of Angular and is entirely up to you. In any case, it is too broad of a topic to discuss in a SO answer.

How do I create a custom object class that's available to my methods in AngularJS

I'm a huge fan of angular but it's got some tricky concepts with extremely nuanced differences between them and this is one of them.
I just want to create an class that I can use to create custom objects in my Angular controllers and factories. It surely shouldn't be that hard but I can't figure out how to do it. I want to have a custom, ResultSet class which I can instantiate to create instances of ResultSet. However for the life of me I can't figure out the correct syntax of factory v. service to use.
This is all I want:
ResultSet = function(dataSet){
this.filter = function(){
# filters and returns dataSet
# ...
}
}
and then I want to be able instantiate an instance of ResultSet inside a controller etc:
MyApp.controller('pageCtrl', ['ResultSet', (ResultSet) ->
# ...
rs = ResultSet.new(dataToFilter)
How can I create a service that allows me to create instances of my custom object?
It seems more correct to use an Angular Service rather than a Factory since a service returns an instance of an object (which is exactly what I want). But I can't figure out how to do this...
How would I use a service to declare my custom ResultSet class and then how would I instantiate an instance from it?
Maybe you were looking for something like this:
.factory('User', function (Organisation) {
/**
* Constructor, with class name
*/
function User(firstName, lastName, role, organisation) {
// Public properties, assigned to the instance ('this')
this.firstName = firstName;
this.lastName = lastName;
this.role = role;
this.organisation = organisation;
}
/**
* Public method, assigned to prototype
*/
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
/**
* Private property
*/
var possibleRoles = ['admin', 'editor', 'guest'];
/**
* Private function
*/
function checkRole(role) {
return possibleRoles.indexOf(role) !== -1;
}
/**
* Static property
* Using copy to prevent modifications to private property
*/
User.possibleRoles = angular.copy(possibleRoles);
/**
* Static method, assigned to class
* Instance ('this') is not available in static context
*/
User.build = function (data) {
if (!checkRole(data.role)) {
return;
}
return new User(
data.first_name,
data.last_name,
data.role,
Organisation.build(data.organisation) // another model
);
};
/**
* Return the constructor function
*/
return User;
})
From this post by Gert Hengeveld.
myApp.factory('ResulSet', function() {
function ResultSetInstance(dataSet) {
this.filter = function(){
// ...
}
}
return {
createNew: function(dataSet) {
return new ResultSetInstance(dataSet);
}
};
});
and then
myApp.controller('pageCtrl', function(ResultSet) {
var someData = ...;
var rs = ResultSet.createNew(someData);
}
Edit (from the question asker)
On experimenting with this further I found that you didn't even need to have the createNew method.
myApp.factory('ResultSetClass', function() {
ResultSetClass = function(dataSet) {
this.filter = function(){
// ...
}
}
return ResultSetClass
});
works just fine and then you can call new ResultSetClass(args).
Note for those using Coffeescript
Coffeescript will return the last variable or method in your class instance so if you are using coffeescript (as a general rule), it's imperative to return this at the end of the class definition
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
return this
return ResultSetClass
If you don't return this explicitly then you'll find that when you call
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
then you'll simply be left with the last thing the coffeescript returns which is the filter method.
I recently has do do something like that because I wanted to implement a factory of class instance, and being able to configurate my instances and benefit from Angular Dependency injection. I ended up with something like that
// Implem
export class XAPIService {
private path: string;
/* this DO NOT use angular injection, this is done in the factory below */
constructor(
private seed: XAPISeed,
private $http: ng.IHttpService,
private slugService: SlugService
) {
const PATH_MAP: Map<Y, Z> = new Map([
['x', id => `/x/${id}`],
['y', id => `/y/${id}`],
]);
this.path = PATH_MAP.get(this.seed.type)(this.seed.id);
}
list() {
/* implem that use configured path */
return this.slugService
.from(this.path + `/x`)
.then(url => this.$http.get<IX>(url))
.then(response => response.data)
}
}
export type IXAPIFactory = (s: XAPISeed) => XAPIService;
export function XAPIFactory(
$http: ng.IHttpService,
myService: SlugService
) {
'ngInject';
return (seed: XAPISeed) =>
new XAPIService(seed, $http, myService);
}
// angular
angular.module('xxx', [])
.factory('xAPIFactory', XAPIFactory)
// usage in code
export class XsController implements ng.IComponentController {
/* #ngInject */
constructor(
private xAPIFactory: IXAPIFactory,
) {}
$onInit() {
this.xService = this.xAPIFactory({ id: 'aaabbbaaabbb', type: 'y' });
return this.xService.list()
.then(xs => {
this.xs = xs;
})
}
}

Why is that the UI is not refreshed when data is updated in typescript and angularjs program

I learn typescript and angularjs for a few days,and now I have a question that confuses me for days, I want to make a gps tracking system, so I try to write a service like this:
1.
module Services {
export class MyService {
getGpsPeople(): Array<AppCommon.GPSPerson> {
var gpsPeople = new Array<AppCommon.GPSPerson>()
for (var i = 0; i < 10; i++) {
var tempPerson = new AppCommon.GPSPerson({ name: "username" + i.toString() });
gpsPeople.push(tempPerson);
}
return gpsPeople;
}
} // MyService class
}
A controller like this:
module AppCommon {
export class Controller {
scope: ng.IScope;
constructor($scope: ng.IScope) {
this.scope = $scope;
}
}
}
module Controllers {
export interface IMyScope extends ng.IScope {
gpsPeople: Array<AppCommon.GPSPerson>;
}
export class MyController extends AppCommon.Controller {
scope: IMyScope;
static $inject = ['$scope','myService'];
constructor($scope: IMyScope,service:Services.MyService) {
super($scope);
$scope.gpsPeople = service.getGpsPeople();
}
}
}
3.The GPSPerson class like this:
export class GPSPoint {
latitude = 0;
longtitude = 0;
constructor(la: number, lg: number) {
this.latitude = la;
this.longtitude = lg;
}
}
export interface IPerson {
name: string;
}
export class GPSPerson
{
name: string;
lastLocation: GPSPoint;
countFlag = 1;
historyLocations: Array<GPSPoint>;
timerToken: number;
startTracking() {
this.timerToken = setInterval(
() => {
var newGpsPoint = null;
var offside = Math.random();
if (this.countFlag % 2 == 0) {
newGpsPoint = new GPSPoint(this.lastLocation.latitude - offside, this.lastLocation.longtitude - offside);
}
else {
newGpsPoint = new GPSPoint(this.lastLocation.latitude + offside, this.lastLocation.longtitude + offside);
}
this.lastLocation = newGpsPoint;
this.historyLocations.push(newGpsPoint);
console.log(this.countFlag.toString() + "+++++++++++++++++++" + this.lastLocation.latitude.toString() + "----" + this.lastLocation.longtitude.toString());
this.countFlag++;
}
, 10000);
}
stopTracking() {
clearTimeout(this.timerToken);
}
constructor(data: IPerson) {
this.name = data.name;
this.lastLocation = new GPSPoint(123.2, 118.49);
this.historyLocations = new Array<GPSPoint>();
}
}
The problem is:
1.Should I make the GPSPerson class a Controller?
2.The setinterval works but the UI dose not change(when I hit button ,it changes,the button do nothing )?
I'm a beginner of ts and angular,and have no experience with js, I do not know if I have explained it clearly, hope someone can help me, thanks!
setInterval works but the ui dose not change
This is because the angular $digest loop does not run on completion of setInterval. You should use $interval service https://docs.angularjs.org/api/ng/service/$interval as that tells Angular to do its dirty checking again.
You just need to provide MyService access to $interval though. Inject it using $inject (see https://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1).
1.should i make the GPSPerson class a Controller?
No. Its an array of JavaScript objects inside the controller and that is fine.
I would separate the tracking logic and keep just the data in GPSPerson. For the tracking logic I would make a factory.
My examples are not in Typescript but I'm sure you will have no problem in converting the code if you want.
This is a link to a Plunk
I've made a much simpler example but I think you'll understand the idea.
The factory would have two methods for start and stop tracking. They will take a person as parameter.
app.factory('tracking',function($interval){
var trackingInterval;
var trackingFn = function(person){
var currentPos = Math.floor(Math.random()*10);
var newPosition = {id:person.positions.length, position:currentPos};
person.positions.push(newPosition);
};
var startTracking = function(person){
person.interval = $interval(function(){
trackingFn(person);
},2000);
};
var stopTracking = function(person){
console.log('STOP');
$interval.cancel(person.interval);
};
var getNewTrack = function(){
};
return {
startTracking: startTracking,
stopTracking: stopTracking,
};
});
I've also made a very simple directive to show the data
app.directive('position',function(){
return {
templateUrl: 'positionTemplate.html',
link: function(scope, element,attrs){
}
}
});
and the template look like this
<div>
<button ng-click="startTracking(person)">Start tracking</button>
<button ng-click="stopTracking(person)">Stop tracking</button>
<p>{{person.name}}</p>
<ul>
<li ng-repeat="pos in person.positions">{{pos.position}}</li>
</ul>
</div>
And the directive would be called this way
<div ng-repeat="person in people">
<div position></div>
</div>
I'm not saying that this is a better solution but that is how I would do it. Remember it is just a model and needs a lot of improvement.

Resources