ng-click toggle variable and update entire page - angularjs

I have a global variable called RecruiterDashboard.IsColdList. It is a boolean that decides rather to run one directive or another. When the page is loaded the variable is made either true or false through normal scripting.
What I want to do is allow the user to toggle through an ng-click rather to show this other directive or not, so I made an ng-click and set RecruiterDashboard.IsColdList from 'false' to 'true'. Problem is this doesn't reload the angular page and fire the controllers off. How do make the page run through the controllers again?
This is what I have so far:
$scope.showColdList = function (projId) {
RecruiterDashboard.isColdList = true;
};
I want to point out that I am not using angular routing. I am using C# MVC.
My logic looks like so:
callPanelControllers.controller('callPanelController', function($scope) {
$scope.isColdList = RecruiterDashboard.isColdList;
});
callPanelControllers.controller('incomingCall', function ($scope) {
$scope.showColdList = function () {
RecruiterDashboard.isColdList = true;
};
});
<div ng-click="showColdList()" ng-controller="incomingCall"></div>
<div ng-controller="callPanelController">
<cold-list ng-show="isColdList"></cold-list>
</div>

i had developed sort of a hack, so that the entire content inside of a given element is reloaded on change of a certain variable.
csapp.directive("csReloadOn", ["$timeout", function ($timeout) {
var getTemplate = function () {
return '<div ng-if="doRefreshPageOnModeChange"><div ng-transclude=""></div></div>';
};
var linkFunction = function (scope, element, attrs) {
scope.doRefreshPageOnModeChange = true;
scope.$watch(attrs.csReloadOn, function (newVal, oldVal) {
if (newVal === oldVal) return;
scope.doRefreshPageOnModeChange = false;
$timeout(function () { scope.doRefreshPageOnModeChange = true; }, 100);
});
};
return {
restrict: 'A',
transclude: true,
template: getTemplate,
link: linkFunction
};
}]);
you can use it like
<div cs-reload-on="{{pagemode}}">
<!-- your html here-->
</div>
so it just removes and re-renders the complete content inside of the div, so everything is reinitialized etc etc.

Related

Two instances of same directive overwrite each other

I am using same directive for multiple instances of a toolbar. A toolbar has a button with a toggle functionality. So, if a first click does 'add' then another click does 'remove'. Here is a simplified directive that I am using:
a.directive("trendChartToolbar", function ($templateRequest, $compile) {
return {
scope: {
},
link: function (scope, elem, attrs) {
templateSRC = 'templateSRC.html';
$templateRequest(templateSRC).then(function (html) {
var template = angular.element(html);
$(elem).append(template);
template = $compile(template)(scope);
angular.element(elem).on('click', function (event) {
if (scope.state === undefined)
scope.state = true;
else
scope.state = !scope.state;
service.someMethod(event.target.name, scope);
});
});
}
};
});
Here is the template:
<div>
<img style="cursor:pointer;margin-right:10px" name="deviation"
src="image.svg" />
</div>
As long as only one instance of the directive exists everything works fine. scope.state changes from true to false and back. But if another instance exists then when I click on the first instance second time the scope has state property undefined. My expectation was that as long as I am using an isolated scope it would work. Any idea?
Thanks

multiple function on ng-click angularjs

I'm new to AngularJs and learning now, in my current assignment I need to achieve multiple things on ng-click.
To hide and show some DOM elements based on the ng-click
Change the background of the element where the ng-click is applied on, I'm trying to acheive this using a directive.
Mark-up:
<div class="catFilter f6" ng-click="showSubCat = !showSubCat;toggleDropDown()">
Choose A Genre
</div>
<div class="inactive" ng-show="showSubCat" ng-click="hideSubCat = !hideSubCat" ng-hide="!hideSubCat">
</div>
<div class="cat-drop-menu-list" ng-show="showSubCat" ng-hide="!hideSubCat">
</div>
angular directive
retailApp.directive('toggleDropDown', function() {
return function(scope, element, attrs) {
$scope.clickingCallback = function() {
element.css({'background':'url("../images/down-arrow.png") no-repeat 225px 12px;'});
};
element.bind('click', $scope.clickingCallback);
}
});
Issues:
I'm not able to see the directive being applied, i.e., when I click on choose a genre, it is hiding and showing the other two divs, but not changing the back ground.
You can do this a couple ways, with bindings or directives:
http://jsfiddle.net/abjeex75/
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.show_sub_cat = false;
$scope.show = function () {
$scope.show_sub_cat = true;
}
$scope.hide = function () {
$scope.show_sub_cat = false;
}
});
app.directive('toggleBg', function () {
var directive = {
restrict: 'A',
link: link
}
return directive;
function link(scope, element, attr) {
element.on('click', function () {
element.toggleClass('red');
});
}
});

