I would like to use angular's component method, but it seems something is wrong. I have been double checking this code for a while. There is no typo, it seems it fits for the documentation, but, still, it is not working.
I have checked, Angular 1.5.3 is installed.
There is no output on the console. According to the documentation and this blog entry I should see the "onInit" text.
The component's template is displayed correctly, and I can see the the template is loaded, but it seems the controller is not instantiated / fired.
My app is written in Typescript.
The component:
module sayusiando.dilib.spa {
export class LeftHandMenuComponent implements ng.IComponentOptions {
public transclude: boolean = false;
public controller: Function = LeftHandMenuController;
public controllerAs: string = "vm";
public templateUrl: string = "app/layout/leftHandMenu/leftHandMenuTemplate.html";
}
angular
.module("dilib")
.component("dilibLeftHandMenu", new LeftHandMenuComponent());
}
compiled code:
var sayusiando;
(function (sayusiando) {
var dilib;
(function (dilib) {
var spa;
(function (spa) {
var LeftHandMenuComponent = (function () {
function LeftHandMenuComponent() {
this.transclude = false;
this.controller = spa.LeftHandMenuController;
this.controllerAs = "vm";
this.templateUrl = "app/layout/leftHandMenu/leftHandMenuTemplate.html";
}
return LeftHandMenuComponent;
})();
spa.LeftHandMenuComponent = LeftHandMenuComponent;
angular
.module("dilib")
.component("dilibLeftHandMenu", new LeftHandMenuComponent());
})(spa = dilib.spa || (dilib.spa = {}));
})(dilib = sayusiando.dilib || (sayusiando.dilib = {}));
})(sayusiando || (sayusiando = {}));
Layout template:
<div>
<dilib-left-hand-menu class="col-lg-2"></dilib-left-hand-menu>
</div>
LeftHandMenuController:
module sayusiando.dilib.spa {
"use strict";
export interface ILeftHandMenuController {
}
export class LeftHandMenuController implements ILeftHandMenuController {
$onInit: Function = (() => {console.log("onInit");});
static $inject = ["LeftHandMenuService"];
constructor(leftHandMenuService: sayusiando.dilib.spa.ILeftHandMenuService) {
console.log("con");
this.leftHandMenuService = leftHandMenuService;
//this.activate();
console.log("construct");
}
activate() { //activate logic }
}
angular
.module('dilib')
.controller('leftHandMenuController', LeftHandMenuController);
}
Compiled controller code:
var sayusiando;
(function (sayusiando) {
var dilib;
(function (dilib) {
var spa;
(function (spa) {
"use strict";
var LeftHandMenuController = (function () {
function LeftHandMenuController(leftHandMenuService) {
this.$onInit = (function () { console.log("onInit"); });
console.log("con");
this.leftHandMenuService = leftHandMenuService;
//this.activate();
console.log("construct");
}
LeftHandMenuController.prototype.activate = function () {
var _this = this;
this.leftHandMenuService.getLeftHandMenu()
.then(function (result) {
_this.leftHandMenu = result;
});
};
LeftHandMenuController.$inject = ["LeftHandMenuService"];
return LeftHandMenuController;
})();
spa.LeftHandMenuController = LeftHandMenuController;
angular
.module('dilib')
.controller('leftHandMenuController', LeftHandMenuController);
})(spa = dilib.spa || (dilib.spa = {}));
})(dilib = sayusiando.dilib || (sayusiando.dilib = {}));
})(sayusiando || (sayusiando = {}));
I called the $oninit in a wrong way. Here is the proper, well working code:
module sayusiando.dilib.spa {
"use strict";
export interface ILeftHandMenuControllerScope {
}
export class LeftHandMenuController implements ILeftHandMenuControllerScope {
public leftHandMenu: Array<sayusiando.dilib.spa.IModuleContract>;
static $inject = ["leftHandMenuService"];
constructor(
private leftHandMenuService: sayusiando.dilib.spa.ILeftHandMenuService) {
}
public $onInit = () => {
this.leftHandMenuService.getLeftHandMenu()
.then((result: Array<sayusiando.dilib.spa.IModuleContract>): void => {
this.leftHandMenu = result;
});
}
}
angular
.module('dilib')
.controller('leftHandMenuController', LeftHandMenuController);
}
I think this is due to missing dependency parameter list on the module definition. There is a difference in these two statements:
angular.module("dilib")
angular.module("dilib",[])
The first statement tries to access an existing module with name dilib, whereas the second statement tries to create a module dilib with no dependencies. I believe you are trying to create a new module, and hence will need the second format.
Related
In TypeScript I have this simple code:
namespace Customer {
function onOpen() {
}
}
It generates:
var Customer;
(function (Customer) {
function onOpen() {
}
})(Customer || (Customer = {}));
In order for Kendo to use onOpen the JS needs to look like this (Notice the onOpen). Is this possible?:
var Customer;
(function (Customer) {
Customer.onOpen = function () {
}
})(Customer || (Customer = {}));
Add export:
namespace Customer {
export function onOpen() {
}
}
Produces:
var Customer;
(function (Customer) {
function onOpen() {
}
Customer.onOpen = onOpen;
})(Customer || (Customer = {}));
You need to export exposed properties, and since functions are first class citizens, you can write typescript code like this :
namespace Customer {
export const onOpen = () => {}
}
Or instead of a lambda just with normal function
namespace Customer {
export const onOpen = function() {}
}
Or this will work
namespace Customer {
export function onOpen() { }
}
That will generate a property that points to a function instead of a function member - essentially the same thing really.
Javascript output:
var Customer;
(function (Customer) {
Customer.onOpen = function () { };
})(Customer || (Customer = {}));
var app = angular.module('testApp', []);
class Component {
constructor(app, name, template, as, bindings) {
this.bindings = bindings;
this.config = {}
this.config.template = template;
this.config.controllerAs = as;
// pre-create properties
this.config.controller = this.controller;
this.config['bindings'] = this.bindings;
app.component(name, this.config);
console.log("Inside Component ctor()");
}
addBindings(name, bindingType) {
this.bindings[name] = bindingType;
}
controller() {
}
}
class App extends Component {
constructor(app) {
var bindings = {
name: "<"
};
super(app, "app", "Hello", "vm", bindings);
}
controller() {
this.$onInit = () => this.Init(); // DOESN'T WORK
/*
var self = this;
self.$onInit = function () { self.Init(); }; // DOESN'T WORK
*/
/*
this.$onInit = function () { // WORKS
console.log("This works but I don't like it!");
};
*/
}
Init() {
console.log("Init");
}
onNameSelected(user) {
this.selectedUser = user;
}
}
var myApp = new App(app);
<div ng-app="testApp">
<app></app>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
I'm trying to "classify" angular 1.5's .component(). I can get most of it figured out but when I try to assign a class method for $onInit it doesn't work. I've tried assigning to it and using arrow notation to call back to the class method but neither work. It does work if I assign an anonymous function directly but I don't want to do that. I want those functions to point to class methods because I find it cleaner.
So ultimately I want my App classes Init() method to get called for $onInit(). Is it possible?
I'm working on an app and I'm playing around with Angular2 for fun. I'm using ES5 JavaScript, and my question right now is how do I access the Http service? All of the documentation available is TypeScript (which is not helpful) or it's for an alpha version of Angular2, and the system has changed since.
I'm using Angular2 version 2.0.0-beta.13. I am receiving the following error: TypeError: ng.http.get is not a function in [null]. I've tried using ng.http.Http.get to no avail. I have angular2-all.umd.js included in the head, as well as the other js requirements defined by angular.io (RxJS, es6 shims, polyfills, etc).
Here are the code snippets I have, for reference. All files are concatenated together for ease of reading.
;(function (app, ng) {
app.CommandService = (function () {
var CommandService = function () {
this.url = 'api/commands';
};
CommandService.prototype.all = function () {
return ng.http.get(this.url)
.map(function (response) {
return response.json().data;
})
.catch();
};
return CommandService;
})();
})(window.app || (window.app = {}), window.ng);
;(function (app, ng) {
app.CommandComponent = (function () {
function CommandComponent(commandService) {
this.commandService = commandService;
this.commands = [];
}
CommandComponent.parameters = [
app.CommandService
];
CommandComponent.annotations = [
new ng.core.Component({
selector: '#command-listing',
templateUrl: 'api/templates/commands/listing',
providers: [
app.CommandService,
ng.http.HTTP_PROVIDERS
]
})
];
CommandComponent.prototype.all = function () {
var self = this;
this.commandService.all().subscribe(
function (commands) {
self.commands = commands;
}, function (error) {
self.error = error;
}
);
};
CommandComponent.prototype.ngOnInit = function () {
this.all();
};
return CommandComponent;
})();
})(window.app || (window.app = {}), window.ng);
;(function (app, ng) {
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(app.CommandComponent);
});
})(window.app || (window.app = {}), window.ng);
From a timing perspective, TypeScript does not seem to be a viable option. I tried to set up the environment as needed, and after a full day of debugging TS issues, I now have SystemJS issues, so I'm hoping the plain JS option can suffice for now until I have time to figure out all of the intricacies.
Let me know if more information is needed; I'm happy to give it.
Change your CommandService:
;(function (app, ng) {
app.CommandService = (function () {
var CommandService = function (http) { // this line changed
this.url = 'api/commands';
this.http = http; // this line added
};
CommandService.parameters = [ // this line added
ng.http.Http // this will be passed as arg in the constructor
]; // this line added
CommandService.prototype.all = function () {
return this.http.get(this.url) // this line changed
.map(function (response) {
return response.json().data;
})
.catch();
};
return CommandService;
})();
})(window.app || (window.app = {}), window.ng);
See plunker: http://plnkr.co/edit/4FG1Lrt7Yhnzo20azV8Z?p=preview
Just as an additional info, you could remove from the CommandComponent (line 41 of app/main.js) and add the ng.http.HTTP_PROVIDERS at the bootstrap() like this:
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(app.CommandComponent, [ng.http.HTTP_PROVIDERS]);
});
i want to share data between components, so im implemented a Service which has an EventEmitter.
My Service looks like this:
#Injectable()
export class LanguageService {
constructor() {
this.languageEventEmitter = new EventEmitter();
this.languages = [];
this.setLanguages();
}
setLanguages() {
var self = this;
axios.get('/api/' + api.version + '/' + api.language)
.then(function (response) {
_.each(response.data, function (language) {
language.selected = false;
self.languages.push(language);
});
self.languageEventEmitter.emit(self.languages);
})
.catch(function (response) {
});
}
getLanguages() {
return this.languages;
}
toggleSelection(language) {
var self = this;
language.selected = !language.selected;
self.languages.push(language);
self.languageEventEmitter.emit(self.languages);
}
}
I have to components, which are subscribing to the service like this:
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
});
});
When both components are loaded, the language arrays get filled as i wish.
This is the first component:
export class LanguageComponent {
static get parameters() {
return [[LanguageService]];
}
constructor(languageService) {
var self = this;
this.languageService = languageService;
this.languages = [];
this.setLanguages();
}
setLanguages() {
var self = this;
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
})
});
}
updateLanguages(newLanguage) {
var self = this;
if (!newLanguage) {
return;
}
var match = _.find(self.languages, function (language) {
return newLanguage._id === language._id;
});
if (!match) {
self.languages.push(newLanguage);
}
else {
_.forOwn(newLanguage, function (value, key) {
match[key] = value;
})
}
toggleLanguageSelection(language) {
var self = this;
self.languageService.toggleSelection(language)
}
}
When LanguageComponent executes the function toggleLanguageSelection() which triggered by a click event, the other component, which subscribes like this:
self.languageService.languageEventEmitter.subscribe((newLanguages) => {
_.each(newLanguages, function (language) {
self.updateLanguages(language);
})
});
doesn't get notfiefied of the change. I think this happens because both component get a different instance of my LanguageService, but i'm not sure about that. I also tried to create a singleton, but angular'2 di doesn't work then anymore. What is the reason for this issue and how can i solve this ?
You need to define your shared service when bootstrapping your application:
bootstrap(AppComponent, [ SharedService ]);
and not defining it again within the providers attribute of your components. This way you will have a single instance of the service for the whole application. Components can leverage it to communicate together.
This is because of the "hierarchical injectors" feature of Angular2. For more details, see this question:
What's the best way to inject one service into another in angular 2 (Beta)?
Below is typescript for an angular directive. The problem is with the injected service "datacontext". The debugger shows that the datacontext in the constructor is a WINDOW object, not the datacontext object that was created as a service. Therefore, in the scope.viewRecord function, the datacontext.cancelChanges() function is, obviously, undefined - - as its not part of a WINDOW object This is probably some strange scoping issue that I just don't get, but I'm at a loss as to how to debug this. Any insight would be most appreciated. Thanks.
module app {
export interface IEditButtonGroup {
...
}
export interface IEditButtonScope extends ng.IScope {
...
}
export class PrxEditButtonGroup implements IEditButtonGroup {
public static $inject: Array<string> = [
"datacontext"
];
constructor(
public datacontext: IDataContext, <---- datacontext HERE is typed as a WINDOW object
public directive: ng.IDirective = {}) {
directive.templateUrl = "app/directives/templates/editbuttongroup.html",
directive.restrict = 'E';
directive.link = (scope: IEditButtonScope, element, attr) => {
scope.isEditing = false;
scope.isAdding = false;
$("form.disabled").find("input:not(:disabled), select:not(:disabled), textarea:not(:disabled)").prop("disabled", true);
scope.editRecord = () => {
$("input, select, textarea").removeAttr("disabled");
scope.isEditing = true;
scope.afterEdit();
}
scope.viewRecord = (afterCancel: boolean) => {
datacontext.cancelChanges(); <--- HERE TOO! Debugger says datacontext = WINDOW object
scope.isEditing = scope.isAdding = false;
$("form.disabled").find("input:not(:disabled), select:not(:disabled), textarea:not(:disabled)").prop("disabled", true);
scope.afterAdd();
}
}
return <any>directive;
}
}
}
The error seems to be at the place where you register this directive. Make sure it is like :
mymodule.directive('prxEditButtonGroup',app.PrxEditButtonGroup )