Clicking on element not hit angularjs directive link function - javascript

It seems my li elements in angularjs directive not responding clicking event.
HTML:
<my-selbg>
<ul>
<li ng-repeat="bgimage in bgimages"><img src={{bgimage}} width="85" height="82" dir={{bgimage}}></li>
</ul>
</my-selbg>
JS:
var mlwcApp = angular.module('mlwcApp', [])
.controller('BgImagesListController', function($scope, $http) {
$http.get("http://localhost:8080/webcontent/bg_images").success(function(response) {
$scope.bgimages = response;
});
})
.directive('myselbg', function(){
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs){
var elementOne = angular.element(element.children[1]);
var elementTwo = angular.element(element.children[2]);
var elementThree = angular.element(element.children[3]);
setUpBGImg = function(){
console.log('link function');
};
$(elementOne).on('click', setUpBGImg);
$(elementTwo).on('click', setUpBGImg);
$(elementThree).on('click', setUpBGImg);
}
};
});
I have 3 li elements and clicking any of them dose not hit the code in link function. Anyone has idea?

You're new to angular, by the looks of it.
First off, before going any further - your directive will not even bind at all in the state it is in. You've got an element directive (which is fine, though if I were you I'd make it an attribute directive by restricting on A, which allows you to then apply it to the list rather than an element above it) named myselbg in your code. However, your markup is set as my-selbg, which would then look for the angular directive mySelbg, which does not exist.
In addition to this, your directive will evaluate before the list is rendered (thanks to the order of priority in execution). You have two choices to go around this:
You can do something like this: https://jsfiddle.net/a01n3srw/1/ . Really not recommended - I am using $timeout in order to evaluate code after the current refresh cycle is done, at which point the list fully exists
You can use the simple ngClick angular core directive in order to make this easy. Added bonus, when your function that you evaluate starts modifying scope, you won't shoot yourself in the foot using the previous method and having to use $apply

Related

Angular: Run method every time a directive updates

I'm looking to have a function run every time an angular directive updates. In my case, I have an array of modal configurations that get used on a modal markup template.
Every time the template is used to generate a modal due to a change in the model, I want to run a positionModal() method.
scope.$watch in the link function doesn't seem to notice when I change the model, and I cant think of any other way of doing this. I tried a post-link function thinking that the compile function would get called when the directive was applied, but that doesn't seem to work either. Here is my example controller:
MyApp.controller("ModalController", function () {
//Define scope vars
$scope.modals = [];
$scope.$on("modalTrigger", function (event, settings) {
$scope.modals.push(settings);
});
});
Note: I've simplified the controller here- know that it DOES work.
Here is the template code:
<div class="modalParent" ng-controller="ModalController">
<div id="{{modal.id}}" class="modal" ng-class="modal.type" ng-repeat="modal in modals">
<div class="content">
<h2 ng-show="modal.title">{{modal.title}}</h2>
<p>{{modal.message}}</p>
<button>{{modal.button}}</button>
</div>
</div>
</div>
The directive is currently like this:
MyApp.directive("modalParent", function () {
var positionModals = function (element) {
element.find(".modal .content").each(function () {
//position logic here
});
};
return {
restrict: "C",
compile: function (tElement) {
positionModals(tElement);
}
};
});
Note: Also simplified for the purposes here.
The positionModals() call works when the first modal gets pushed to the array. After that, it stops working.
I've tried using the linking function as well, same result. scope.$watch(modals, function(){...}) does not work.
Can somebody help me figure out what I'm doing wrong?
Figured it out!
I was applying the directive to the parent, ".modalParent".
The ng-repeated element in this case is the modal itself ".modal".
You would want the directive to run on elements that get updates as the model changes, because then the linking function will get called each time the element is instantiated, rather than sitting and watching the parent and trying to update from there.
Hope this helps somebody.
Instead of calling like this, my approach is to write this in the services and inject that services in the controller wherever you want to get that function or the data to be notified as,
Services.js
as.service("xyzservice",function(factoryname){
//here the code goes...
})
Now inject in Controller,
ac.controller("controllername",function(xyzservice){
})
ac.controller("controllername",function(servicename){
})
ac.controller("controllername",function(xyzservice){
})
Here we have injected it in the two controller, we can get it.

Angular Page Partials Don't allow <script> tags

