AngularJS call directive function from the controller on data load - javascript

I have a directive function scope.automaticallySelectClosingTime(). It takes the value of first selected dropdown value and creates a list of time on the second select drop-down. It triggers on ng-change.
Directive:
.directive('closingTimeSync', function() {
return {
template: `<md-select ng-disabled="time.uiStoreOpen === false" ng-model="openCloseSet[1]">
<md-option ng-repeat="uiTime in closingTimes" ng-value="uiTime.msValue">
{{::uiTime.display}}
</md-option>
</md-select>`,
replace: true,
transclude: true,
link: function(scope) {
scope.automaticallySelectClosingTime = function(msValue) {
scope.closingTimes = scope.uiAvailableTimes;
var dayMS = 86400000 - 1;
var remainingTimings = [];
var index = scope.closingTimes.map(function(obj) {
return obj.msValue;
}).indexOf(msValue);
index = (index === scope.uiAvailableTimes.length - 1) ? 1 : index + 1;
scope.closingTimes = scope.closingTimes.slice(index, scope.uiAvailableTimes.length);
if (msValue !== dayMS) {
remainingTimings = scope.uiAvailableTimes.slice(1, index - 1);
}
scope.closingTimes = scope.closingTimes.concat(remainingTimings);
};
}
};
})
Controller .
.controller('TestCtrl', function($scope) {
init();
// CREATE AVAIABLE TIMES
$scope.uiAvailableTimes = [];
$scope.uiAvailableTimes.push({
'msValue': 0,
'display': '12:00 Morning'
});
for (var msValue = 900000; msValue <= 85500000; msValue += 900000) { // 90.000ms = 15 min, 85.500.000ms = 11:45PM
$scope.uiAvailableTimes.push({
'msValue': msValue,
'display': moment(msValue).utc().format("h:mm A")
})
}
var dayMS = 86400000 - 1;
$scope.uiAvailableTimes.push({
'msValue': dayMS,
'display': '12:00 Midnight'
});
$scope.closingTimes = $scope.uiAvailableTimes;
function init() {
$scope.uiHoursOfOperation = [] // FROM SERVER
}
});
This works fine. But I've data that coming from the server as well. That means my select fields are preselected via ng-model.
How can I call the $scope.automaticallySelectClosingTime() from the controller. Maybe inside init(). So that it also creates the list of time to second drop-down on init() function call or on page load. And I don't have to create $scope.uiAvailableTimes in the controller.
Working Example: PLUNKER

try to add scope parameter to the directive, you can use this:
.directive('closingTimeSync', function() {
return {
template: `<md-select ng-model="ngModel" ng-disabled="time.uiStoreOpen === false" ng-model="openCloseSet[1]">
<md-option ng-repeat="uiTime in closingTimes" ng-value="uiTime.msValue">
{{::uiTime.display}}
</md-option>
</md-select>`,
scope: {
ngModel: '='
},
replace: true,
transclude: true,
link: function(scope) {
scope.automaticallySelectClosingTime = function(msValue) {
scope.closingTimes = scope.uiAvailableTimes;
var dayMS = 86400000 - 1;
var remainingTimings = [];
var index = scope.closingTimes.map(function(obj) {
return obj.msValue;
}).indexOf(msValue);
index = (index === scope.uiAvailableTimes.length - 1) ? 1 : index + 1;
scope.closingTimes = scope.closingTimes.slice(index, scope.uiAvailableTimes.length);
if (msValue !== dayMS) {
remainingTimings = scope.uiAvailableTimes.slice(1, index - 1);
}
scope.closingTimes = scope.closingTimes.concat(remainingTimings);
};
}
};
})
and also you will need to add the ng-model inside the directive:
<closing-time-sync ng-model="paramFromController"></closing-time-sync>
hope that will resolve your issue.

Related

Unable to create dynamic rows along with tree dropdown in angular js