how to binding one way angular and binding again when model change?

There are any solution or angular plug-in to do binding one way and binding again when model is change?
Now I'm using plug-in bind-once , But it just binding on first time and then it destroy watcher. Example:
<div bindonce="model"><span bo-bind="model.title"></span></div>
Angular already does this for you
<div><span ng-bind="model.title"></span></div>
or
<div><span>{{model.title}}</span></div>
how you can do it is: re-redner(ng-if) the page on change of certain variable. what would happen is, the
dom would be removed, and added again, which it is being added again : angular should bind the variable to its current value, so this way you get to keep bind-once and also update the value as per your need.
only caveat is, you would need an indicator for DOM on when to reload.
you can use the reload directive below(which I am using in my app):
csapp.directive("csReloadOn", ["$timeout", "Logger", function ($timeout, logManager) {
var $log = logManager.getInstance("csReloadOn");
var getTemplate = function () {
return '<div ng-if="doRefreshPageOnModeChange"><div ng-transclude=""></div></div>';
};
var linkFunction = function (scope, element, attrs) {
scope.doRefreshPageOnModeChange = true;
scope.$watch(attrs.csReloadOn, function (newVal, oldVal) {
if (newVal === oldVal) return;
$log.info("changed mode from : " + oldVal + ", to : " + newVal);
scope.doRefreshPageOnModeChange = false;
$timeout(function () { scope.doRefreshPageOnModeChange = true; }, 100);
});
};
return {
restrict: 'A',
transclude: true,
template: getTemplate,
link: linkFunction
};
}]);

AngularJS - Binding 'click' to directive

I'm currently working on a simple module to display notifications to the user in the form of toasts, or popups, that appear in the lower right-hand corner of the screen. The following directive works great except I would prefer to
a) use .bind instead of ng-click to handle manual dismissing of a notification;
b) use replace: true help reduce some of the generated markup
toast.directive('toast', function() {
return {
link: function($scope, $element, $attrs, $location) {
$scope.delayDismiss = function(index) {
setTimeout(function() {
$scope.dismiss(index);
$scope.$apply();
}, 5000);
}
$scope.dismiss = function(index) {
$scope.messages[index].dismissed = true;
}
$element.bind('click', function() {
console.log('This never triggers');
});
},
replace: true,
restrict: 'E',
template: '<li ng-repeat="toast in messages" ng-click="dismiss($index)" ng-init="delayDismiss($index)" ng-if="!toast.dismissed">{{toast.sender}} says {{toast.message}}</li>'
}
});
The problem is that, now, $element in my link() function refers to the generated Angular comment instead of the <li> resulting in the bound click event never triggering. Which doesn't make any sense to me.
Am I misunderstanding this whole directive thing?
a) use .bind instead of ng-click to handle manual dismissing of a notification;
By removing ngRepeat from your directive's template, you should be able to do this within the directive's link function. However, I would advise against doing this if it is only a matter of preference on your part.
See the answer to How do I “think in AngularJS” if I have a jQuery background? for more details as to why.
Your usage of ngRepeat is causing the linked element to refer to the generated comment node instead of the <li>.
By moving the ngRepeat directive outside your directive's template, the behavior is closer to what you're expecting:
<toast ng-repeat="toast in messages"></toast>
b) use replace: true help reduce some of the generated markup
That's fine, although note that the angular 1.3 documentation mentions that replace has been deprecated and will be removed from the next major release.
To make the directive more "re-usable", I would move the logic for an individual toast into its own factory instead of trying to handle it on the link function of the directive.
I would also add a ngTransclude to the template <li> and transclude: true to the directive's definition.
View
<ol>
<toast ng-repeat="toast in messages">
{{toast.sender}} says {{toast.message}}
</toast>
</ol>
Controller
controller("MainCtrl", function ($scope, $toast) {
$scope.addToast = function(message, sender, timeout) {
$toast.add(message, sender, parseInt(timeout));
};
})
Directive
directive('toast', function() {
return {
replace: true,
restrict: 'E',
template: '<li ng-click="toast.dismiss()" ng-transclude></li>',
transclude: true
}
})
Factory
factory("$toast", function($rootScope, $timeout) {
var messages = $rootScope.messages = [];
var $toast = (function() {
function $toast(message, sender, timeout) {
var self = this;
this.message = message;
this.sender = sender;
this.timeout = timeout || $toast.defaultTimeout;
this.timer = $timeout(function() {
self.dismiss();
}, this.timeout);
}
$toast.prototype.dismiss = function() {
if (this.timer) {
$timeout.cancel(this.timer);
this.timer = null;
}
var index = messages.indexOf(this);
if (index !== -1) {
messages.splice(index, 1);
}
};
return $toast;
})();
$toast.defaultTimeout = 5000;
$toast.add = function(message, sender, timeout) {
messages.push(new $toast(message, sender, timeout));
return $toast;
};
return $toast;
})
Here is a working example: http://plnkr.co/edit/52JiHWi2jgloIyDst74I?p=preview

