AngularJs - directive not updating controller variable - javascript

Before anything I just have to say that I did a full google/stackoverflow search regarding this issue and I couldn't find anything that would help.
So, with that behind us, here's my problem:
I have this directive which should return the data selected in a simple input type file.
In my console.log the data comes out just fine but when I watch for the value change in my controller it simply never does.
Here's my directive:
app.directive('esFileRead', [
function () {
return {
restrict: 'A',
$scope: {
esFileRead: '='
},
link: function ($scope, $elem) {
$elem.bind('change', function (changeEvent) {
if (FileReader) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
$scope.$apply(function () {
$scope.esFileRead= loadEvent.target.result;
console.log($scope.esFileRead);
});
};
reader.readAsDataURL(changeEvent.target.files[0]);
}
else {
// FileReader not supported
}
});
}
}
}
]);
My controller:
app.controller('MainController', [
'$rootScope',
'$scope',
'DataManagementService',
function ($rootScope, $scope, DataManagementService) {
$scope.importFile = "";
$scope.$watch('importFile', function (newValue, oldValue) {
console.log(newValue);
if(newValue && newValue !== oldValue) {
DataManagementService.importData(newValue);
}
});
}
]);
My view:
<input id="btnImport" type="file" es-file-read="importFile"/>

You seems to be made a typo in Directive Definition Object, where $scope should be scope.
I'd like to suggest few things to improve your current approach, rather than using watcher in parent controller method. You should pass a callback to directive and call it as soon as you're done with reader object loaded. Which turns out that your isolated scope will appear like below
restrict: 'A',
scope: {
callback: '&'
},
and then put new method inside controller where we will pass a file data to that method.
$scope.importData = function(data) {
DataManagementService.importData(newValue);
});
And then from element pass callback method correctly on directive
<input id="btnImport" type="file" es-file-read callback="importData(data)"/>
Call method from directive code correctly.
reader.onload = function (loadEvent) {
$scope.$apply(function () {
$scope.esFileRead= loadEvent.target.result;
$scope.callback({ item: $scope.esFileRead });
});
};

Related

AngularJS rootScope.$emit from within Directive

I have a directive for users to like (or "fave") posts in my application. Throughout my controllers I use $rootScope.$emit('name-of-function', some-id) to update user data when they like a new post, as this is reflected throughout my application. But I can't seem to use $rootScope.$emit in the directive. I receive an error
$rootScope.$emit is not a function
Presumably the $rootScope.$on event which corresponds with this command has not been called yet, so this function does not yet exist? What can be done about this? Is there a better way to arrange this?
var module = angular.module('directives.module');
module.directive('postFave', function (contentService, $rootScope) {
return {
restrict: 'E',
templateUrl: 'directives/post-fave.html',
scope: {
contentId: '#',
slug: '#'
},
link: function ($scope, $rootScope, element) {
$scope.contentFavToggle = function (ev) {
contentId = $scope.contentId;
contentService.contentFavToggle(contentId, ev).then(function (response) {
$rootScope.$emit('dataUpdated', $scope.slug);
if (response) {
$scope.favourite[contentId] = response;
} else {
$scope.favourite[contentId] = null;
}
});
};
console.log("track fave directive called");
}
};
});
from controller:
var dataUpdatedListener = $rootScope.$on('dataUpdated', function (event, slug) {
dataService.clearData(slug);
dataControllerInit();
});
How can I access this rootscope function from within the directive? Thanks.
FYI - "link" has been used in the directive because this is related to an instance of an HTML element which will be used a number of times on the page
link has the following signature, there is no need to add $rootScope injection into link function:
function link(scope, element, attrs, controller, transcludeFn) { ... }
Remove it from link and it will work.

Trouble with $rootScope.$broadcast from angular directive 'link'