I am working on to add dynamic row with angular js and it's working fine but I need to add tree dropdown as well in the row but onclick of add row button its populate the same data in all rows.
Here is my code and rows snapshot
I am using angularjs but its not working, somewhere I heard that we can use callback to prevent previous click event but I don't know how to use.
please help me
<div class="tree-drop">
<tree-dropdown class="tree-dropdown" call-fuc="test()"
withparam="param(name)" testing="selectedNodeData(msg)"
id="tree-dropdown" data="FarmLands" on-select="selectedLand()"
ng-model="selectedFarmData[0]" selected="selectedFarmData[0]">
</tree-dropdown>
</div>
module.directive('treeDropdown', [ '$compile','$parse' ,function($compile,$parse) {
var template = "<div id='treedata' class='select' ng-click='openTree()' ng-model='selected.name'><p>{{selected.name}}</p></div>";
template += "<div class='list' ng-show='isOpen'></div>";
return {
restrict : 'E',
scope : {
test : "&callFuc",
data : '=',
selected : '=',
testing : "&",
param : "&withparam",
select: '&onSelect'
},
template : template,
controller : function($scope, $element) {
ctrl = $scope;
ctrl.isOpen = false;
ctrl.openTree = function() {
ctrl.isOpen = ctrl.isOpen ? false : true;
}
ctrl.childClick = function(obj) {
setSelected(ctrl, obj);
ctrl.isOpen = false;
ctrl.$apply();
}
},
link : function(scope, element, attrs, ngModel,rootScope) {
var onSelectCallback = $parse(attrs.onSelect);
scope.senddata = function(obj) {
scope.testing({msg:obj});
};
var list = angular.element(element[0].querySelector('.list'));
scope.$watchGroup([ 'data', 'selected' ], function(newValues, oldValues, scope) {
list.html('');
if (!scope.selected) {
setSelected(scope, null);
}
var options = getOptions(scope, scope.data, 0);
list.append($compile(options)(scope));
});
// Close on click outside the dropdown...
angular.element(document).bind('click', function(event) {
event.stopPropagation();
event.preventDefault();
if (element !== event.target && !element[0].contains(event.target)) {
scope.$apply(function() {
scope.isOpen = false;
})
}
scope.select();
});
}
};
function getOptions(scope, data, level) {
var optionUL = angular.element("<ul></ul>");
angular.forEach(data, function(obj) {
var optionLI = angular.element("<li class=''></li>");
var optionA = angular.element("<p class='level-" + level + "'> " + obj.name + "</p>");
if (!angular.isUndefined(obj.children) && obj.children.length) {
var optionC = angular.element("<i class='fa fa-plus level-" + level + "'></i>");
optionLI.append(optionC).append(optionA);
} else {
var optionC = "";
if (obj.type == "field") {
optionC = angular.element("<i class='fa fa-bookmark-o block-icon level-" + level + "'></i>");
}
if (obj.type == "field_block") {
optionC = angular.element("<i class='fa fa-envira zone-icon level-" + level + "'></i>");
}
if (obj.type == "node") {
optionC = angular.element("<i class='fa fa-arrow-circle-right farm-icon level-" + level + "'></i>");
}
optionLI.append(optionC).append(optionA);
}
// Set selected option if selected id or object exist..
if (scope.selected == obj) {
setSelected(scope, obj);
}
optionA.bind("click", function() {
scope.childClick(obj);
})
if (obj.children) {
optionLI.append(getOptions(scope, obj.children, level + 1));
}
optionUL.append(optionLI);
})
return optionUL;
}
function setSelected(scope, obj) {
if (obj) {
scope.selected = obj;
scope.senddata({
"obj" : obj
});
} else {
scope.selected = null;
}
}
} ]);
Any help would appreciated.
We tried it by using dynamic index but when it create duplicate dropdown it get stuck.

How init a controller variable from the view? (Angular JS)

