AngularJS, How to call a function in component from Directive? - angularjs

Thanks in advance, Basically in app component a timer which should be start when I call from a directive, Please let me know how I can call? I tried some method but didn't helpful. I'm new in angular js Please guide me.Thanks Here is the Js File code.
var app = angular.module('quizApp', []);
app.directive('quiz', function (quizFactory) {
return {
restrict: 'AE',
scope: {},
templateUrl: '/Home/Dashboard',
link: function (scope, elem, attrs) {
//Calling function cmpTimer(); in Component
};
}
}
});
app.component('cmpTimer', {
bindings: {
cmpFrom: '<',
cmpUnit: '#'
},
template: '<div class="timer">{{$ctrl.timeRemaining}}</div>',
controller: function ($interval) {
var vm = this, ONE_SECOND = 1000, timerInterval;
function startTimer() {
var END_TIME = vm.getEndTime();
function update() {
vm.timeRemaining = moment((moment(END_TIME) - moment())).format("HH:mm:ss");
}
timerInterval = $interval(function () {
if (moment() > moment(END_TIME)) return vm.stopTimer();
update();
}, ONE_SECOND);
update();
}
vm.getEndTime = function () {
return moment().add(vm.cmpFrom, vm.cmpUnit);
}
vm.stopTimer = function () {
alert('Time\'s up!');
vm.$onDestroy();
}
vm.$onDestroy = function () {
$interval.cancel(timerInterval);
}
vm.$onInit = startTimer;
}
});

Related

Pass Value from Modal Directive to Controller

I have a directive for signaturePad and i want to send the Signature Data to the controller once the signature is accepted by the user.
Here is the directive
.directive('signature', function ($ionicModal) {
var canvas = null,
ratio = 1.0;
return {
scope: {
signature: '=ngModel'
},
link: function ($scope, $element, $attrs, $controller) {
$scope.signature = null;
$scope.signaturePadModel = {};
$ionicModal.fromTemplateUrl('modal.html', {
animation: 'slide-in-up',
scope: $scope,
}).then(function(modal) {
$scope.signatureModal = modal;
});
$scope.$on('$destroy', function () {
$scope.signatureModal.remove();
});
$scope.openSignatureModal = function () {
$scope.signatureModal.show();
canvas = angular.element($scope.signatureModal.modalEl).find('canvas')[0];
$scope.signaturePad = new SignaturePad(canvas, {
backgroundColor: '#FFF',
minWidth: 1,
maxWidth: 1.5,
dotSize: 3,
penColor: 'rgb(66, 133, 244)',
onEnd: function () {
$scope.signature = $scope.signaturePad.toDataURL();
}
});
if ($scope.signature) {
$scope.signaturePad.fromDataURL($scope.signature);
}
$scope.resizeCanvas();
};
$scope.resizeCanvas = function () {
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
canvas.getContext('2d').scale(ratio, ratio);
};
$scope.clear = function () {
$scope.signaturePadModel.signatureConfirm = false;
$scope.signaturePad.clear();
$scope.signature = null;
};
$scope.save = function () {
$scope.signaturePadModel = {};
$scope.signatureModal.hide();
};
},
require: 'ngModel',
replace: true,
restrict: 'A',
templateUrl: 'signatureButton.html'
};
})
I am getting the signature inside the directive but how to pass that to the controller.
The value is coming inside $scope.signature.
Here is the codepen link
https://codepen.io/thesourav/pen/QWLVyjE
Ideal way can be to store that information in service and reuse it. But in this case, I have triggered the emit event and passing the signature once save function is called and catching the same in the controller and accessing the signature.
Below snippet in directive
$scope.save = function () {
$scope.signaturePadModel = {};
$scope.signatureModal.hide();
$scope.$emit('c', $scope.signature);
};
And catching the event in controller as below :
$scope.$on('c', function (event, signature) {
alert(signature);
})
I have forked your codepen example(CodePen) and done the changes. Please test.

