Angular with Coffeescript: why my method are executed? - angularjs

I'm an angular beginner, and coming from Ruby I choose to use Coffescript instead of JS. I'm using ng-classify to define my controller, services and Factory with Coffeescript classes, but I cannot understand what is wrong.
I have my code in this [github repo], but I try to explain here my issue.
I have this controller
class Setting extends Controller
constructor: (#DataService,$log) ->
#examType = #DataService.getObject('setting_examtype') || { checked: false }
#settingList = #DataService.getObject('setting_list') || [
{ text: 'Dai precedenza a domande sbagliate', checked: false },
{ text: 'Dai precedenza a domande mai fatte', checked: false },
{ text: 'Mostra subito la soluzione', checked: false }
]
#questionPossibility = [10,20,30,40,50]
#questionNumber = #DataService.get('question_number') || 30
return
examTypeChecked: () =>
#DataService.setObject('setting_examtype',#examType)
console.log 'examTypeChecked'
return
settingListChecked: () =>
console.log 'settingListChecked'
#DataService.setObject('setting_list',#settingList)
return
questionNumberChecked: () =>
console.log 'questionNumberChecked'
#DataService.set('question_number',#questionNumber)
return
The compiled version is:
(function() {
var Setting,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Setting = (function() {
function Setting(DataService, $log) {
this.DataService = DataService;
this.questionNumberChecked = __bind(this.questionNumberChecked, this);
this.settingListChecked = __bind(this.settingListChecked, this);
this.examTypeChecked = __bind(this.examTypeChecked, this);
this.examType = this.DataService.getObject('setting_examtype') || {
checked: false
};
this.settingList = this.DataService.getObject('setting_list') || [
{
text: 'Dai precedenza a domande sbagliate',
checked: false
}, {
text: 'Dai precedenza a domande mai fatte',
checked: false
}, {
text: 'Mostra subito la soluzione',
checked: false
}
];
this.questionPossibility = [10, 20, 30, 40, 50];
this.questionNumber = this.DataService.get('question_number') || 30;
return;
}
Setting.prototype.examTypeChecked = function() {
this.DataService.setObject('setting_examtype', this.examType);
console.log('examTypeChecked');
};
Setting.prototype.settingListChecked = function() {
console.log('settingListChecked');
this.DataService.setObject('setting_list', this.settingList);
};
Setting.prototype.questionNumberChecked = function() {
console.log('questionNumberChecked');
this.DataService.set('question_number', this.questionNumber);
};
return Setting;
})();
angular.module('app').controller('settingController', ['DataService', '$log', Setting]);
}).call(this);
As you can see I insert some log statement, and from the console I understand that all my methods are executed. Why? Why examTypeChecked is called?
I call it only if someone use a toggle..
<ion-toggle ng-model="setting.examType" ng-checked="setting.examTypeChecked()" toggle-class="toggle-calm" ng-true-value="oltre" ng-false-value="entro">Tipo di esame</ion-toggle>

You got it wrong way, your code is fine, use of code is not what you expected
<ion-toggle ng-model="setting.examType" ng-checked="setting.examTypeChecked()" toggle-class="toggle-calm" ng-true-value="oltre" ng-false-value="entro">Tipo di esame</ion-toggle>
setting.examTypeChecked() will be called every time $digest() process is triggered, and it's triggered with each change of model, by $scope.apply(), $scope.digest(), $timeout() and few more

Related

Does filter in AngularJs work if value = 0?

I have $scope.event.privacy = 0;
When I'm trying todo {{ event.privacy | eventPrivacyFilter }} filter doesn't do anythink... But if $scope.event.privacy = 1 or 2 or ..... it's work.
Does filter in AngularJs work if value = 0?
Filter:
(function() {
'use strict';
angular.module('eventMod').filter('eventPrivacyFilter', eventPrivacyFilter);
function eventPrivacyFilter(EventPrivacyRegistry) {
return function(privacyId) {
if (!privacyId) {return null;}
return EventPrivacyRegistry.getById(privacyId).name;
};
}
})();
Regist:
(function() {
'use strict';
angular.module('eventMod').service('EventPrivacyRegistry', EventPrivacyRegistry);
function EventPrivacyRegistry(_) {
return {
getList: function () {
return [
{ id: 0, name: '1'},
{ id: 1, name: '2'},
{ id: 2, name: '3'},
{ id: 3, name: '4'}
];
},
getById: function (privacyId) {
console.log(privacyId);
console.log(_.find(this.getList(), {id: privacyId }));
return _.find(this.getList(), {id: privacyId });
}
};
}
})();
if (!privacyId) {return null;} is the statement which returns null when $scope.event.privacy = 0 because if(!0) evaluates to true.
And therefore, EventPrivacyRegistry.getById(privacyId).name; is not executed in this case.
The solution would be to compare with null and undefined as follows:
if(privacyId === null || privacyId === undefined) {
return;
}
It has nothing to do with AngularJs. This is javascript falsy value.
Zero (0) is evaluated to falsy value in your example.
If want to strict comparison then you need to explicitly ask for it.
So change your code like this
// old code
{{ event.privacy | eventPrivacyFilter }}
// new code
{{ event.privacy != null | eventPrivacyFilter }}
Read more about comparing falsy and truthy values here http://www.sitepoint.com/javascript-truthy-falsy/

