AngularJS ng-class doesn't update - javascript

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.

Related

extending $scope while converting directive to typescript

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>

Typescript with Ember Views

I have the following Ember View in Vanilla JS:
App.TextboxView = Ember.View.extend({
tagName: 'div',
classNames: ['custom-text-area__child custom-text-area--text'],
attributeBindings: ['contenteditable'],
isEditable: true,
typing: false,
didInsertElement: function() {
},
contenteditable: (function() {
return this.get('isEditable') ? 'true' : 'false';
}).property('isEditable')
)};
I am attempting to implement said Ember View in Typescript however:
Typescript classes export a function
Ember Views expect an object
contenteditable seems to run due to Embers conventions (somewhere in its lifecycle) and sets the computed property appropriately
How could the contenteditable function be converted to work with Typescript?
So far I have stubbed out the following class:
export class TextboxView {
public tagName = 'div';
public classNames = ['custom-text-area__child custom-text-area--text'];
public atttributeBindings = ['contenteditable'];
public isEditable = true;
public typing = false;
constructor() {
var self = this;
}
}
Is this even possible?
Seems like I am creating a habit of answering my own questions....
The following implementation works as expected (I definitely tried this but obviously was too quick to assume it didn't work LOL):
export class customTextBox {
public tagName = 'div';
public classNames = ['custom-text-area__child custom-text-area--text'];
public attributeBindings = ['contenteditable'];
public isEditable = true;
public typing = false;
public contenteditable = (function () {
return this.get('isEditable') ? 'true' : 'false';
}).property('isEditable')
public keyUp = function(event) {
}
public keyDown = function(event) {
}
}

Typescript method as ng-repeat custom filter not behaving as expected

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>

Service object wrongly typed for angular directive in Typescript

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 )

My Controller constructor is never called (AngularJS)

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.

Categories

Resources