Error: [ng:areq] Argument 'AddInstallationCtrl' is not a function, got Object

i cant find my fail :(
can help me?
in my project i have:
controller principal
define([
"./directives/subscribe",
"./controllers/subscribe",
"./controllers/script",
"angular",
"../models/app.models",
"../login/app.login",
], function (directives, controllers) {
var module = angular.module("app.matrix", ["app.models", "app.login"]).config(['$routeProvider', function ($routeProvder) {
$routeProvder
.when('/matrix', {
templateUrl: 'modules/matrix/templates/main.html',
controller: 'MatrixCtrl'
})
.when('/modals', {
templateUrl: 'modules/matrix/templates/index.html',
controller: 'AddInstallationCtrl'
});
}]);
module = directives.subscribe(module);
return controllers.subscribe(module);
(function () {
"use strict";
var app = angular.module('myApp', [
'ap.lateralSlideMenu',
]);
// service
app.service('number', function () {
return {
isPositive: function (operationPrice) {
return String(operationPrice).indexOf("-") == -1;
}
};
});
})
();
});
controller Secondary
define([
"./matrix",
"./sidebar",
"./script"
], function (matrix, sidebar, script) {
return {
subscribe: function (app) {
app.controller('MatrixCtrl', matrix).controller('SidebarCtrl', sidebar);
app.controller('AddInstallationCtrl', script);
app.directive('sidebarDirective', function () {
return {
link: function (scope, element, attr) {
scope.$watch(attr.sidebarDirective, function (newVal) {
if (newVal) {
element.addClass('show');
return;
}
element.removeClass('show');
});
}
};
});
return app;
}
};
});
Controller Finaly
define(["angular"], function () {
// Code goes here
var app = angular.module('myApp', ['ngAnimate', 'ngSanitize']);
app.config([
'$compileProvider',
function ($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
// Angular before v1.2 uses $compileProvider.urlSanitizationWhitelist(...)
}
]);
app.directive('modalDialog', function ($window, $templateCache, $compile, $http) {
return {
restrict: 'EA',
scope: {
show: '=',
modalUser: '=',
saveUser: '&',
templateUser: '#'
},
replace: true, // Replace with the template below
//transclude: true, // we want to insert custom content inside the directive
link: function (scope, element, attrs) {
$http.get(scope.templateUser, { cache: $templateCache }).success(function (tplContent) {
element.replaceWith($compile(tplContent)(scope));
});
scope.dialogStyle = {};
if (attrs.width) {
scope.dialogStyle.width = attrs.width + '%';
scope.dialogStyle.left = ((100 - attrs.width) / 2) + '%';
}
if (attrs.height) {
scope.dialogStyle.height = attrs.height + '%';
scope.dialogStyle.top = ((100 - attrs.height) / 2) + '%';
}
scope.hideModal = function () {
scope.show = false;
};
scope.clone = function (obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
var temp = obj.constructor(); // give temp the original obj's constructor
for (var key in obj) {
temp[key] = scope.clone(obj[key]);
}
return temp;
};
var tempUser = scope.clone(scope.modalUser);
scope.save = function () {
scope.saveUser(scope.modalUser);
scope.show = false;
};
scope.cancel = function () {
scope.modalUser = scope.clone(tempUser);
scope.show = false;
};
}
//template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
//templateUrl: 'my-customer.html'
//templateUrl: scope.templateUser
};
});
app.controller('AddInstallationCtrl', function ($scope, $window) {
$scope.modalShown = false;
$scope.modalShown2 = false;
$scope.user = { name: "Mara", surname: "Sanchez", shortKey: "1111" };
$scope.userMod = {};
$scope.toggleModal = function () {
$scope.modalShown = !$scope.modalShown;
};
$scope.toggleModal2 = function () {
$scope.modalShown2 = !$scope.modalShown2;
};
$scope.saveUser = function (usr) {
$scope.userMod = usr;
$window.alert('Desde metodo SALVAR del controller fuera de la ventana: ' + $scope.userMod.shortKey);
}
});
});
With the first controller I declare that the url: modules / matrix / templates / index.html
It is managed by the AddInstallationCtrl driver
With the second controller I declare that
The application has the AddInstallationCtrl driver
And that the controller is in the variable script
App.controller ('AddInstallationCtrl', script);
In the final controller I declare all the functions that I want to execute the letter
Even there is right, right?
Because when you entered modules / matrix / templates / index.html
Does it tell me that AddInstallationCtrl is not a controller?

How to redisplay directive after Javascript function executes

I have an AngularJS Directive defined in a Javascript file that looks like this:
(function () {
'use strict';
angular
.module('ooApp.controllers')
.directive('fileUploader', fileUploader);
fileUploader.$inject = ['appInfo', 'fileManager'];
function fileUploader(appInfo, fileManager) {
var directive = {
link: link,
restrict: 'E',
templateUrl: 'views/directive/UploadFile.html',
scope: true
};
return directive;
function link(scope, element, attrs) {
scope.hasFiles = false;
scope.files = [];
scope.upload = fileManager.upload;
scope.appStatus = appInfo.status;
scope.fileManagerStatus = fileManager.status;
}
}
})();
and in the template URL of the directive there is a button that calls a Javascript function which looks like this:
function upload(files) {
var formData = new FormData();
angular.forEach(files, function (file) {
formData.append(file.name, file);
});
return fileManagerClient.save(formData)
.$promise
.then(function (result) {
if (result && result.files) {
result.files.forEach(function (file) {
if (!fileExists(file.name)) {
service.files.push(file);
}
});
}
appInfo.setInfo({ message: "files uploaded successfully" });
return result.$promise;
},
function (result) {
appInfo.setInfo({ message: "something went wrong: " +
result.data.message });
return $q.reject(result);
})
['finally'](
function () {
appInfo.setInfo({ busy: false });
service.status.uploading = false;
});
}
Once I select files for upload and click the upload button I need to reload the directive or somehow get it back to it's initial state so I can upload additional files. I'm relatively new to AngularJS and I'm not quite sure how to do this. Any help is much appreciated.
Thanks,
Pete
You just need to create a reset method. Also, you may want to call the parent controller function.
Using answer from this
ngFileSelect.directive.js
...
.directive("onFileChange",function(){
return {
restrict: 'A',
link: function($scope,el){
var onChangeHandler = scope.$eval(attrs.onFileChange);
el.bind('change', onChangeHandler);
}
}
...
fileUploader.directive.js
(function () {
'use strict';
angular
.module('ooApp.controllers')
.directive('fileUploader', fileUploader);
fileUploader.$inject = ['appInfo', 'fileManager'];
function fileUploader(appInfo, fileManager) {
return {
link: link,
restrict: 'E',
templateUrl: 'views/directive/UploadFile.html',
scope:{
onSubmitCallback: '&',
onFileChange: '&'
}
};
function link(scope, element, attrs) {
scope.reset = reset;
scope.fileChange = fileChange;
reset();
function reset() {
scope.hasFiles = false;
scope.files = [];
scope.upload = fileManager.upload;
scope.appStatus = appInfo.status;
scope.fileManagerStatus = fileManager.status;
if(typeof scope.onSubmitCallback === 'function') {
scope.onSubmitCallback();
}
}
function fileChange(file) {
if(typeof scope.onFileChange === 'function'){
scope.onFileChange(file);
}
}
}
}
})();
UploadFile.html
<form>
<div>
...
</div>
<input type="submit" ng-click="reset()" file-on-change="fileChange($files)" />Upload
</form>
parent.html
<file-uploader on-submit-callback="onUpload" on-file-change="onFileChange" ng-controller="UploadCtrl" />
upload.controller.js
...
$scope.onUpload = function() {
console.log('onUpload clicked %o', arguments);
};
$scope.onFileChange = function(e) {
var imageFile = (e.srcElement || e.target).files[0];
}
...

angular directive encapsulating a delay for ng-change

I have a search input field with a requery function bound to the ng-change.
<input ng-model="search" ng-change="updateSearch()">
However this fires too quickly on every character. So I end up doing something like this alot:
$scope.updateSearch = function(){
$timeout.cancel(searchDelay);
searchDelay = $timeout(function(){
$scope.requery($scope.search);
},300);
}
So that the request is only made 300ms after the user has stopped typing. Is there any solution to wrap this in a directive?
As of angular 1.3 this is way easier to accomplish, using ngModelOptions:
<input ng-model="search" ng-change="updateSearch()" ng-model-options="{debounce:3000}">
Syntax: {debounce: Miliseconds}
To solve this problem, I created a directive called ngDelay.
ngDelay augments the behavior of ngChange to support the desired delayed behavior, which provides updates whenever the user is inactive, rather than on every keystroke. The trick was to use a child scope, and replace the value of ngChange to a function call that includes the timeout logic and executes the original expression on the parent scope. The second trick was to move any ngModel bindings to the parent scope, if present. These changes are all performed in the compile phase of the ngDelay directive.
Here's a fiddle which contains an example using ngDelay:
http://jsfiddle.net/ZfrTX/7/ (Written and edited by me, with help from mainguy and Ryan Q)
You can find this code on GitHub thanks to brentvatne. Thanks Brent!
For quick reference, here's the JavaScript for the ngDelay directive:
app.directive('ngDelay', ['$timeout', function ($timeout) {
return {
restrict: 'A',
scope: true,
compile: function (element, attributes) {
var expression = attributes['ngChange'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) attributes['ngModel'] = '$parent.' + ngModel;
attributes['ngChange'] = '$$delay.execute()';
return {
post: function (scope, element, attributes) {
scope.$$delay = {
expression: expression,
delay: scope.$eval(attributes['ngDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
$timeout(function () {
if (Date.now() - state.then >= state.delay)
scope.$parent.$eval(expression);
}, state.delay);
}
};
}
}
}
};
}]);
And if there are any TypeScript wonks, here's the TypeScript using the angular definitions from DefinitelyTyped:
components.directive('ngDelay', ['$timeout', ($timeout: ng.ITimeoutService) => {
var directive: ng.IDirective = {
restrict: 'A',
scope: true,
compile: (element: ng.IAugmentedJQuery, attributes: ng.IAttributes) => {
var expression = attributes['ngChange'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) attributes['ngModel'] = '$parent.' + ngModel;
attributes['ngChange'] = '$$delay.execute()';
return {
post: (scope: IDelayScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes) => {
scope.$$delay = {
expression: <string>expression,
delay: <number>scope.$eval(attributes['ngDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
$timeout(function () {
if (Date.now() - state.then >= state.delay)
scope.$parent.$eval(expression);
}, state.delay);
}
};
}
}
}
};
return directive;
}]);
interface IDelayScope extends ng.IScope {
$$delay: IDelayState;
}
interface IDelayState {
delay: number;
expression: string;
execute(): void;
then?: number;
action?: ng.IPromise<any>;
}
This works perfectly for me: JSFiddle
var app = angular.module('app', []);
app.directive('delaySearch', function ($timeout) {
return {
restrict: 'EA',
template: ' <input ng-model="search" ng-change="modelChanged()">',
link: function ($scope, element, attrs) {
$scope.modelChanged = function () {
$timeout(function () {
if ($scope.lastSearch != $scope.search) {
if ($scope.delayedMethod) {
$scope.lastSearch = $scope.search;
$scope.delayedMethod({ search: $scope.search });
}
}
}, 300);
}
},
scope: {
delayedMethod:'&'
}
}
});
Using the directive
In your controller:
app.controller('ctrl', function ($scope,$timeout) {
$scope.requery = function (search) {
console.log(search);
}
});
In your view:
<div ng-app="app">
<div ng-controller="ctrl">
<delay-search delayed-method="requery(search)"></delay-search>
</div>
</div>
I know i'm late to the game but,hopefully this will help anyone still using 1.2.
Pre ng-model-options i found this worked for me, as ngchange will not fire when the value is invalid.
this is a slight variation on #doug's answer as it uses ngKeypress which doesn't care what state the model is in.
function delayChangeDirective($timeout) {
var directive = {
restrict: 'A',
priority: 10,
controller: delayChangeController,
controllerAs: "$ctrl",
scope: true,
compile: function compileHandler(element, attributes) {
var expression = attributes['ngKeypress'];
if (!expression)
return;
var ngModel = attributes['ngModel'];
if (ngModel) {
attributes['ngModel'] = '$parent.' + ngModel;
}
attributes['ngKeypress'] = '$$delay.execute()';
return {
post: postHandler,
};
function postHandler(scope, element, attributes) {
scope.$$delay = {
expression: expression,
delay: scope.$eval(attributes['ngKeypressDelay']),
execute: function () {
var state = scope.$$delay;
state.then = Date.now();
if (scope.promise) {
$timeout.cancel(scope.promise);
}
scope.promise = $timeout(function() {
delayedActionHandler(scope, state, expression);
scope.promise = null;
}, state.delay);
}
};
}
}
};
function delayedActionHandler(scope, state, expression) {
var now = Date.now();
if (now - state.then >= state.delay) {
scope.$parent.$eval(expression);
}
};
return directive;
};

Unable to watch/observe scope?

<button-large color="green" click="createWorkstation()" busy="disableSave()" busyLabel="Saving...">Save</button-large>
I'm not able to watch changes to the output of disableSave(). The console.log()'s shown in my directive are never triggered when the output of .busy is changed. What am I doing wrong?
directive('buttonLarge', function () {
return {
scope: {
busy: '&',
click: '&'
},
replace: true,
restrict: 'E',
transclude: true,
template: '<button class="buttonL" ng-transclude/>',
link: function (scope, element, attrs) {
//when the button is busy, disable the button
if (angular.isDefined(scope.busy())) {
scope.$watch(scope.busy(), function () {
console.log('watched');
});
attrs.$observe(scope.busy(), function () {
console.log('observed');
});
}
//setup click event - https://groups.google.com/forum/#!topic/angular/-uVE5WJWwLA
if (angular.isDefined(scope.click)) {
element.bind('click', scope.click);
}
}
}
})
Controller
$scope.newWorkstationDialog = function (workflowProcess) {
var d = $dialog.
dialog({
resolve: {
workflowProcess: function () {
return workflowProcess;
}
}
}).
open('/partials/admin/'+workflowProcess.entity.slug+'/setup.htm', ['$scope', 'dialog', ..., function ($scope, dialog, ...) {
$scope.saving = false;
/* Create the workstation */
$scope.createWorkstation = function () {
console.log('saving');
$scope.saving = true;
$timeout(function () {
$scope.saving = false;
console.log('stopped saving');
}, 1000);
}
//Should the save button be disabled?
$scope.disableSave = function () {
return $scope.saving;//|| $scope.form.$valid;
}
$scope.cancel = function () {
dialog.close();
}
}]);
}
Your syntax of watching in not correct .You should be not using scope when doing watch because internally it use $parse service which internally attach scope . So you need to modify your code as below
1st option
scope.$watch(function(){
return scope.busy()
}, function (newvalue,oldvalue) {
console.log('watched');
});
2nd option
scope.$watch('busy()', function (newvalue,oldvalue) {
console.log('watched');
});

Resources