I am setting up a small angularjs app inside a Sharepoint web part. This is the first time doing this, but I made a demo work in this setting before doing the proper code. My problem now is that I think everything is properly hooked up, but the Controller constructor is never called? I am aware that the code is not complete, but at the very least I should enter the constructor and call the service. The only alerts I see are the ones in the App file. Here are the different files:
HTML:
<div id="bidragsoplysninger2015App">
<div data-ng-controller="bidragsoplysninger2015Controller">
<div id="template-content" ng-include="'/AngularJS/Views/Bidragsoplysninger2015.html'"></div>
</div>
Partial:
<ul>
<li data-ng-repeat="indbetaling in indbetalinger | orderBy:'registreringsdato'">{{indbetaling.registreringsdato}} - {{indbetaling.beloeb}}</li>
</ul>
Model:
class Indbetaling {
constructor(
public beloeb: number,
public betalingsform: string,
public fagkode: string,
public indskudstype: string,
public institutionsNr: string,
public kundeNr: number,
public ordningNr: number,
public pensionsform: string,
public pensionsmodel: string,
public registreringsdato: Date,
public valoerMaaned: string
) {}
}
Interface:
interface IIndbetalinger {
getIndbetalinger(cpr: string): Indbetaling[]
}
Service:
class Bidragsoplysninger2015Service implements IIndbetalinger
{
private scope: any;
private http: any;
constructor($scope: ng.IScope, $http: ng.IHttpService) {
this.scope = $scope;
this.http = $http;
}
getIndbetalinger(cpr: string): Indbetaling[]
{
var promise = this.http.get('/_layouts/wpgenerelportal/Indbetalinger.aspx/GetIndbetalinger?cpr=' + cpr);
promise = promise.then(this.mapIndbetalinger).then(response => response.data);
return promise;
}
mapIndbetalinger(data) {
alert("Mapping!");
}
}
Controller:
class Bidragsoplysninger2015Controller {
private indbetalinger: Indbetaling[];
public static $inject = [
'$scope',
'bidragsoplysninger2015Service'
];
constructor(private $scope, private bidragsoplysninger2015Service: IIndbetalinger) {
alert("Controller constructor");
this.indbetalinger = $scope.indbetalinger = bidragsoplysninger2015Service.getIndbetalinger("110680-3419");
// 'vm' stands for 'view model'. We're adding a reference to the controller to the scope
// for its methods to be accessible from view / HTML
$scope.vm = this;
}
}
App:
alert("In app 1");
var bidragsoplysninger2015AppModule = angular.module('bidragsoplysninger2015App', []);
bidragsoplysninger2015AppModule.service("bidragsoplysninger2015Service", Bidragsoplysninger2015Service);
bidragsoplysninger2015AppModule.controller("bidragsoplysninger2015Controller", Bidragsoplysninger2015Controller);
alert("In app 2");
$(document).ready(() => {
alert("In app 3");
angular.bootstrap($("#bidragsoplysninger2015App"), ['bidragsoplysninger2015App']);
});
I found the problem buried in this post: 'unknown provider error' in Angular app
The sentence: "Services don't have $scope" was the key. Once I removed the $scope as an input parameter to the service constructor, everything worked.
Related
I'm working on a simple project using nodejs and angular 2.
In my client project I have a component which has a form and a submit event. When I throw this event all data from the form is properly sent to my node application.
But I have another component which has a table where I list all registers located on my database.
Everything works fine. But I should make my table load in the same time after I've sent my submit.
I have no idea how I can do this.
I tried implement something like this:
export class EmployeeListComponent implements OnInit {
public employees: string[];
constructor(private employeeService : EmployeeService) {
this.employeeService.employeeRequest().subscribe(employees => {
this.employees = employees;
})
}
ngOnInit() {
}
}
And in my register component I did this method:
onRegister(){
const Employee = {
name : this.name,
familyName: this.familyName,
participation: this.participation
}
if (!this.validateService.validateRegister(Employee)){
this.alertMsg = JSON.stringify({'msg': 'All fields must be filled'});
console.log(this.alertMsg);
return false;
} else {
this.registerService.employeeRegister(Employee).subscribe(data => {
this.alertMsg = JSON.stringify({'msg': 'Employee registered successfully'});
console.log(this.alertMsg);
return this.router.navigate(['/list']);
});
};
};
My tests show that my code works fine, but my table just load properly at first submit. After that I have to refresh manually the browser to load my table again.
Is there someone who know what I'm doing wrong or can tell me some way to code that?
Try to change your constructor to a function, and than you call it on ngOninit, so every time your list component initialize your list of employees will load again, something like this:
export class EmployeeListComponent implements OnInit {
public employees: string[];
constructor(private employeeService : EmployeeService) { }
ngOnInit() {
this.loadAllEmployees();
}
loadAllEmployees() {
this.employeeService.employeeRequest().subscribe(data => {
this.employees = data;
});
}
}
Also see Angular life cycle hooks documentation to understand more about ngOnInit and others:
https://angular.io/guide/lifecycle-hooks
Well. After I got the suggestions I tried another strategies. First I've simplify my two components as a single component. Then I moved my logic from the constructor to a method called dashCall() which I call inside my constructor and in the final of onRegister() method.
The final code looks like this:
export class EmployeeRegisterComponent {
public name: string;
public familyName: string;
public participation: number;
public alertMsg: any;
public employees: string[];
constructor(private validateService : ValidateService,
private registerService : EmployeeService,
private employeeService : EmployeeService) {
this.dashCall();
}
onRegister(){
const Employee = {
name : this.name,
familyName: this.familyName,
participation: this.participation
}
if (!this.validateService.validateRegister(Employee)){
this.alertMsg = JSON.stringify({'msg': 'All fields must be filled'});
return false;
} else {
this.registerService.employeeRegister(Employee).subscribe(data => {
this.alertMsg = JSON.stringify({'msg': 'Employee registered successfully'});
this.dashCall();
});
};
};
dashCall(){
this.employeeService.employeeRequest().subscribe(employees => {
this.employees = employees;
});
}
}
I know it's not the best way to implement this. But in the end I got what I needed.
I've got a problem with ng-class.
This is a piece of my controller
export class UIController
{
public static $inject = [];
public commands: Array<string> = ['showMap', 'showGantry', 'showSign'];
public showMap: boolean = false;
public showGantry: boolean = false;
public showSign: boolean = false;
public loadingMessage: string;
constructor()
{
this.showMap = true;
}
public toggleContent(commandToExecute: string)
{
for (var command of this.commands) {
if (command != commandToExecute) {
this[command] = false;
}
else if (!this[command]) {
this[command] = true;
}
}
}
}
directive that is connected with it
class LeafletMap implements ng.IDirective
{
public restrict: string = "A";
public controller: string = 'UIController';
public link = (scope, elem, attr) : void =>
{
scope.uiCtrl.loadingMessage = 'test';
scope.$root.$on('details', (event: IAngularEvent, message: any) =>
{
scope.uiCtrl.toggleContent('showGantry');
});
};
}
and piece of HTML view:
<div class="accordion-button" data-ng-click="uiCtrl.toggleContent('showGantry')">Gantry</div>
<div class="accordion-content" data-ng-class="{'show-content':uiCtrl.showGantry}">test</div>
When I call toggleContent method via ng-click, everything works fine then.
But when the call comes from LeafletMap directive it runs toggleContent method and sets proper boolean values to public properties in UIController but ng-class doesn't update.
scope.uiCtrl.loadingMessage = 'test'; //it works correctly BTW
When I run scope.$apply() (just for test) inside LeafletMap after calling toggleContent method, then ng-class was updated, but it caused errors like apply already in progress of course.
Why ng-class doesn't update when I call appropriate controller method within directive?
EDIT
scope.$root.$on('gantry-details', (event: IAngularEvent, message: any) =>
{
this._timeout(() =>
{
scope.uiCtrl.toggleContent('showGantry');
});
});
Using timeout solved the problem.
The following is my typescript angular controller. I want a method to trigger every time when the url changed (even a small part of url e.g. a number).
I made the method what i want to trigger, i have a constructor in the controller. How can i know when to trigger that method and where do i need to call it? Inside the constructor to initialize it?
This is my controller's code:
private returnedClass: Class;
static $inject: Array<string> = ['$scope', 'common', 'repository.class'];
constructor(public scope: any, public common: app.core.Common, public myService: app.data.IRepositoryClass) {
}
getFromDB(): void{
if (location.href.indexOf("classAdd") !== -1 && typeof this.common.itemId !== 'undefined' && this.common.itemId != null)
{
this.myService.getClassById(this.common.itemId).then((returnedItem: Class) => {
this.returnedClass = returnedItem;
console.log(this.returnedClass);
});
}
}
}
angular
.module('app.layout')
.controller('HeaderNavigationController', HeaderNavigationController);
I want to mention that this controller is 1 time executed, when the entire HTML DOM is loaded (index.html). Do i need a watcher or something? How can i do that?
Thanks in advance
If you are using uiRouter listen for the $stateChangeSuccess event:
constructor(private scope: any, private common: app.core.Common, private myService: app.data.IRepositoryClass) {
scope.$on('$stateChangeSuccess', () => this.getFromDB());
}
Side Note it is a good practice to mark your dependancies as private rather than public.
i want to convert existing directive in javascript to typescript. How do i convert below function
$scope.loadData = function () {
$scope.tableParams = $cspagination.initPromise(getPagedData);
};
So I am trying to write it as
class controller {
this $scope.loadData = (): void {
.....
.....
};
}
but it is giving error that this is not available on class level.
then i tried
class controller {
public $scope.loadData = (): void {
.....
.....
};
}
but this also does not work. its obvious that I cannot define a new public property on $scope, but then atleast I should be able to assign value to it.
so how do I dynamically add functions on $scope?? The workaround I could think of is creating a function extendScope and then
class controller {
public loadData = (): void => {
.....
.....
};
private extendScope = (): void =>{
this.$scope.loadData = this.loaddata;
}
constructor(){
this.extendScope();
}
}
but then this feels like a hack.. are there any cleaner ways of doing this?
The way I go - is to create custom scope definition (i.e. inteface), e.g.:
export interface IMyScope extends ng.IScope
{
loadData: () => void;
otherFunction: function;
...
Ctrl: MyCtrl;
}
and Controller constructor now requires that interface
export class MyCtrl
{
static $inject = ["$scope", ...];
constructor(protected $scope: IMyScope ,
...)
{
this.$scope.Ctrl = this; // we can use "controllerAs" as well
// and here we can add these functions
this.$scope.loadData = this.loadData;
this.$scope.otherFunction = function() {};
...
}
public loadData = (): void => {
//.....
}
See more here:
How To bind data using TypeScript Controller & Angular Js
I don't see anything wrong with this, only that your loadData method shouldn't be public in this case. What I would do though is use the 'controller-as' method:
class controller {
static ID = "myController";
// defining methods like this will make them immediately available on
// the controller with the 'controller as' method
public loadData = (): void => {
//.....
}
constructor(){
}
}
And in your html:
<div ng-controller="myController as $my">
<button ng-click="$my.loadData()">Load!</button>
</div>
module ngrFilter{
'use strict';
export class UsersCtrl {
public userCollection: any[];
public userFilter: string;
constructor(){
this.userCollection = [{id:1,name:'John',surname:'Klopper'},
{id:2,name:'Mary',surname:'Schoeman'}];
}
public filterUser(user){
//The this is undefined when using as a custom filter for ng-repeat
console.log(this.userFilter)
if(user.name == this.userFilter ||
user.surname == this.userFilter){
return true;
}
}
}
angular
.module('ngrFilter',[])
.controller('UsersCtrl', UsersCtrl);
}
When using a custom filter for ng-repeat the this property is undefined in the filterUser method. Is there a way to get this filter to work or am I just doing something stupid.
I also tried adding a sample of the code here: http://fiddlesalad.com/typescript/custom-ngrepeat-filter/
Instead of a method, use a public field as filter function, which can create a closure for this.
export class UsersCtrl {
public userCollection: any[];
public userFilter: string;
public filterUser: (User) => boolean;
constructor(){
this.userCollection = [{id:1,name:'John',surname:'Klopper'},
{id:2,name:'Mary',surname:'Schoeman'}];
var self = this;
this.filterUser = function(user:User) {
return self./*...*/;
}
}
}
What you are trying to do can be done via an angular filter
For what you're trying to do I would just handle an event and rebind the list upon changing of the filter
export class UsersCtrl {
public userCollection: any[];
public filteredUserCollection: any[];
public userFilter: string;
constructor(){
this.userCollection = [{id:1,name:'John',surname:'Klopper'},
{id:2,name:'Mary',surname:'Schoeman'}];
this.filteredUserCollection = this.userCollection;
}
public filterChanged(filter){
this.filterUser(filter);
}
public filterUser(filter){
var results = [];
if(user.name == filter ||
user.surname == filter){
results.push(user);
}
this.filteredUserCollection = results;
}
}
And then your markup would change to:
<input ng-model="users.userFilter" ng-change="users.filterChanged(users.userFilter)">
<ul>
<li ng-repeat="user in users.filteredUserCollection">
{{user.name}} {{user.surname}}
</li>
</ul>