I'm working with a custom label witch controller contains a max value, this is for a horizontal bar that reperesents a percentage.
Since the max value for the horizontal bar it's defined dynamically I need to set it directly on the view, so when the ng-repeat starts, it set the caorrect max value.
Currently i have it like this (the view):
<div class="scroll">
<table class="tbody">
<tr ng-repeat="specialItem in specialItems">
<td class="tcellsmall alg_justify" title="{{specialItem.label | translate}}">
<label class='{{specialItem.class}}' ng-click="openLightbox(specialItem)">{{specialItem.label | translate}}</label>
</td>
<td class="tcell" ng-repeat="area in areas">
<span ng-if="specialItem[area] > 0">{{specialItem[area]}}</span>
<horizontal-bar type="{{item}}" value="{{specialItem[area]}}" ng-init="hValues.maxValue = specialItem.itemTotal " ng-if="specialItem[area] > 0" ng-cloak ng-click="clickMeter(area, specialItem.type, specialItem.custom)" area="{{area}}"/>
</td>
<td class="tcellsmall alg_right" ng-click="clickMeter('Plant', specialItem.type, specialItem.custom)">
<label class="cur_pointer">{{specialItem.itemTotal}}</label>
</td>
</tr>
</table>
</div>
And my controller is:
app.directive('horizontalBar', function () {
return {
restrict: 'E',
scope: {
type: '#',
value: '#',
barColor: '#',
threshold: '#', // only used by the Restriktionsverfolgung page
area: '#'
},
templateUrl: 'views/tools/horizontalBar/horizontalBar.html',
replace: true,
controller: ['$scope', '$rootScope', function ($scope, $rootScope) {
$rootScope.hValues = {};
var min = 0;
$rootScope.hValues.maxValue = 0;
var max = $rootScope.hValues.maxValue;
// var max = 300;
var ranges = new AssemblyLive.Models.RangeCollection();
// Init component
$scope.valuePercent = 0;
if ($scope.value !== undefined) {
$scope.$watchCollection('value', function (newValue) {
$scope.SetValue($scope.value);
});
}
// If data is found in the Config file, load it
if (Config.Areas[$scope.type] !== undefined) {
min = Config.Areas[$scope.type].minimum;
max = Config.Areas[$scope.type].maximum;
if (Config.Areas[$scope.type].ranges !== undefined) {
for (var u in Config.Areas[$scope.type].ranges)
ranges.Add(new AssemblyLive.Models.Range(parseFloat(Config.Areas[$scope.type].ranges[u].from), parseFloat(Config.Areas[$scope.type].ranges[u].to), Config.Areas[$scope.type].ranges[u].style));
}
}
//Functions
$scope.SetColor = function (color) {
$scope.backgroundColor = color;
};
$scope.SetValue = function (value) {
value = Math.round(value);
if (value <= min) value = min;
if (value >= max) value = max;
$scope.valuePercent = value / max * 100;
$scope.color = ranges.getValue($scope.valuePercent);
// Only for the Restriktionsverfolgung page
if ($scope.threshold !== undefined) {
if ($scope.valuePercent >= Number($scope.threshold))
$scope.color = "div_error";
else if ($scope.valuePercent >= Number($scope.threshold - 2))
$scope.color = "div_warning";
else
$scope.color = "div_success";
} else if (Utils.isEmpty($scope.color)) {
if (!Utils.isEmpty($scope.barColor)) {
$scope.color = $scope.barColor;
} else {
$scope.color = "grayfill";
}
} else {
$scope.color = $scope.barColor;
}
};
// Meter was clicked
$scope.clickMeter = function () {
if ($scope.type !== '') {
if (Config.Areas[$scope.type] !== undefined && Config.Areas[$scope.type].onClickURL !== undefined) {
window.location.href = Config.Areas[$scope.type].onClickURL;
}
}
};
}],
controllerAs: 'hmCtrl',
link: function ($scope, element, attrs) {
}
};
});
As you can see in the code, I have my $rootScope variables ready for set the value on the view, but it's not working.
Do you have any idea why?
----update----
Using a plugin for check the scopes in firefox, i can see that the max value is been set correctly.
Thanks
Using a plugin for check the scopes in firefox, i can see that the max value is been set correctly.