I am trying to broadcast some data from inside my angular directive. The directive. I am getting cannot read property $broadcast of undefined errors. Is there some sort of problem with the way I'm trying to broadcast the data to the controller from my directive?
angular.module('myapp').directive("fileread", function (ServerRequest, $sessionStorage, $rootScope) {
return {
scope: {
myData: '=',
opts: '='
},
link: function ($scope, $elm, $attrs, $rootScope) {
console.log($scope)
console.log('fileread before',$scope.myData,$scope.opts)
$elm.on('change', function (changeEvent) {
console.log('directive');
var reader = new FileReader();
console.log(reader)
reader.onload =function (evt) {
$scope.$apply(function () {
var data = evt.target.result;
console.log('fileread scope apply')
var workbook = XLSX.read(data, {type: 'binary'});
------------PROBLEM IS BELOW HERE-------------
$scope.broadcastfilename = data;
$rootScope.$broadcast('filenameforupload', '$scope.broadcastfilename');
console.log('passed broadcast');
-------------AND ABOVE HERE--------------------
var headerNames = XLSX.utils.sheet_to_json(
workbook.Sheets[workbook.SheetNames[0]],
{ header: 1 }
)[0];
var importedData = XLSX.utils.sheet_to_json( workbook.Sheets[workbook.SheetNames[0]]);
console.log(headerNames,workbook);
console.log(importedData)
$sessionStorage.headerNames = headerNames;
$sessionStorage.importedData = importedData;
// addRows(data);
$elm.val(null);
//this is where we add the new data to the existing data
var query = {
patients: importedData,
coverKey: $sessionStorage.practiceLogged.coverKey
}
});
};
reader.readAsBinaryString(changeEvent.target.files[0]);
});
}
}
});
In my controller I have:
$scope.$on('filenameforupload', function(event, args) {
console.log(args, "<------------");
});
Remove $rootScope parameter from your link function, which is killing an existence of injected $rootScope dependency inside directive factory function.

Binding a Directive Controller's method to its parent $scope

I will explain what exactly I'm trying to do before explaining the issue. I have a Directive which holds a form, and I need to access that form from the parent element (where the Directive is used) when clicking on a submit button to check fi the form is valid.
To do this, I am trying to use $scope.$parent[$attrs.directiveName] = this; and then binding some methods to the the Directive such as this.isValid which will be exposed and executable in the parent.
This works fine when running locally, but when minifying and building my code (Yeoman angular-fullstack) I will get an error for aProvider being unknown which I traced back to a $scopeProvider error in the Controller.
I've had similar issues in the past, and my first thought was that I need to specifically say $inject for $scope so that the name isn't lost. But alas.....no luck.
Is something glaringly obvious that I am doing wrong?
Any help appreciated.
(function() {
'use strict';
angular
.module('myApp')
.directive('formDirective', formDirective);
function formDirective() {
var directive = {
templateUrl: 'path/to/template.html',
restrict: 'EA',
scope: {
user: '='
},
controller: controller
};
return directive;
controller.$inject = ['$scope', '$attrs', 'myService'];
function controller($scope, $attrs, myService) {
$scope.myService = myService;
// Exposes the Directive Controller on the parent Scope with name Directive's name
$scope.$parent[$attrs.directiveName] = this;
this.isValid = function() {
return $scope.myForm.$valid;
};
this.setDirty = function() {
Object.keys($scope.myForm).forEach(function(key) {
if (!key.match(/\$/)) {
$scope.myForm[key].$setDirty();
$scope.myForm[key].$setTouched();
}
});
$scope.myForm.$setDirty();
};
}
}
})();
Change the directive to a component and implement a clear interface.
Parent Container (parent.html):
<form-component some-input="importantInfo" on-update="someFunction(data)">
</form-component>
Parent controller (parent.js):
//...
$scope.importantInfo = {data: 'data...'};
$scope.someFunction = function (data) {
//do stuff with the data
}
//..
form-component.js:
angular.module('app')
.component('formComponent', {
template:'<template-etc>',
controller: Controller,
controllerAs: 'ctrl',
bindings: {
onUpdate: '&',
someInput: '<'
}
});
function Controller() {
var ctrl = this;
ctrl.someFormThing = function (value) {
ctrl.onUpdate({data: value})
}
}
So if an event in your form triggers the function ctrl.someFormThing(data). This can be passed up to the parent by calling ctrl.onUpdate().

how to send an object to angular directive on a check-box