How to pass async data from directive to controller?

I want to compile a third-party api (uploadcare) to a directive.
The api will return the data info after uploaded in async then I want to do something with the return data in my controller but I have to idea how to pass the return data from directive to controller. Below is my code.
in js
link: function (scope, element, attrs) {
//var fileEl = document.getElementById('testing');
var a = function() {
var file = uploadcare.fileFrom('event', {target: fileEl});
file.done(function(fileInfo) {
//scope.$apply(attrs.directUpload)
//HERE IS MY PROBLEM.
//How can I get the fileInfo then pass and run it at attrs.directUpload
}).fail(function(error, fileInfo) {
}).progress(function(uploadInfo) {
//Show progress bar then update to node
console.log(uploadInfo);
});
};
element.bind('change', function() {a()});
}
in html
<input type="file" direct-upload="doSomething()">
in controller
$scope.doSomething = function() {alert(fileInfo)};
AngularJS allows to execute expression in $parent context with specified values, in your case doSomething().
Here's what you need to do that:
In directive definition, mark directUpload as expression:
scope: {
directUpload: "&"
}
In done callback, call:
scope.directUpload({fileInfo: fileInfo})
Update markup:
<input type="file" direct-upload="doSomething(fileInfo)">
To summorize: scope.directUpload is now a callback, which executes expression inside attribute with specifeid values. This way you can pass anything into controller's doSomething.
Read $compile docs for detailed explanation and examples.
Example you might find useful:
angular
.module("app", [])
.directive("onDone", function ($timeout) {
function link (scope, el, attr) {
$timeout(function () {
scope.onDone({
value: "something"
});
}, 3000)
}
return {
link: link,
scope: {
onDone: "&"
}
}
})
.controller("ctrl", function ($scope) {
$scope.doneValue = "nothing";
$scope.done = function (value) {
$scope.doneValue = value;
};
})
<body ng-controller="ctrl">
Waiting 3000ms
<br>
<div on-done="done(value)">
Done: {{doneValue}}
</div>
</body>
You can pass through an object to the scope of the directive using = within the directive to do two way data binding. This way you can make updates to the data within the directive on the object and it will be reflected in it's original location in the controller. In the controller you can then use $scope.watch to see when the data is changed by the directive.
Something like
http://plnkr.co/edit/gQeGzkedu5kObsmFISoH
// Code goes here
angular.module("myApp",[]).controller("MyCtrl", function($scope){
$scope.something = {value:"some string"}
}).directive("simpleDirective", function(){
return {
restrict:"E",
scope:{someData:"="},
template:"<button ng-click='changeData()'>this is something different</button>",
link: function(scope, iElem, iAttrs){
scope.changeData=function(){
scope.someData.value = "something else";
}
}
}
});

Resources