issue: dual selectlist moving all items

below code is about dual selectlist. as shown in below image.
Its working as desire. Only problem is, if I click on last subject from Right hand side list box (select Subject list), it is moving all subject to left list box. Technically it should only move the clicked (selected) one subject.
Any suggestion to resolve this?
Code is as following
(function () {
'use strict';
angular
.module("eSchoolSystem")
.directive('multiSelect',['$compile', '$log', multiSelect])
function multiSelect($compile, $log) {
return {
restrict: 'E',
scope: {
name: '#',
selectedModel: '=',
availableModel: '='
},
/*template: '<select id="{{name}}-available" multiple ng-model="availableModel" ng-click="toSelected()" ng-options="a as a.subjectShortName for a in Model.available"></select>\n\n\
<select id="{{name}}-selected" ng-click="toAvailable()" multiple ng-model="selectedModel" ng-options="s as s.subjectShortName for s in Model.selected"></select>',
*/
templateUrl:'app/class/html/dualMultiSelect.html',
compile: function() {
return {
pre: function($scope, $elem, $attr) {
var RefreshSelect = function(original, toFilter) {
var filtered = [];
angular.forEach(original, function(entity) {
var match = false;
for (var i = 0; i < toFilter.length; i++) {
if (toFilter[i]["subjectId"] === entity["subjectId"]) {
match = true;
break;
}
}
if (!match) {
filtered.push(entity);
}
});
return filtered;
};
var RefreshModel = function() {
/*$scope.selectedModel = $scope.Model.selected;*/
$scope.selectedModel = angular.copy($scope.Model.selected);
/*$scope.availableModel = $scope.Model.available;*/
$scope.availableModel = angular.copy($scope.Model.available);
};
var Init = function() {
$scope.Model = {
available: $scope.availableModel,
selected: $scope.selectedModel
};
$scope.selectedModel = [];
$scope.availableModel = [];
$scope.toSelected = function() {
$scope.Model.selected = $scope.Model.selected.concat($scope.availableModel);
$scope.Model.available = RefreshSelect($scope.Model.available, $scope.availableModel);
RefreshModel();
};
$scope.toAvailable = function() {
console.log("in here -x1")
console.log("x3-> " + JSON.stringify($scope.selectedModel))
$scope.Model.available = $scope.Model.available.concat($scope.selectedModel);
$scope.Model.selected = RefreshSelect($scope.Model.selected, $scope.selectedModel);
RefreshModel();
};
};
Init();
}
};
}
};
}
}());
Try to remove RefreshModel function and updating selected values in toSelected and toAvailable:
$scope.toSelected = function() {
$scope.Model.selected = $scope.Model.selected.concat($scope.availableModel);
$scope.Model.available = RefreshSelect($scope.Model.available, $scope.availableModel);
$scope.selectedModel = $scope.availableModel;
$scope.availableModel = [];
};
$scope.toAvailable = function() {
$scope.Model.available = $scope.Model.available.concat($scope.selectedModel);
$scope.Model.selected = RefreshSelect($scope.Model.selected, $scope.selectedModel);
$scope.selectedModel = [];
$scope.availableModel = $scope.selectedModel;
};

How to pass functions from factory into controller angularJS