i am trying to consume iCheck check box using a directive. my directive is setup like this.
.module('app').directive('bootstrapCheck', ['$timeout', '$parse', function ($timeout, $parse) {
return {
compile: function (element, $attrs) {
var icheckOptions = {
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal'
};
var modelAccessor = $parse($attrs['ngModel']);
return function ($scope, element, $attrs, controller) {
var modelChanged = function (event) {
$scope.$apply(function () {
modelAccessor.assign($scope, event.target.checked);
});
};
$scope.$watch(modelAccessor, function (val) {
var action = val ? 'check' : 'uncheck';
element.iCheck(icheckOptions, action).on('ifChanged', modelChanged);
});
};
}
};
}]);
my check-boxes are in ng-repeat. i want to send the current object to function to process it further. my html is setup like this.
<input type="checkbox" ng-model="item.isSelected" ng-change="onChangeEvent(item)" bootstrap-check>
modelChanged gets triggered each time i change any check-box. but i am trying to access item inside modelChanged function to process it further. please guide.
You can pass the item to directive scope and access it inside the link function. Along with item you can also pass the onChangeEvent handler to the directive. Try this.
JS
angular.module('app').directive('bootstrapCheck', ['$timeout', '$parse', function ($timeout, $parse) {
return {
scope: {
item: '=',
onChangeEvent: '&'
},
compile: function (element, $attrs) {
var icheckOptions = {
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal'
};
var modelAccessor = $parse($attrs['ngModel']);
return function ($scope, element, $attrs, controller) {
var modelChanged = function (event) {
//Here $scope.item will give you the item
//This will trigger the parent controller's onChangeEvent set in the directive markup
$scope.onChangeEvent({ item: $scope.item });
$scope.$apply(function () {
modelAccessor.assign($scope, event.target.checked);
});
};
$scope.$watch(modelAccessor, function (val) {
var action = val ? 'check' : 'uncheck';
element.iCheck(icheckOptions, action).on('ifChanged', modelChanged);
});
};
}
};
}]);
HTML
<input type="checkbox"
ng-model="item.isSelected"
item="item"
on-change-event="onChangeEvent(item)" bootstrap-check>

angular.js directive two-way-binding scope updating

I wanted to use a directive to have some click-to-edit functionality in my front end.
This is the directive I am using for that: http://icelab.com.au/articles/levelling-up-with-angularjs-building-a-reusable-click-to-edit-directive/
'use strict';
angular.module('jayMapApp')
.directive('clickToEdit', function () {
return {
templateUrl: 'directives/clickToEdit/clickToEdit.html',
restrict: 'A',
replace: true,
scope: {
value: '=clickToEdit',
method: '&onSave'
},
controller: function($scope, $attrs) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
$scope.method();
};
}
};
});
I added a second attribute to the directive to call a method after when the user changed the value and then update the database etc. The method (´$onSave´ here) is called fine, but it seems the parent scope is not yet updated when I call the method at the end of the directive.
Is there a way to call the method but have the parent scope updated for sure?
Thanks in advance,
Michael
I believe you are supposed to create the functions to attach inside the linking function:
Take a look at this code:
http://plnkr.co/edit/ZTx0xrOoQF3i93buJ279?p=preview
app.directive('clickToEdit', function () {
return {
templateUrl: 'clickToEdit.html',
restrict: 'A',
replace: true,
scope: {
value: '=clickToEdit',
method: '&onSave'
},
link: function(scope, element, attrs){
scope.save = function(){
console.log('save in link fired');
}
},
controller: function($scope, $attrs) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
console.log('save in controller fired');
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
$scope.method();
};
}
};
});
I haven't declared the functions inside the controller before, but I don't see why it wouldn't work.
Though this question/answer explain it Link vs compile vs controller
From my understanding:
The controller is used to share data between directive instances, not to "link" functions which would be run as callbacks.
The method is being called but angular doesn't realise it needs to run the digest cycle to update the controller scope. Luckily you can still trigger the digest from inside your isolate scope just wrap the call to the method:
$scope.$apply($scope.method());

Categories

Resources