ngChange is not picking up changes from the model

I am trying to access the obj from the select in my controller but the only thing that I end up getting is null and undefined.
<select
ng-change="watchActions(part)"
ng-options="part.serial as part.description for part in rawBaList.items"
ng-model="currentChose"
></select>
$scope.currentChose = null;
$scope.watchActions = function (obj) {
console.log($scope.currentChose);
console.log(obj);
};
$scope.$watch('currentChose', function (newValue, oldValue) {
if(newValue){
console.log("here the new value goes");
}
});
Here is the ex of data:
{
count: 2,
items: [
{serial: 2, description: 'here is some description'},
{serial: 24, description: 'some other description'}
]
}
it was a scope issue, $parent.currentChose fixed it

Finding the deepest nested components in DOM tree

I am trying to implement a keybinding mixin which let me write something like
createClass({
...
keybindings: function() {
return {
'escape' : function(event) { this._handleEscapeKey(); },
'enter' : function(event) { this._handleEnterKey(); },
'up' : function(event) { this._handleUpKey(); },
'down' : function(event) { this._handleDownKey(); },
'left' : function(event) { this._handleLeftKey(); },
'right' : function(event) { this._handleRightKey(); },
};
},
In my component. However I have problems when multiple components include the mixin. I need a way make the event listeners on the most deeply nested components in the DOM tree get precedence over its ancestors.
This is what I got so far, any ideas/suggestions/feedback is much appreciated
mixin:
KeybindingsMixin = {
modifiers: {
shift: 16,
ctrl: 17,
alt: 18,
meta: 91
},
keyCodes: {
escape : 27,
up : 38,
down : 40,
left : 37,
right : 39,
enter : 13,
shift : 16,
ctrl : 17,
alt : 18,
meta : 91,
s : 83,
},
singleModifier: {
shift: {
keyCode : 16,
shift : true,
ctrl : false,
alt : false,
meta : false
}
},
componentDidMount: function() {
if (this.__keybindings !== undefined) {
var keybindings = this.getAllKeybindings();
this.__keybindings = _.merge(this.__keybindings, keybindings);
$(window).on('keydown', this.__matchEvent);
}
},
componentWillUnmount: function() {
if (this.__keybindings !== undefined) {
var keybindings = _.keys(this.getAllKeybindings());
this.__keybindings = _.omit(this.__keybindings, keybindings);
$(window).off('keydown', this.__matchEvent);
}
},
childContextTypes: {
__keybindings: React.PropTypes.object
},
contextTypes: {
__keybindings: React.PropTypes.object
},
getAllKeybindings: function() {
return this.__getKeybindings();
},
getChildContext: function() {
return {
__keybindings: this.__getKeybindings()
};
},
__getKeybindings: function() {
var keybindings = Object.getPrototypeOf(this).keybindings();
this.__keybindings = this.__keybindings || (this.context && this.context.__keybindings) || keybindings || {};
return this.__keybindings;
},
__parseKeybindingString: function(binding) {
var tokens = binding.split(' ');
var modifiers = _.keys(this.modifiers);
var bindings = _.keys(this.keyCodes);
var parsedEvent = {
keyCode: 0,
alt: false,
ctrl: false,
shift: false,
meta: false
};
_.each(tokens, function(token) {
if (_.includes(modifiers, token)) {
parsedEvent[token] = true;
} else if (_.includes(bindings, token)) {
parsedEvent.keyCode = this.keyCodes[token];
}
}, this);
return parsedEvent;
},
__keybindingSpecified: function(event) {
},
__matchEvent: function(event) {
var eventMatcher = {
keyCode: event.keyCode,
alt: event.altKey,
ctrl: event.ctrlKey,
shift: event.shiftKey,
meta: event.metaKey,
};
var keybindings = this.__keybindings;
_.forOwn(keybindings, function(action, binding) {
var parsedEvent = this.__parseKeybindingString(binding);
if (_.isEqual(eventMatcher, parsedEvent)) {
event.preventDefault();
return action.call(this, event);
}
}, this);
return;
},
};
If by "deeply nested components in the DOM tree get precedence over its ancestors" you mean that the event should only trigger on the most deeply nested component and not bubble up to the components further up, you should call event.stopPropagation(). This cancels the bubbling, and the components further up won't get the event.
EDIT
Ah, sorry, I thought you used Reacts event system which allows stopPropagation even if they only attach one event handler to the root element. What you can do is something similar to what React does, in that for each element that uses the the mixin, you attach some property to that DOM node to say that it's listening to the event. Then when the event comes in, you check event.target and see if it has that property. If it doesn't, you check event.target.parentElement and recursive upwards until you find it. And as soon as you find that element, you stop going upwards to simulate the effect of stopPropagation.

AngularJS + Datatables + Dropdownlist

I'm using AngularJS to populate my datatable. What I want to know is how can I populate the datatable based on the dropdownlist
This is my dropdownlist
<div>
Get Users with Role:
<select id="ddlRole" data-ng-model="selectedRole" data-ng-change="populateDataTable()" data-ng-options="v.name for (k,v) in roles"></select>
<input type="hidden" value="{{selectedRole}}" />
</div>
This is my angular code
$scope.roles = [
{name: 'XXX' },
{name: 'YYY' }
];
$scope.selectedRole = $scope.roles[0];
//onchange event
$scope.populateDataTable = function () {
$scope.selectedRole = $scope.selectedRole.name;
RefreshDataTable(); //TODO
};
How can I change this to make an ajax call to retreive the data, populate the datatable based on the dropdownlist value and retain the value of dropdownlist as well.
I'm sure we can do this using JQuery but I dont want to mix these and make a mess. Is there any way I can acheive this using AngularJS?
Here is a simple data table directive:
appModule.directive('dataTable', [function () {
return function (scope, element, attrs) {
// apply DataTable options, use defaults if none specified by user
var options = {};
if (attrs.myTable.length > 0) {
options = scope.$eval(attrs.myTable);
} else {
options = {
"bStateSave": true,
"iCookieDuration": 2419200, /* 1 month */
"bJQueryUI": true,
"bPaginate": false,
"bLengthChange": false,
"bFilter": false,
"bInfo": false,
"bDestroy": true
};
}
// Tell the dataTables plugin what columns to use
// We can either derive them from the dom, or use setup from the controller
var explicitColumns = [];
element.find('th').each(function (index, elem) {
explicitColumns.push($(elem).text());
});
if (explicitColumns.length > 0) {
options["aoColumns"] = explicitColumns;
} else if (attrs.aoColumns) {
options["aoColumns"] = scope.$eval(attrs.aoColumns);
}
// aoColumnDefs is dataTables way of providing fine control over column config
if (attrs.aoColumnDefs) {
options["aoColumnDefs"] = scope.$eval(attrs.aoColumnDefs);
}
if (attrs.fnRowCallback) {
options["fnRowCallback"] = scope.$eval(attrs.fnRowCallback);
}
// apply the plugin
var dataTable = element.dataTable(options);
// watch for any changes to our data, rebuild the DataTable
scope.$watch(attrs.aaData, function (value) {
var val = value || null;
if (val) {
dataTable.fnClearTable();
dataTable.fnAddData(scope.$eval(attrs.aaData));
}
});
if (attrs.useParentScope) {
scope.$parent.dataTable = dataTable;
} else {
scope.dataTable = dataTable;
}
};
}]);
Then initialize it in your controller. Override fnServerData method, append your selected value (selected role) and filter data on server side.
$scope.overrideOptions = {
"bStateSave": true,
"iDisplayLength": 8,
"bProcessing": false,
"bServerSide": true,
"sAjaxSource": 'Data/Get',
"bFilter": false,
"bInfo": true,
"bLengthChange": false,
"sServerMethod": 'POST', ,
"fnServerData": function(sUrl, aoData, fnCallback, oSettings) {
var data = {
dataTableRequest: aoData,
selectedDropDownValue: $scope.selectedRole
};
$http.post(sUrl, data).success(function (json) {
if (json.sError) {
oSettings.oApi._fnLog(oSettings, 0, json.sError);
}
$(oSettings.oInstance).trigger('xhr', [oSettings, json]);
fnCallback(json);
});
}
};
var columnDefs = [
{
"mData": "id",
"bSortable": false,
"bSearchable": false,
"aTargets": ['tb-id']
},
{
"mData": "data",
"aTargets": ['tb-data']
}
];
Refresh the datatable on select change.
$scope.populateDataTable = function () {
if ($scope.dataTable) {
$scope.dataTable.fnDraw();
}
};
Html markup:
<table class="display m-t10px" data-table="overrideOptions" ao-column-defs="columnDefs">
<thead>
<tr>
<th class="tb-id"></th>
<th class="tb-data></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Hope above your code is in controller.
Inject $http and make a $http get or post call
$scope.populateDataTable = function () {
$scope.selectedRole = $scope.selectedRole.name;
$http.get('api/controller', function(result){
//response from the service call in result
});
};

Rally App SDK getting object ID of feature

var showAssignedProgram = 1;
var value = null;
var showIterationCombo = 0;
var iterationComboValue = null;
var lumenize = window.parent.Rally.data.lookback.Lumenize;
var iterationComboField = null;
var iterationRecord = myMask = null;
var setOfStories = setOfFeatures = null;
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
//Write app code here
Ext.state.Manager.setProvider(
new Ext.state.CookieProvider({ expires: new Date(new Date().getTime()+(10006060247)) })
);
app = this;
var that = this;
console.log("launch");
// get the project id.
this.project = this.getContext().getProject().ObjectID;
// get the release (if on a page scoped to the release)
var tbName = getReleaseTimeBox(this);
var configs = [];
configs.push({ model : "Release",
fetch : ['Name', 'ObjectID', 'Project', 'ReleaseStartDate', 'ReleaseDate' ],
filters:[]
});
configs.push({ model : "Iteration",
fetch : ['Name', 'ObjectID', 'Project', 'StartDate', 'EndDate' ],
filters:[]
});
async.map( configs, this.wsapiQuery, function(err,results) {
that.releases = results[0];
that.iterations = results[1];
if (showAssignedProgram)
that.createAssignedProgramCombo();
that.createIterationCombo(that.iterations);
});
},
wsapiQuery : function( config , callback ) {
Ext.create('Rally.data.WsapiDataStore', {
autoLoad : true,
limit : "Infinity",
model : config.model,
fetch : config.fetch,
filters : config.filters,
listeners : {
scope : this,
load : function(store, data) {
callback(null,data);
}
}
});
},
createAssignedProgramCombo : function() {
// assigned Program (if set to true)
this.assignedProgramCombo = Ext.create("Rally.ui.combobox.FieldValueComboBox", {
model : "PortfolioItem/Feature",
field : "AssignedProgram",
stateful : true,
stateId : "assignedProgramCombo",
noData: false,
listeners:{
scope: this,
change: function(field,eOpts){
if(value!="" && value!=null)
{
this.afterCollapse(fieldValue,value);
}
}
}
});
this.add(this.assignedProgramCombo);
},
createIterationCombo: function(iterationRecords){
//console.log("Iteration records ",iterationRecords);
iterationRecord = iterationRecords;
var iterations = _.map(iterationRecords, function(rec){return {name: rec.get("Name"), objectid: rec.get("ObjectID"), startDate: new Date(Date.parse(rec.get("StartDate")))};});
console.log('iterations', iterations);
iterations = _.uniq(iterations, function(r){return r.name;});
iterations = _.sortBy(iterations, function(rec){return rec.StartDate;}).reverse();
var iterationStore = Ext.create('Ext.data.Store', {
fields: ['name','objectid'], data : iterations
});
var cb = Ext.create('Ext.form.ComboBox',{
fieldLabel: 'Iterations',
store: iterationStore,
queryMode: 'local',
displayField: 'name',
valueField: 'name',
listeners:{
scope: this,
change: function(field, eOpts){
console.log('field ', field, ' eOpts ',eOpts);
iterationComboValue = eOpts;
iterationComboField = field;
},
collapse: function(field, eOpts){
this.afterCollapse(field,eOpts);
}
}
});
this.add(cb);
},
afterCollapse: function(field,eOpts){
var r = [];
_.each(field.getValue().split(","), function(rn){
var matching_iterations = _.filter(iterationRecord, function(r){return rn == r.get("Name");});
var uniq_iterations = _.uniq(matching_iterations, function(r){return r.get("Name");});
_.each(uniq_iterations,function(iteration){r.push(iteration);});
});
if(r.length>0){
myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
myMask.show();
this.selectedIterations = r;
this.queryFeatures(r);
}
},
queryFeatures: function(iterations){
var that = this;
var filter = null;
if (showAssignedProgram && this.assignedProgramCombo.getValue() != null && this.assignedProgramCombo.getValue() != "") {
console.log("assingedValue",this.assignedProgramCombo.getValue());
filter = Ext.create('Rally.data.QueryFilter', {
property: 'AssignedProgram',
operator: '=',
value: this.assignedProgramCombo.getValue()
});
}
else{
_.each(iterations, function(iteration, i){
var f = Ext.create('Rally.data.QueryFilter', {
property: 'Iteration.Name',
operator: '=',
value: iteration.get("Name")
});
filter = i === 0 ? f : filter.or(f);
});
}
console.log("filter",filter.toString());
var configs = [];
configs.push({
model: 'PortfolioItem/Feature',
fetch: ['ObjectID','FormattedID','UserStories' ],
filters: [filter],
listeners: {
load: function(store, features) {
setOfFeatures = features;
console.log("# features",features.length,features);
that.StartDate = that.startDate(iterations);
that.start = _.min(_.pluck(iterations,function(r) { return r.get("StartDate");}));
isoStart = new lumenize.Time(that.start).getISOStringInTZ("America/Chicago");
console.log("isoStart1",isoStart);
that.end = _.max(_.pluck(iterations,function(r) { return r.get("EndDate");}));
that.iterations = iterations;
console.log('End date ',that.end);
// that.getStorySnapshotsForFeatures( features, iterations);
}
}
});
configs.push({
model: 'HierarchicalRequirement',
limit: 'Infinity',
fetch: ['Name','Iteration','ObjectID','Feature'],
filters: [{
property: 'Iteration.Name',
operator: '=',
value: iterationComboValue
}],
listeners: {
load: function(store, stories){
setOfStories = stories;
console.log('Iteration combo value is ', iterationComboValue);
console.log("# stories ",stories.length,stories);
}
}
});
async.map(configs, this.wsapiQuery, function(err,results){
setOfFeatures = results[0];
console.log("# features",setOfFeatures.length,setOfFeatures);
that.StartDate = that.startDate(iterations);
that.start = _.min(_.pluck(iterations,function(r) { return r.get("StartDate");}));
isoStart = new lumenize.Time(that.start).getISOStringInTZ("America/Chicago");
that.end = _.max(_.pluck(iterations,function(r) { return r.get("EndDate");}));
that.iterations = iterations;
//Here is the problem
setOfStories = results[1];
var stories = _.map(setOfStories, function(story){ return {name: story.get("Name"),fid: story.get("Feature").ObjectID,objectid: story.get("ObjectID")};}); //throws error
console.log('stories ',setOfStories);
var features = _.map(setOfFeatures, function(feature){return {name: feature.get("Name"), fid: feature.get("ObjectID")};});
console.log('features ',setOfFeatures);
var candidateStories = [];
_.each(stories, function(story){_.each(features, function(feature){
if(story.fid == feature){
candidateStories.push(story);
}
});});
console.log('candidate stories ',candidateStories.length,candidateStories);
if(candidateStories!=null){
that.getStorySnapShotsForFeatures(candidateStories);
}
//create snapshot store based on candidateStories.
});
},
getStorySnapShotsForFeatures: function(stories){
var snapshots = [];
var that = this;
async.map(stories, this.readStorySnapshots,function(err,results){
console.log('results ',results);
});
},
readStorySnapshots: function(parent,callback){
console.log('inside story snapshots ');
Ext.create('Rally.data.lookback.SnapshotStore',{
limit: 'Infinity',
autoLoad: true,
listeners:{
scope: this,
load: function(store,data,success){
callback(null,data);
}
},
fetch: ['ObjectID'],
filters:[{
property: 'ObjectID',
operator: 'in',
value: ['ObjectID']
},
{
property: '__At',
operator: '=',
value: 'current'
}]
});
},
startDate: function(iterations){
var start = _.min(_.pluck(iterations, function(r){return r.get("StartDate");}));
return Rally.util.DateTime.toIsoString(start, false);
}
});
In the async.map callback function, when setOfStories are returned, I try to map the name, fid, and objectID to a new array. But for some reason, the fid: story.get("Feature").ObjectID gives an error saying get("") is null. But just before returning the array, when I console log story.get("Feature").ObjectID the correct value is printed, but somehow when I try to return the same value, it generates an error.
The field on HierarchicalRequirement for its PI parent is called PortfolioItem (since the PI types are customizable- feature just happens to be the default name of the lowest level one).
story.get('PortfolioItem').ObjectID

Resources