Right now I have this JS bin: http://jsbin.com/uhabed/64/ in which you can hopefully see the infite scroll loading of more images. These images are added when the page bottom is reached by the scroll bar on line 13 of the javascript:
angular.module('infinitescroll', []).directive('onScrolled', function () {
return function (scope, elm, attr) {
var el = elm[0];
elm.bind('scroll', function () {
if (el.scrollTop + el.offsetHeight >= el.scrollHeight) {
scope.$apply(attr.onScrolled);
}
});
};
}).controller("scrollCtrl", function($scope, getStuff){
$scope.data = getStuff;
$scope.loaddata = function(){
var length = $scope.data.length;
for(var i = length; i < length + 10; i ++){
$scope.data.push(i);
}
};
$scope.loaddata();
}).factory('getStuff',function($http) {
var images_list = ["www.google.com","www.facebook.com","www.supercuber.com","www.snappiesticker.com"];
images_list.addStuff = function(){ $http.get("http://jsbin.com/yehag/2").success(function(data){
var returned_list = JSON.parse(data.javascript);
console.log(returned_list);
for (var i=0;i<8;i++){
images_list.push(returned_list[i].name);
}
});
};
console.log(images_list);
return images_list;
});
I want to replace line 13 with $scope.loaddata = images_list.addStuff(); from the factory below. basically to use the $http function and add the data from that instead. images_list is already being returned properly seeing as the first 4 items in the output are the ones defined in the factory on line 21. However, the optomistic $scope.loaddata = images_list.addStuff(); doesn't seem to be working.
How can I pass this function up into the $scope.loaddata?
images_list is an array. You can't arbitrarily assign a property to it like images_list.addStuff
Create an object and return that object from the factory
factory('getStuff',function($http) {
var images_list = ["www.google.com","www.facebook.com","www.supercuber.com","www.snappiesticker.com"];
var addStuff = function(){....};
return{
images_list: images_list,
addStuff: addStuff
}
});
Then in controller:
$scope.data = getStuff.images_list;
$scope.loaddata = getStuff.addStuff
This is not a clean way of having an array-like object that has additional properties on it, however the above answer that you 'cannot add functions onto an array' is incorrect. While creating array-like objects is kind of messy and should be avoided where possible. If you feel it is absolutely necessary, I would handle it like this (this is similar to how jQuery does it.
function ImagesList($http) {
this.push.apply(this, [
"www.google.com",
"www.facebook.com",
"www.supercuber.com",
"www.snappiesticker.com"
]);
this._$http = $http;
}
ImagesList.prototype = {
push: [].push,
splice: [].splice,
pop: [].pop,
indexOf: [].indexOf,
addStuff: function () {
this._$http.get("http://jsbin.com/yehag/2").then(function(data){
var returnedList = angular.toJson(data.javascript);
for (var i=0; i<8; i++) {
this.push(returnedList[i].name);
}
}.bind(this));
}
};
angular
.module('infinitescroll', [])
.service('imageList', ImagesList);
.directive('onScrolled', function () {
return {
scope: {
onScrolled: '&'
},
link: function (scope, elm, attr) {
var el = elm[0];
// Original implementation will end up causing memory leak because
// the handler is never destroyed. Use as follows
elm.on('scroll.iScroll', function () {
if (el.scrollTop + el.offsetHeight >= el.scrollHeight) {
scope.$apply(attr.onScrolled);
}
}).on('$destroy', function () {
elm.off('.iScroll');
});
}
};
}).controller("scrollCtrl", function($scope, imageList){
$scope.data = imageList;
$scope.loaddata = function(){
var length = $scope.data.length;
for(var i = length; i < length + 10; i++){
$scope.data.push(i);
}
};
$scope.loaddata();
})

Pass scope and parameters to directive - AngularJS

I'm trying to set a class in a ng-repeat with a directive.
I need to pass a parameter to this directive: wineQty
if I use scope: { wineQty: '=' } this works however $scope.bullet1Color is undefined in my html and thus doesn't affect the class that I want.
If I use scope: '#' I get the correct class however I can't specify wineQty
Is there a way to combine theses two syntaxes? so that I can access the scope and pass a paramater?
I've tried { wineQty: '#' } but with no luck sadly.
Here's my directive
angular.module('myApp').directive('wineQtyBullets', function () {
return {
restrict: 'A',
scope: { wineQty: '=', totalBullets: '=', numRedBullets: '=', numOrangeBullets: '#', bullet1Color: '#' },
link: function (scope, element, attrs) {
// defaults
var defaultNumRedBullets = 1;
var defaultNumOrangeBullets = 2;
var defaultTotalBullets = 12;
var defaultWineQty = 0;
// set values from attributes
var numRedBullets = scope.numRedBullets ? scope.numRedBullets : defaultNumRedBullets;
var numOrangeBullets = scope.numOrangeBullets ? scope.numOrangeBullets : defaultNumOrangeBullets;
var totalBullets = scope.totalBullets ? scope.totalBullets : defaultTotalBullets;
var wineQty = scope.wineQty ? scope.wineQty : defaultWineQty;
for (var currentBullet = 1; currentBullet <= totalBullets; currentBullet++) {
var bulletColorClass = 'bg-grey';
if (currentBullet <= wineQty) {
if (currentBullet <= numRedBullets) {
bulletColorClass = 'bg-red';
}
else if (currentBullet <= (numOrangeBullets + numRedBullets)) {
bulletColorClass = 'bg-orange';
}
else {
bulletColorClass = 'bg-green';
}
}
scope["bullet" + currentBullet + "Color"] = bulletColorClass;
}
console.log(scope.bullet1Color);
}
};
}
);
Here's my html
<div class="bullets pull-left ml15 mt6" wine-qty="wine.owned_qty" wine-qty-bullets>
<ul>
<li class="bullet"
ng-class="bullet1Color"></li>
</ul>
</div>
I managed to solve the problem, by changing scope to true and accessing the parameters through attrs
If this can help anyone here's the directive:
angular.module('myApp').directive('wineQtyBullets', function () {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
var options = { };
angular.forEach([
'numRedBullets',
'numOrangeBullets',
'totalBullets',
'wineQty'
], function (key) {
if (angular.isDefined(attrs[key]))
options[key] = attrs[key];
});
// defaults
var defaultNumRedBullets = 1;
var defaultNumOrangeBullets = 1;
var defaultTotalBullets = 12;
var defaultWineQty = 0;
// set values from attributes
var numRedBullets = parseInt(options.numRedBullets) ? parseInt(options.numRedBullets) : defaultNumRedBullets;
var numOrangeBullets = parseInt(options.numOrangeBullets) ? parseInt(options.numOrangeBullets) : defaultNumOrangeBullets;
var totalBullets = parseInt(options.totalBullets) ? parseInt(options.totalBullets) : defaultTotalBullets;
var wineQty = parseInt(options.wineQty) ? parseInt(options.wineQty) : defaultWineQty;
for (var currentBullet = 1; currentBullet <= totalBullets; currentBullet++) {
var bulletColorClass = 'bg-grey';
if (currentBullet <= wineQty) {
if (wineQty <= numRedBullets) {
bulletColorClass = 'bg-red';
}
else if (wineQty <= (numOrangeBullets + numRedBullets)) {
bulletColorClass = 'bg-orange';
}
else {
bulletColorClass = 'bg-green';
}
}
scope["bullet" + currentBullet + "Color"] = bulletColorClass;
}
}
};
});
Using "=" means 2-way data binding, and it's definitely fine.
The problem might be that your link function are executed only once at the very beginning, when it's possible that the values of your attributes are not yet assigned (may be caused by some AJAX calls).
I would suggest that you wrap all your link function into a scope.$watch function. Like:
link: function (scope, element, attrs) {
scope.$watch(function() {
return {
wineQty: scope.wineQty,
totalBullets: scope.totalBullets,
numRedBullets: scope.numRedBullets,
numOrangeBullets: scope.numOrangeBullets,
bullet1Color: scope.bullet1Color
}
}, function() {
// Your original code here.
})
}
Then your final result will automatically update if your directive got new attribute values.

Categories

Resources