So long story short, I've tried a billion ways to put a parameter into a string as text, with no solution to be had. I decided that instead I might use a Document.Write to cheat and print a global variable as HTML... However, I discovered that an HTML partial in AngularJS, won't let me use and tags on the partial page.
Example:
$routeProvider
.when('/score', {templateUrl: 'partials/score.html'})
Then, on score.html, I can't put in this statement. It simply does not execute:
<script type="text/javascript">
document.write("This is a test");
//document.write("This is a test"+$rootScope.variable);
</script>
I've also tried window.document.write, even ngApp.document.write. It just doesn't seem to write at all. On network view it looks like the JS isn't running at all. Why? How?
EDIT: I know about data binding. It doesn't work inside an attribute string... I don't know why. This doesn't not work:
<div addthis-toolbox class="addthis_toolbox addthis_default_style addthis_32x32_style social-width" style="width: 150 px">
<a class="addthis_button_twitter" addthis:title="My score is {{score}}"></a>
</div>
EDIT2: Actually, I think that there might be a directive overriding the div class. Here is the angular directive in question:
myApp.directive('addthisToolbox', function() {
return {
restrict: 'A',
transclude: true,
replace: true,
template: '<div ng-transclude></div>',
link: function ($scope, element, attrs) {
// Dynamically init for performance reason
// Safe for multiple calls, only first call will be processed (loaded css/images, popup injected)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#configuration-url
// http://support.addthis.com/customer/portal/articles/381221-optimizing-addthis-performance
addthis.init();
// Ajax load (bind events)
// http://support.addthis.com/customer/portal/articles/381263-addthis-client-api#rendering-js-toolbox
// http://support.addthis.com/customer/portal/questions/548551-help-on-call-back-using-ajax-i-lose-share-buttons
addthis.toolbox($(element).get());
}
}
});
How does one pass data bindings into an attribute if it's got a directive affecting it?

Conditionally animating ng-view transitions

I am trying to apply animations to ng-view (routing) depending of the views involved.
For example, from View1 to View2 I need the View1 leaving through the left side and View1 entering from the right side. Otherwise, from View2 to View1 I need View2 leaving through the right side and View1 entering from the left side.
But I have also situations where I need apply different animations to both views, for example, View1 leaving fading out and View2 entering scaling up.
What I am doing is using a scope associated variable as class in the ng-view:
<div ng-view class="{{transition}}"></div>
This variable is set in each route change with something like this in each controller:
$scope.transition=Global.transition;
$rootScope.$on("$routeChangeStart",function (event, current, previous) {
// Here I get the leaving view and the entering view and the kind of transition is selected
...
$scope.transition=selectedLeavingTransition; // Set the transition for the leaving view
Global.transition=selectedEnteringTransition; // Set the transition for the entering view
});
Global is a service to set the transition variable for the entering scope from the leaving scope.
This way, when a route change is detected, the current ng-view is set with the class associated to selectedLeavingTransition, and the entering ng-view is set with the class associated to selectedEnteringTransition.
For example, if the route change was from View1 to View2 the ng-views during the animation could be:
<div ng-view class="fadeOut ng-animate ng-leave ng-leave-active"></div>
<div ng-view class="scaleUp ng-animate ng-enter ng-enter-active"></div>
The CSS in this case could be:
fadeOut.ng-leave {animation:1s fadeOut;}
scaleUp.ng-enter {animation:1s scaleUp;}
Though it works, I am wondering if there is a simpler way to do it as it seems a little mess.
An alternative solution that doesn't require much code is to define your animations on your routes:
$routeProvider.when('/view1', {
templateUrl: 'view1.html',
controller: 'View1Controller',
animations: {
enter: 'enter-left',
leave: 'leave-left'
}
});
Then use a directive to retrieve the current route's animations and add them to the element:
app.directive('viewAnimations', function ($route) {
return {
restrict: 'A',
link: function (scope, element) {
var animations = $route.current.animations;
if (!animations) return;
if (animations.enter) element.addClass(animations.enter);
if (animations.leave) element.addClass(animations.leave);
}
};
});
And put the directive on the element that contains the ngView directive:
<body ng-view view-animations></body>
Demo: http://plnkr.co/edit/Y3ExDyiPIJwvVKO4njBT?p=preview
Edit: New solution.
To set animations during run-time I would use a service just like you are doing, but a directive to apply them.
Very basic example of service:
app.factory('viewAnimationsService', function ($rootScope) {
var enterAnimation;
var getEnterAnimation = function () {
return enterAnimation;
};
var setEnterAnimation = function (animation) {
enterAnimation = animation;
};
var setLeaveAnimation = function (animation) {
$rootScope.$emit('event:newLeaveAnimation', animation);
};
return {
getEnterAnimation: getEnterAnimation,
setEnterAnimation: setEnterAnimation,
setLeaveAnimation: setLeaveAnimation
};
});
And the directive:
app.directive('viewAnimations', function (viewAnimationsService, $rootScope) {
return {
restrict: 'A',
link: function (scope, element) {
var previousEnter, previousLeave;
var enterAnimation = viewAnimationsService.getEnterAnimation();
if (enterAnimation) {
if (previousEnter) element.removeClass(previousEnter);
previousEnter = enterAnimation;
element.addClass(enterAnimation);
}
$rootScope.$on('event:newLeaveAnimation', function (event, leaveAnimation) {
if (previousLeave) element.removeClass(previousLeave);
previousLeave = leaveAnimation;
element.addClass(leaveAnimation);
});
}
};
});
Demo: http://plnkr.co/edit/DuQXaN2eYgtZ725Zqzeu?p=preview
I have been working on it and I have a neater solution, what I was doing had some problems. Now I am just using the $routeChangeStart at root scope and selecting there the leaving and enter transitions.
The only problem I have is that on the routeChangeStart event I can't modify the leaving view so I can't establish the leaving transition to the ngView element class attribute. I had to set it directly through the DOM (I know that is bad practice).
I tried to modify the leaving view through a shared service, the root scope and $apply() but none of them worked. Once the routeChangeStart event is launched the view seems static.
Here is a working example: jsfiddle.net/isidrogarcia/Fs5NZ

