Angularjs. Access to variable in directive scope - javascript

I have the following directive
var chartDir = function () {
return {
scope: {
stat: '='
},
link: function (scope, element, attr) {
console.log(scope);
return;
}
}
HTML
<svg chart stat="stat"></svg>
So in this case I can see in console that scope has a property called stat (an array, not empty!)
Here is the screenshot...
But if I do console.log( scope.stat ) then i get just undefined. I have the same directive in some other chart and it is working fine. But here I can not understand why I can't get access to scope.stat
UPD:
more html
<div class="chart-container container" ng-show="statLoaded">
<div class="text col">
<div class="item" ng-repeat="item in stat | limitTo: middleIndex">
<div class="color">
<div class="sample" color-sample color="item.color"></div>
</div>
<div class="amount">
{{ item.amount }}
</div>
<div class="text">
{{ item.text }}
</div>
</div>
</div>
<div class="col">
<svg chart stat="stat"></svg>
</div>
...
So I see all items and directive "color-sample" works good. So 'stat' is loaded. So, why 'color-sample' directive works and 'chart' doesen't?

Solution is
scope.$watch(attr.stat, function () {
instead of
scope.$watch(scope.stat, function () {

Related

accessing other scope variables inside custom template

I'm trying to display files inside each folder/directory.
I'm using custom directive to disply each of the directories as follows (this part works).
But it fails to resolve the {{file}} variable inside custom template folderListing.html. Can somebody please correct me where I'm going wrong ?
folderListing.js
app.directive('folderListing', function(){
return {
restrict: 'E',
scope: {
listing:'='
},
templateUrl: 'js/directives/folderListing.html'
};
});
RdaController.js
app.controller('RdaController', ['$scope','RdaService', function($scope,RdaService) {
$scope.folders = ['RDA Server Folder','CTP Outbox', 'CTP Inbox', 'CTP Jobs'];
$scope.sendToCTP = function(file){
return RdaService.SendFileToCTP(file);
};
$scope.listOfFiles = ['learn_javascript.pdf', 'HTML Made Easy.pdf', 'AngularJS for everybody.pdf'];
}]);
index.html
<folder-listing listing="folder" ng-repeat="folder in folders"></folder-listing>
folderListing.html
<div class="row">
<div class="col-md-3" id="{{listing}}">
<table class="table table-striped">
<h3> {{ listing }} </h3>
<div class="row">
<div class="col-md-3" ng-repeat="file in listOfFiles">
{{file}}
</div>
</div>
<td><span class="btn-group" role="group"><button type="button" class="btn btn-default" ng-click="sendToCTP(file)">Execute</button></span></td>
</table>
</div>
</div>
With this:
scope: {
listing:'='
},
You have created an isolate scope passing only listing to the directive. You need to change this to:
scope: {
listing: '=',
listOfFiles: '=',
sendToCTP: '&sendToCtp'
},
To pass the function you'll have to add a send-to-ctp="sendToCTP(file)" attribute on your directive. However, in your template your button with ng-click="sendToCTP(file)" is outside your ng-repeat so file will always be undefined.

How do I access ng-show directive with ng-click? AngularJS

I just started learning so I'm creating a todo app.
I'm trying to show a div when I click on the edit button to edit my task.
this is my html
<div ng-controller='TasksCtrl'>
<div ng-repeat='(key, task) in tasksList' class='task-list'>
<div class='easy'>
<div class='div-list-style'></div>
<div id='task-{{key}}' style='cursor:pointer;z-index:5'
ng-click='editTask()' data-key='{{key}}' class='options
pull-right glyphicon glyphicon-pencil'>
</div>
<div class='task-desc' ng-bind='task.description'></div>
<div ng-hide='taskEdit = true'>FORM</div>
</div>
</div>
</div
This is my controller
todoApp.controller('TasksCtrl',['$scope', 'saveTaskService', function($scope, saveTaskService){
$scope.editTask= function(){
todoApp.directive('taskEdit', function(){
return function(scope, element){
//so I guess over here I need do ngHide = 'false' ? //
alert(element.attr('data-key'));
};
});
};
}]);
Here is a quick solution, you have a scope variable called taskShow that will tell ng-hide when to show it, then on ng-click you will trigger a function that will toggle that value:
<div ng-controller='TasksCtrl'>
<div ng-repeat='(key, task) in tasksList' class='task-list'>
<div class='easy'>
<div class='div-list-style'></div>
<div id='task-{{key}}' style='cursor:pointer;z-index:5'
ng-click='toggleHide(key)' data-key='{{key}}' class='options
pull-right glyphicon glyphicon-pencil'>
</div>
<div class='task-desc' ng-bind='task.description'></div>
<div ng-show='taskShow[key]'>FORM</div>
</div>
</div>
</div>
Controller:
todoApp.controller('TasksCtrl',['$scope', 'saveTaskService', function($scope, saveTaskService){
$scope.taskShow = {};
$scope.toggleHide = function (key) {
$scope.taskShow[key] = !$scope.taskShow[key];
};
}]);
Edit: switched ng-hide to ng-show so the divs are hidden by default and only shown when the other is clicked.

How to use the moment library in angular in a directive that uses compile instead of link

I have a tree of connected documents (parent to child) in my database from a single model called Actions, they're recursively compiled in an angular directive so that they're nested inside their parents.
I have the following code:
angular.module('crmDashboardApp')
.directive('actionDirective', function ($http, $compile, RecursionHelper) {
return {
scope: {
actionId: '=', // html:action-node, js:actionNode
actionList: '='
},
templateUrl: 'app/main/directive/action/action.directive.html',
replace: true,
restrict: 'E',
compile: function (element) {
return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn){
scope.deleteAction = function (_action) {
var id = _action._id;
$http.delete('/api/actions', {
data: {'id':id},
headers: {"Content-Type": "application/json;charset=utf-8"} // we need to do this if we want to send params, otherwise we need to do traditional REST in URL
});
};
// Find for already called action list
scope.findAction = function (_id, _list) {
scope.actionNode = _.findWhere(_list, {_id:_id})
};
scope.findAction(scope.actionId, scope.actionList);
function calculateTimeSince(){
scope.fromNow = moment(scope.actionNode.content).fromNow(true);
}
setInterval(calculateTimeSince, 1000);
scope.fromNow = moment(scope.actionNode.content).fromNow(true);
});
}
};
});
This only compiles once on load and changing anything in the scope after does nothing. I want the setInterval function to change a variable scope.fromNow to be updated every second and update the view (the HTML references this with a simple {{fromNow}})
I believe I'll have to re-compile the directive somehow but doing something like:
$compile(element.contents())(scope)
within the setInterval function doesn't work.
My directive's HTML looks like this:
<div class="action-node">
<header>{{ actionNode.name }}</header>
<div class="row">
<h3 class="col-md-12">{{ actionNode.title }}</h2>
<h5 class="col-md-12">{{ actionNode.description }}</h5>
</div>
<div class="row">
<div class="col-md-3">Time Since: {{fromNow}}</div>
<div class="col-md-3">Content: {{ actionNode.content}}</div>
<div class="col-md-3">Duration Type:{{ actionNode.duration_type }}</div>
<div class="col-md-3">Type: {{ actionNode.type }}</div>
</div>
<div class="row">
<div class="col-md-4">
{{actionNode.children.length > 0 ? actionNode.children : "No children" }}
</div>
<form class="pull-right" ng-submit="deleteAction(actionNode)">
<input class="btn btn-primary" type="submit" value="Delete">
</form>
</div>
<div class="action-wrapper" ng-repeat="child in actionNode.children" ng-if="actionNode.children.length > 0">
<!-- <div class="row" ng-repeat="child in actionNode.children" ng-if="actionNode.children.length > 0" ng-style="{'margin-left': ({{actionNode.nest_level}}+1)*30+'px'}"> -->
<action-directive action-ID="child" action-list="actionList" />
</div>
</div>
You can see that it calls itself again right at the bottom. I am also using RecursionHelper so infinite loop isn't an issue.
Instead of using setInterval, you need to use the Angular wrapper service $interval.
$interval service synchronizes the view and model by internally calling $scope.$apply which executes a digest cycle.

how to pass data from controller to directive using $emit

I had strucked in passing value from controller to directive
I have two arrays in my controller
$scope.displayPeople.push(data.userName);
$scope.displayOrg.push(data.orgname);
i need to pass these data from controller to directive
my directive
<div>
<div class="multitext-wrap blue-border">
<ul inputfocus>
<!--<li class="tag" ng-repeat="list in selecteditemsdisplay track by $index" ng-class="{selected: $index==selectedIndex}" >-->
<!--<span class="tag-label">{{list}}</span><span class="tag-cross pointer" ng-click="Delete($index,selecteditemslist[$index],list,searchid)">x</span>-->
<!--</li>-->
<li class="tag" ng-repeat="list in displayItems track by $index" ng-class="{selected: $index==selectedIndex}" >
<span class="tag-label">{{list}}</span><span class="tag-cross pointer" ng-click="Delete($index,selecteditemslist[$index],list,searchid)">x</span>
</li>
<li class="">
<input type="text" ng-model="searchModel" ng-keydown="selected=false" ng-keyup="searchItem(searchModel,searchobj)"/>
</li>
</ul>
</div>
<div class="typeahead" ng-hide="!searchModel.length || selected">
<div class="typeahead" ng-repeat="item in searchData | filter:searchModel | limitTo:8" ng-click="handleSelection(item,searchobj,$index,searchid)" style="cursor:pointer" ng-class="{active:isCurrent($index)}" ng-mouseenter="setCurrent($index)">
<div class="bs-example">
<div class="list-group list-group-item active">
{{item.displayConfig[0].propertyValue}} {{item.displayConfig[1].propertyValue}}
</div>
</div>
</div>
</div>
</div>
I was using $emit to send
in controller
$rootScope.$emit("displayEvent", {displayItems: $scope.displayPeople});
$rootScope.$emit("displayEvent", {displayItems: $scope.displayOrg});
in directive
$rootScope.$on('displayEvent', function (event, args) {
$scope.displayOrgs = args.displayItems;
console.clear();
console.info($scope.displayOrgs);
});
by doing this i getting duplicates in place of org (both people and org wher coming )
how can i solve this problem please hepl me thanks in advance
By declaring 'scope: false' you´re able to access the controller´s scope in your directive. 'false' means 'do not create an isolated scope, inherit the controllers'.
.directive('myDirective', function() {
return {
scope: false,
link: function($scope){
//Do stuff with $scope.displayOrgs
//Do stuff with $scope.displayPeople
}
};
});
This option will create an isolated scope and inherits the selected variables. This is a cleaner way of doing it.
.directive('myDirective', function() {
return {
scope:{
displayPeople:'=',
displayOrg :'=',
},
link: function($scope){
//Do stuff with $scope.displayOrgs
//Do stuff with $scope.displayPeople
}
};
});
using $emit for communication between controller and directive is not a preferable.
you need to use "=" scope of directive to allow two-way communication between controller and directive like:
directive
angular.module('YourModuleName').directive('yourDirectiveName',function () {
return{
restrict:'E',
scope:{
displayPeople:'=',
displayOrg :'=',
},
link: function postLink(scope, element, attrs) {
}
}
});
template respective to controller
<yourDirectiveName displayPeople="displayPeople" displayOrg ="displayOrg "></yourDirectiveName >

How to dynamically update view in AngularJS

I have 2 div elements as following and I want to show only one of them based on the doStuff() function being called in the controller when an anchor element is clicked.
<div ng-controller='myController'>
<div ng-show="{{states['currentState'] == 'A'}}">
//displaying this div if the currentState is A
<a ng-click="doStuff('B')">Do stuff and show B</a>
</div>
<div ng-show="{{states['currentState'] == 'B'}}">
//displaying this div if the currentState is B
</div>
</div>
Following is the controller code:
myApp.controller('myController', ['$scope', function($scope) {
var states = ['A', 'B'];
$scope.states = states;
$scope.states['currentState'] = $scope.states['currentState'] || 'A';
$scope.doStuff = function(stateToShow) {
//doing stuff
$scope.states['currentState'] = stateToShow;
};
}]);
The code above doesn't work as the state remains 'A' even after clicking the Do stuff and show B anchor element.
Could somebody help me understand why is it not working?
Edit
app.js
//...
.state('home', {
url: '/',
views: {
'': { templateUrl: 'partials/index.html' },
'myView#home': {
templateUrl: 'partials/myView.html',
controller: 'VehicleController'
}
//other named ui views
}
})
//...
index.html
<div class="main">
<div class="container">
<div class="row margin-bottom-40">
<div class="col-md-12 col-sm-12">
<div class="content-page">
<div class="row">
<div ui-view="myView"></div>
<!-- other named ui-views -->
</div>
</div>
</div>
</div>
</div>
</div>
myView.html
<div ng-controller='myController'>
<div ng-show="states['currentState'] == 'A'">
//displaying this div if the currentState is A
<a ng-click="doStuff('B')">Do stuff and show B</a>
</div>
<div ng-show="states['currentState'] == 'B'">
//displaying this div if the currentState is B
</div>
</div>
It is updating the scope. But possibly the issue is with the ng-show you are setting a string by using "{{notation}}" which becomes truthy always (even if it is "true" or "false"), just use the expression directly.
Change
<div ng-show="{{states['currentState'] == 'A'}}">
to
<div ng-show="states.currentState === 'A'">
Demo
From Doc:-
ngShow expression - If the expression is truthy then the element is shown or hidden respectively.
You are very close. The reason it is not working is the attribute "ng-show" does not need the "{{" "}}" notation to work.
I just built your code but took those off, and it is working as you described you wanted it to.
<div ng-show="states['currentState'] == 'A'">
//displaying this div if the currentState is A
<a ng-click="doStuff('B')">Do stuff and show B</a>
</div>
<div ng-show="states['currentState'] == 'B'">
//displaying this div if the currentState is B
</div>

Categories

Resources