Send parameters from directive to controllers in AngularJS - javascript

Inside oferta-ut.controller.js I need to change state from main.ut.ofertas to main.ut.resumen. Then select something and call scope.clickAction inside ut-result.directive.js. This action should send parameters to oferta-ut.controller.js so I can use it with others parameters to go to another state.
Here goes some code
servicio-ut.controller.js
$state.go('main.ut.ofertas', {
plan: $stateParams.plan,
destino: $stateParams.destino,
fechaEntrada: $stateParams.fechaEntrada,
fechaSalida: $stateParams.fechaSalida,
duracion: $stateParams.duracion,
huespedes: $stateParams.huespedes,
servicio: ctrl.servicioInc,
});
oferta-ut.controller.js
'use strict';
function ofertasUtCtrl($scope, $stateParams, $state,
unidadTuristicaService, moment, util, SessionService, $analytics, $window) {
'ngInject';
var ctrl = this,
if (!$stateParams.servicio) {
$state.go('main.home');
} else {
ctrl.ofertaSeleccionada=null;
activate();
}
function activate() {
var fechaEntrada = $stateParams.fechaEntrada.getFullYear() + "-" +
($stateParams.fechaEntrada.getMonth() + 1) + "-" +
$stateParams.fechaEntrada.getDate();
var fechaSalida = $stateParams.fechaSalida.getFullYear() + "-" +
($stateParams.fechaSalida.getMonth() + 1) + "-" +
$stateParams.fechaSalida.getDate();
unidadTuristicaService.obtenerOfertasUt(
$stateParams.plan.id,$stateParams.servicio.code,
$stateParams.destino.id,
fechaEntrada,
fechaSalida,
$stateParams.duracion.id)
.then(function(ofertas){
for(var clave in ofertas.resultadoBusquedaUt){
ofertas.resultadoBusquedaUt[clave].fechaDesde = moment(fechaEntrada).format('DD/MM/YYYY');
ofertas.resultadoBusquedaUt[clave].fechaHasta = moment(fechaSalida).format('DD/MM/YYYY');
}
ctrl.ofertas = ofertas;
});
}
ctrl.confirmarServ = function() {
$analytics.eventTrack("Clic Siguiente", {
category: 'UT',
label: "Clic Servicios UT"
});
$state.go('main.ut.resumen', {
plan: $stateParams.plan,
destino: $stateParams.destino,
fechaEntrada: ctrl.fechaEntrada,
fechaSalida: ctrl.fechaSalida,
huespedes: ctrl.cantHuespuedes,
duracion: $stateParams.duracion,
servicio: ctrl.servicioInc
});
};
}
module.exports = ofertasUtCtrl;
The view for the action is this one
<div flex>
<pager results="ctrl.ofertas.resultadoBusquedaUt" per-page="10" result-type="UT"></pager>
</div>
ut-result.directive.js
"use strict";
var utResultView = require('./ut-result.view.html'),
imgPlaceHolder = require('../../img/hotel_holder.png');
function hotelResult(util, $state, moment) {
'ngInject';
var directive = {
templateUrl: utResultView,
restrict: 'EA',
scope: {
model: '=',
params: '<'
},
link: link
};
return directive;
function link(scope) {
scope.icono = mapearIcono;
scope.filterHide = function() {
// ver si está filtrado por algun criterio
return scope.model.filterHide && scope.model.filterHide.length > 0;
};
var unbindWatch = scope.$watch("model", function(oldV, newV) {
if (newV) {
activate();
}
});
function activate() {
// iterador para estrellitas
scope.estrellas = [1,2,3,4,5].slice(0, scope.model.estrellas);
//Categoria UT
scope.categoriaUt = scope.model.categoriasUt;
scope.datosPrestador = scope.model.razonSocial + ' - CUIT: ' + scope.model.cuit;
scope.imgSrc = scope.model.urlImagen || imgPlaceHolder;
scope.imgError = imgPlaceHolder;
scope.precio = scope.model.precio? util.formatCurrency(scope.model.precio) : null;
scope.clickAction = function(model) {
// THIS ACTION
};
}
}
module.exports = hotelResult;

Related

Can't update view from $timeout

I'm loading a page with 3 collapsible panels, all containing a grid.
I can successfully load the panel as expanded (also tested with collapsed), using this code:
var component = {
bindings: {
configurationMigrationLogs: '<'
},
template: require("./reMigrationConfiguration.template.html"),
controller: ControllerFn,
controllerAs: 'vm'
};
ControllerFn.$inject = ['$timeout'];
function ControllerFn($timeout) {
const vm = this;
vm.isCardOpen = true; //Does work.
$timeout(function (vm) {
vm.isCardOpen = false; //Doesn't work.
console.log(vm);
}, 3000, true, vm);
}
export default function (app) {
app.component('reMigrationConfiguration', component);
}
However, when I try to collapse the panel in the $timeout function, it doesn't work.
I can see the vm.isCardOpen property is updated to false, in the console window.
But the panel remains expanded.
Here's the HTML:
<re-card is-collapsed="true" is-open="vm.isCardOpen" ng-repeat="configurationMigrationLog in vm.configurationMigrationLogs">
<!--grid code here-->
</re-card>
The re-card component is set up in this .js:
(function() {
'use strict';
angular.module('app.components')
.directive('reCard', directiveFn);
directiveFn.$inject = ['$timeout'];
function directiveFn($timeout) {
var directive = {
restrict:'EA',
scope:true,
priority:10,
controller:controllerFn,
transclude:true,
controllerAs:'reCard',
template: '<div>' +
'<div re-blocked="isBlocked" re-loading="isBusy"> ' +
'<div class="grid simple fadeIn animated" ' +
'ng-class="{collapsible:isCollapsible, \'is-open\':!isCollapsed }" ' +
're-transclude></div>' +
'</div></div>',
link:{
pre:preLinkFn,
post:postLinkFn
}
};
return directive;
function preLinkFn(scope, ele, attrs) {
if (attrs.isCollapsed) {
scope.isCollapsed = scope.$eval(attrs.isCollapsed);
scope.isCollapsible = attrs.isCollapsed;
scope.isOpen = scope.$eval(attrs.isOpen);
}
}
function postLinkFn(scope, ele, attrs, ctrl) {
var blockWatcher, obsv;
scope.isBusy = false;
if (attrs.notifyOfToggle) {
ctrl.registerNotifyOfToggle(attrs.notifyOfToggle);
}
if (attrs.isBlocked) {
blockWatcher = scope.$parent.$watch(attrs.isBlocked, function(val) {
scope.isBlocked = val;
});
}
if (attrs.hasOwnProperty('isBusy')) {
obsv = attrs.$observe('isBusy', function(val) {
if (val && scope.$eval(val)) {
scope.isBusy = true;
}
else {
scope.isBusy = false;
}
});
}
scope.$on('destroy', function() {
blockWatcher();
});
attrs.$observe('isOpen', function(val) {
if (typeof(val) !== 'undefined') {
ctrl.toggleCollapsed(!scope.$eval(val));
}
});
}
}
controllerFn.$inject = ['$scope'];
function controllerFn($scope) {
var notifyOfToggle = null;
this.getScope = function() {
return $scope;
};
this.toggleCollapsed = function(val) {
if (typeof($scope.isCollapsed) !== 'undefined') {
if (angular.isDefined(val)) {
$scope.isCollapsed = val;
} else {
$scope.isCollapsed = !$scope.isCollapsed;
}
if (notifyOfToggle) {
$scope.$eval(notifyOfToggle);
}
}
};
this.registerNotifyOfToggle = function(notifyOfToggleFromAttrs) {
notifyOfToggle = notifyOfToggleFromAttrs;
};
}
})();
Binding your is-open attribute should fix it:
<re-card is-collapsed="true" is-open="{{vm.isCardOpen}}" ng-repeat="configurationMigrationLog in vm.configurationMigrationLogs">
<!--grid code here-->
</re-card>

get selected checkbox values from tree in angularjs

I would like to know how can we get the selected checkbox values from tree in controller from the below example? On click of a button i want to display all the checkbox names in an array. Here is my plnkr- https://plnkr.co/edit/OSpLLl9YrlzqhM7xsYEv?p=preview
//code goes here,
//Controller
Controller to display the tree.
(function (ng) {
var app = ng.module('tree', ['tree.service', 'tree.directives']);
app.controller("TreeController", ["TreeService", function (TreeService) {
var tc = this;
buildTree();
function buildTree() {
TreeService.getTree().then(function (result) {
tc.tree = result.data;
}, function (result) {
alert("Tree no available, Error: " + result);
});
}
}]);
})(angular);
//Tree Directive
Directive used for creating tree node.
(function (ng) {
var app = ng.module('tree.directives', []);
app.directive('nodeTree', function () {
return {
template: '<node ng-repeat="node in tree"></node>',
replace: true,
restrict: 'E',
scope: {
tree: '=children'
}
};
});
app.directive('node', function ($compile) {
return {
restrict: 'E',
replace: true,
templateUrl: 'node.html', // HTML for a single node.
link: function (scope, element) {
/*
* Here we are checking that if current node has children then compiling/rendering children.
* */
if (scope.node && scope.node.children && scope.node.children.length > 0) {
scope.node.childrenVisibility = true;
var childNode = $compile('<ul class="tree" ng-if="!node.childrenVisibility"><node-tree children="node.children"></node-tree></ul>')(scope);
element.append(childNode);
} else {
scope.node.childrenVisibility = false;
}
},
controller: ["$scope", function ($scope) {
// This function is for just toggle the visibility of children
$scope.toggleVisibility = function (node) {
if (node.children) {
node.childrenVisibility = !node.childrenVisibility;
}
};
// Here We are marking check/un-check all the nodes.
$scope.checkNode = function (node) {
node.checked = !node.checked;
function checkChildren(c) {
angular.forEach(c.children, function (c) {
c.checked = node.checked;
checkChildren(c);
});
}
checkChildren(node);
};
}]
};
});
})(angular);
Hello: Look at this plunker link. It works here
https://plnkr.co/edit/vaoCzUJZBf31wtLNJ5f5?p=preview
(function (ng) {
var app = ng.module('tree', ['tree.service', 'tree.directives']);
app.controller("TreeController", ["TreeService", "$scope", function (TreeService, $scope) {
var tc = this;
buildTree();
function buildTree() {
TreeService.getTree().then(function (result) {
tc.tree = result.data;
}, function (result) {
alert("Tree no available, Error: " + result);
});
}
$scope.selectedItems = [];
$scope.getSelected = function(){
$scope.selectedItems = [];
function checkChildren(c) {
angular.forEach(c.children, function (c) {
if (c.checked){
$scope.selectedItems.push({"selected":c.name});
}
checkChildren(c);
});
}
angular.forEach(tc.tree, function(value, key) {
if (value.checked){
$scope.selectedItems.push({"selected":value.name});
}
checkChildren(value);
});
};
}]);})(angular);

Using controller defined in included template

In the code snippet I try to use a controller FooCtrl which is defined in the included template app/foo.html by using the directive common.script.
angular.module('common.script', []).directive('script', function() {
return {
restrict: 'E',
scope: false,
compile: function(element, attributes) {
if (attributes.script === 'lazy') {
var code = element.text()
new Function(code)()
}
}
}
})
angular.module('app.templates', ['app/foo.html'])
angular.module("app/foo.html", []).run(function($templateCache) {
$templateCache.put("app/foo.html",
"<script data-script=\"lazy\">\n" +
" console.log('Before FooCtrl')\n" +
" angular.module('app').controller('FooCtrl', function($scope) {\n" +
" console.log('FooCtrl')\n" +
" })\n" +
"<\/script>\n" +
"<div data-ng-controller=\"FooCtrl\">app\/foo.html\n" +
"<\/div>"
)
})
angular.module('app', ['common.script', 'app.templates']).controller('ApplicationCtrl', function($scope) {
console.log('ApplicationCtrl')
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div data-ng-app="app" data-ng-controller="ApplicationCtrl">
<div data-ng-include="'app/foo.html'"></div>
</div>
But instead of the expected output FooCtrl in the console AngularJS throws:
Error: [ng:areq] Argument 'FooCtrl' is not a function [...]
I don't understand why! The code in the template is executed before the exception is thrown, thus the controller should be defined. How could I fix that?
The real problem here is lazy loading of resources! There are tons of material and related posts about this topic.
The solution here could be an extended common.script directive:
'use strict'
angular.module('common.script', [])
.config(function($animateProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
angular.module('common.script').lazy = {
$animateProvider: $animateProvider,
$controllerProvider: $controllerProvider,
$compileProvider: $compileProvider,
$filterProvider: $filterProvider,
$provide: $provide
}
})
.directive('script', function() {
return {
restrict: 'E',
scope: {
modules: '=script'
},
link: function(scope, element) {
var offsets = {}, code = element.text()
function cache(module) {
offsets[module] = angular.module(module)._invokeQueue.length
}
function run(offset, queue) {
var i, n
for (i = offset, n = queue.length; i < n; i++) {
var args = queue[i], provider = angular.module('common.script').lazy[args[0]]
provider[args[1]].apply(provider, args[2])
}
}
if (angular.isString(scope.modules)) {
cache(scope.modules)
} else if (angular.isArray(scope.modules)) {
scope.modules.forEach(function(module) {
cache(module)
})
}
/*jshint -W054 */
new Function(code)()
Object.keys(offsets).forEach(function(module) {
if (angular.module(module)._invokeQueue.length > offsets[module]) {
run(offsets[module], angular.module(module)._invokeQueue)
}
})
}
}
})
The only downside of this solution is that you have to specify the module(s) you want to extend in a script tag:
<script data-script="'app'">
angular.module('app').controller('FooCtrl', function($scope) {
console.log('Works!')
})
</script>

AngularJS unit tests: Initialize scope of a directive's controller

I have the following code for a directive using a separated controller with the "controller as" syntax:
'use strict';
angular.module('directives.featuredTable', [])
.controller('FeaturedTableCtrl',
['$scope',
function ($scope){
var controller = this;
controller.activePage = 1;
controller.changePaginationCallback =
controller.changePaginationCallback || function(){};
controller.density = 10;
controller.itemsArray = controller.itemsArray || [];
controller.metadataArray = controller.metadataArray || [];
controller.numberOfItems = controller.numberOfItems || 0;
controller.numberOfPages = 1;
controller.options = controller.options || {
'pagination': false
};
controller.changePaginationDensity = function(){
controller.activePage = 1;
controller.numberOfPages =
computeNumberOfPages(controller.numberOfItems, controller.density);
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
};
controller.getProperty = function(object, propertyName) {
var parts = propertyName.split('.');
for (var i = 0 ; i < parts.length; i++){
object = object[parts[i]];
}
return object;
};
controller.setActivePage = function(newActivePage){
if(newActivePage !== controller.activePage &&
newActivePage >= 1 && newActivePage <= controller.numberOfPages){
controller.activePage = newActivePage;
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
};
initialize();
$scope.$watch(function () {
return controller.numberOfItems;
}, function () {
controller.numberOfPages =
computeNumberOfPages(controller.numberOfItems, controller.density);
});
function computeNumberOfPages(numberOfItems, density){
var ceilPage = Math.ceil(numberOfItems / density);
return ceilPage !== 0 ? ceilPage : 1;
}
function initialize(){
if(controller.options.pagination){
console.log('paginate');
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
}
}]
)
.directive('featuredTable', [function() {
return {
'restrict': 'E',
'scope': {
'metadataArray': '=',
'itemsArray': '=',
'options': '=',
'numberOfItems': '=',
'changePaginationCallback': '&'
},
'controller': 'FeaturedTableCtrl',
'bindToController': true,
'controllerAs': 'featuredTable',
'templateUrl': 'directives/featuredTable/featuredTable.tpl.html'
};
}]);
You can see at the beginning of the controller that I'm initializing its properties with the attributes passed by the directive or providing default values:
controller.activePage = 1;
controller.changePaginationCallback =
controller.changePaginationCallback || function(){};
controller.density = 10;
controller.itemsArray = controller.itemsArray || [];
controller.metadataArray = controller.metadataArray || [];
controller.numberOfItems = controller.numberOfItems || 0;
controller.numberOfPages = 1;
controller.options = controller.options || {
'pagination': false
};
At the end I'm executing the initialize(); function that will execute the callback according to the options:
function initialize(){
if(controller.options.pagination){
controller.changePaginationCallback({
'page': controller.activePage,
'perPage': controller.density
});
}
}
I'm now trying to unit test this controller (with karma and jasmine) and I need to "simulate" the parameters passed by the directive, I tried the following:
'use strict';
describe('Controller: featured table', function () {
beforeEach(module('directives.featuredTable'));
var scope;
var featuredTable;
var createCtrlFn;
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
createCtrlFn = function(){
featuredTable = $controller('FeaturedTableCtrl', {
'$scope': scope
});
scope.$digest();
};
}));
it('should initialize controller', function () {
createCtrlFn();
expect(featuredTable.activePage).toEqual(1);
expect(featuredTable.changePaginationCallback)
.toEqual(jasmine.any(Function));
expect(featuredTable.density).toEqual(10);
expect(featuredTable.itemsArray).toEqual([]);
expect(featuredTable.metadataArray).toEqual([]);
expect(featuredTable.numberOfPages).toEqual(1);
expect(featuredTable.numberOfItems).toEqual(0);
expect(featuredTable.options).toEqual({
'pagination': false
});
});
it('should initialize controller with pagination', function () {
scope.changePaginationCallback = function(){};
spyOn(scope, 'changePaginationCallback').and.callThrough();
scope.options = {
'pagination': true
};
createCtrlFn();
expect(featuredTable.activePage).toEqual(1);
expect(featuredTable.changePaginationCallback)
.toEqual(jasmine.any(Function));
expect(featuredTable.density).toEqual(10);
expect(featuredTable.itemsArray).toEqual([]);
expect(featuredTable.metadataArray).toEqual([]);
expect(featuredTable.numberOfPages).toEqual(1);
expect(featuredTable.numberOfItems).toEqual(0);
expect(featuredTable.options).toEqual({
'pagination': true
});
expect(featuredTable.changePaginationCallback).toHaveBeenCalledWith({
'page': 1,
'perPage': 10
});
});
});
And got the following error, meaning that scope is not well initialized:
Expected Object({ pagination: false }) to equal Object({ pagination: true })
at test/spec/app/rightPanel/readView/historyTab/historyTab.controller.spec.js:56
Simulating the bindings would be non-trivial - after all, it's hard to really know what compiling and linking a directive does with the data passed to it...unless you just do it yourself!
The angular.js documentation offers a guide on how to compile and link a directive for unit testing - https://docs.angularjs.org/guide/unit-testing#testing-directives. After doing that, you'd just need to get the controller from the resulting element(see the documentation for the controller() method here - https://docs.angularjs.org/api/ng/function/angular.element) and perform your tests. ControllerAs would be irrelevant here - you would be testing the controller directly, instead of manipulating the scope.
Here's an example module:
var app = angular.module('plunker', []);
app.controller('FooCtrl', function($scope) {
var ctrl = this;
ctrl.concatFoo = function () {
return ctrl.foo + ' world'
}
})
app.directive('foo', function () {
return {
scope: {
foo: '#'
},
controller: 'FooCtrl',
controllerAs: 'blah',
bindToController: true,
}
})
And test setup:
describe('Testing a Hello World controller', function() {
ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $compile) {
var $scope = $rootScope.$new();
var template = '<div foo="hello"></div>'
var element = $compile(template)($scope)
ctrl = element.controller('foo')
}));
it('should produce hello world', function() {
expect(ctrl.concatFoo()).toEqual('hello world')
});
});
(Live demo: http://plnkr.co/edit/xoGv9q2vkmilHKAKCwFJ?p=preview)

$watch is not called in angularjs

Hi I am newbie to angular I am trying to update using ng-click directive and $watch function. On first click it is getting updated but second time it doesn't get updated. Bare my english and little knowledge Here is my code.
angular.module('charts', [])
.factory('socket', ['$rootScope', function ($rootScope) {
var safeApply = function(scope, fn) {
if (scope.$$phase) {
fn(); // digest already in progress, just run the function
} else {
scope.$apply(fn); // no digest in progress, run with $apply
}
};
var socket = io.connect('http://localhost:5000/');
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
safeApply($rootScope, function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
safeApply($rootScope, function () {
if (callback) {
callback.apply(socket, args);
}
});
})
},
disconnect: function () {
socket.disconnect();
},
socket: socket
};
}])
.controller('mainCtrl',['$scope','socket', function AppCtrl ($scope, socket) {
$scope.options = {width: 500, height: 300, 'bar': 'aaa'};
$scope.data = {"angular":12,"js":15};
$scope.hovered = function(d){
$scope.barValue = d.name;
//$scope.$apply();
};
$scope.barValue = 'None';
}])
.directive('barChart', function(){
var chart = d3.custom.barChart();
return {
restrict: 'E',
replace: true,
template: '<div class="chart"></div>',
scope:{
height: '=height',
data: '=data',
hovered: '&hovered'
},
link: function(scope, element, attrs) {
var chartEl = d3.select(element[0]);
chart.on('customHover', function(d, i){
scope.hovered({args:d});
});
scope.$watch('data', function (newVal, oldVal) {
console.log(newVal);
chartEl.datum(newVal).call(chart);
});
scope.$watch('height', function(d, i){
chartEl.call(chart.height(scope.height));
})
}
}
})
.directive('chartForm', function(){
return {
restrict: 'E',
replace: true,
controller: function AppCtrl ($scope, socket) {
$scope.start = function(d, i) {
$scope.btnIsDisabled = true;
socket.on("connected",function(r){ console.log('breakpoint 1');/*console.log(r);*/ });
socket.on("new tweet", function(tweet) {
var hashtags = tweet.entities.hashtags;
if(hashtags.length) {
_.each(hashtags, function(hashtag) {
hashtag = hashtag.text.toLowerCase();
// if(hashtag == 'food' || hashtag == 'pizza' || hashtag == 'samosa') {
// console.log(hashtag);
if(_hashtags[hashtag]) {
_hashtags[hashtag]++;
}
else {
_hashtags[hashtag] = 1;
}
//}
});
}
num_tweets++;
});
};
$scope.update = function(d, i) {
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
$scope.safeApply( function() {
console.log(JSON.stringify(_hashtags));
$scope.data = _hashtags;
});
};
},
template: '<div class="form">' +
'Height: {{options.height}}<br />' +
'<input type="range" ng-model="options.height" min="100" max="800"/>' +
'<br /><button ng-click="start()" ng-disabled="btnIsDisabled">Start</button>'+
'<button ng-click="update()" ng-disabled="false">Update Data</button>' +
'<br />Hovered bar data: {{barValue}}</div>'
}
});

Categories

Resources