How to add AngularJS directives to .NET WebForms Update Panels?

I have a project that uses legacy code in .NET and WebForms. The legacy code uses several Update Panels.
My hope is to use AngularJS without impacting the legacy code base.
<div ng-app="myApp">
<NS:TheUserControl ID="TheUserControl1" runat="Server" />
</div>
Here is the javascript:
var app = angular.module("myApp", []);
app.run(function ($rootScope, $compile) {
function insertDirective() {
jQuery(targetElementSelector).attr("my-directive", "");
}
insertDirective();
var mgr = Sys.WebForms.PageRequestManager.getInstance();
mgr.add_endRequest(function (sender, args) {
insertDirective();
$compile(jQuery(targetElementSelector))($rootScope);
});
});
The code above adds an attribute to an HTML element inside NS:TheUserControl, and the attribute specifies a directive (see directive below).
Then, these steps are used when the update panel changes:
Detect the change using Sys.WebForms.PageRequestManager (EndRequest listener)
Re-add the directive using javascript
Run $compile on the newly-inserted directive
Here is the directive:
app.directive("myDirective", function () {
return {
template: "<span ng-repeat='item in items'>{{item}}</span>"
+ "<span ng-transclude></span>"
, transclude: true,
, controller: function ($scope) {
$scope.items = ["a", "b", "c"];
}
};
});
This almost works... except...
Problem 1:
AngularJS creates a scope associated with each instance of a directive. When the update panel inside NS:TheUserControl changes the DOM, the previous scopes are still present. I can see this in Batarang (a Chrome developer tool for AngularJS).
Problem 2:
For a brief moment after the update panels change, I get:
abc <-- Initial page load
abcabc <-- Subsequent update panel changes
abcabcabc
abcabcabcabc
Then, a moment later after each update panel change, the content jumps back to the correct:
abc
Questions:
So, how do I either:
Remove the orphaned scopes?
Or, incorporate AngularJS directives in a way that plays nice with the update panels?

Angularjs directive for mouse over an elment

I'm making a profile page for a user. I want to make an inline editable element. Basically when a user move mouse over and element a little pencil icon is shown. Here is what I have so far:
angular.module....yada yada
.directive('editIcon', function () {
return {
restrict: 'A',
link: function($scope, element, attrs) {
var elemIcon = angular.element('<i class="glyphicon glyphicon-pencil"></i>');
var prevDisplay = elemIcon.css('display', 'none');
element.append(elemIcon);
element.on('mouseenter', function() {
elemIcon.css('display', 'inline');
});
element.on('mouseleave', function() {
elemIcon.css('display', 'none');
});
}
};
});
an just add edit-icon to your element. It works, but I'm an angular newbe and a new naming conventions are completely different*. Is the right approach? Should I use template or compile?
tiny rant about broken angular sematics :)
* why restrict: 'A' not 'Attribute' or at least 'Attr' ? save a few bytes?
* transclusion? really? why not inject or something similar
* services are not really services
why restrict: 'A'?
So that the compiler will not waste time looking for elements, CSS classes with this name I guess.
Is the right approach?
If it works :) Showing an element on hover can be accomplished with CSS3/HTML5, if you (and your clients) are on modern browsers.
Should I use template or compile?
Again, since it works, there is no need do make things more complex.

Categories

Resources