Exchange information between controllers inside directive - javascript

I try to register events like window-close, minimize, maximize and use it inside my controller inside content so I can clean up some things before window is closed. I've just an stupid idea with random ids and broadcast. Anything better?
Window Controller
<div class="window">
<div class="header">
<a ng-click="minimize()">Minimize</a>
<a ng-click="maximize()">Maximize</a>
<a ng-click="close()">Close</a>
<div class="content" ng-include="Controllers/someWindow.html"></div>
</div>
Controllers/someWindow.html
<div ng-controller="SomeWindowCtrl">
</div>

You can wrap the div.window element into a directive (say, my-window) and expose a controller from it. Then, directives inside it can require my-window as parent, getting its controller as the fourth argument of the link function.
A concise example can be found here.

You can use
$rootScope.$broadcast('EVENT_ID', data);
and
$rootScope.$on('EVENT_ID', function($event, data) {
//event handler
});

Related

How do I select "this" in angular?

I have 5 buttons using the same ng-click function. Basically each of the buttons operate similarly to a tabbed navigation, where you click one of the buttons and it takes you to that tab's pane. Each of these buttons can be repeatable and are housed in a template. The tab panes are also all in a template but aren't all active until a user clicks one of the buttons and creates a page. So basically there are multiple click functions nested within click functions that do different things depending on what user has activated.
In jQuery, I could just use "this" and select the object that was clicked and do all my manipulations to that object easily; however, it doesn't appear there's a way to do that using just angular. Currently, when you click one of these buttons it does the same thing to all of them. I figure I could create 5 separate functions, but I don't want to do that for scalability reasons.
So to summaraize:
Is there a way to select "this" in Angular?
I'd like a solution that is just using Angular and no jQuery
Is there an efficient way of dealing with click functions within click functions?
<nav class="block--menu">
<section class="content--menu" ng-controller="ActiveCtrl">
<div class="menu" >
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
<button class="menu__item" ng-click="showConfirm()"></button>
</div>
</section>
You can access jQuery event object using $event in angular events check the documentation for details but if you are sending that to your controller it most likely means you are not doing it in angular way.
the usage is
<button class="menu__item" ng-click="showConfirm($event)"></button>
and in the controller
$scope.showConfirm = function($event){
//$event.target should be your link
};
You should stop thinking in a jQuery way and don't try to manipulate the DOM directly. In your controller you should only manipulate the data, which is then reflected in the view. When you think Angular-way, your code usually looks as follows:
HTML
<section ng-controller="ActiveCtrl as ctrl">
<div class="menu" >
<button ng-repeat="button in ctrl.buttons track by $index"
ng-click="ctrl.showConfirm(button)"
ng-class="{'menu__item_active':button.active, 'menu__item':true}"
>{{button.name}}</button>
</div>
</section>
JavaScript
angular.module('app',[]).
controller('ActiveCtrl', ['$window', function($window) {
this.buttons = [{
name: 'First'
}, {
name: 'Second'
}, {
name: 'Third'
}];
this.showConfirm = function(button) {
button.active = !button.active;
$window.alert(button.name);
}
}]);
Plunker
http://plnkr.co/edit/Dg10cXqFxEKgEt7jWQ7Z?p=preview

Angular ng-repeat, Jquery fail

So I am using ng-repeat to repeat some divs which show images out of my JSON file. What I want to do is that when I click on that image (whether its desktop or mobile) the image will scale. Now my problem is that when I want to create a click event on my image tag (which is inside that div that holds the ng-repeat), he doesn't do anything. He cant see the click.
I red something on the internet about issues with jquery and angular, but for me as a beginner its hard to understand what I have to do to make it work how I pleased. I just want to be able to put a jquery function on a image tag inside the ng-repeated divs, so I can manipulate the css from there.
I have a piece of the code posted below here, maybe I have to add something to my controller? I dont know, I am clueless at the moment. :-)
<section class="words">
<div class="colored-sidebar"></div>
<!-- content -->
<div class="previous-button"></div>
<div class="word-container" ng-controller="imageController as imageCtrl">
<h1><span>noun</span>words</h1>
<div class="category-body">
<p><span>noun</span>travel</p><hr>
<div class="category-section" ng-repeat="icon in imageCtrl.imageList.travel">
<!-- <div class="category-image" ng-include="icon.src"></div> -->
<div class="category-image">
<img src="{{icon.src}}" />
</div>
</div>
</div>
</section>
The angular file
(function() {
app.controller('imageController', function(){
this.imageList = imageJson;
});
var imageJson = {
//ALOT OF JSON DATA HERE//
};
})();
I hope this piece of code would be enough to help me :-)
Any tips are welcome, I love to learn this language better and also understand it better.
Thanks!
jQuery is not suitable here, because by the time you run your jQuery code inside jQuery.ready(), the elements in "category-image" class are not created yet.
For solution of your problem you can use two methods:
1) Use the "ng-click", as proposed before. You can also pass "$index" to function inside ng-click. This way you will know index of icon in imageList.travel that was clicked. But this way you will have no information about dom element.
2) Create a directive. The main difference between directives and controllers is that directive have information about dom object. You can treat element as typical jQuery object
JS:
app.directive('imageClick', [function () {
return {
link: function (scope, element, attr) {
element.on("click", function(e){
//do some stuff here
})
}
}
}]);
HTML
<section class="words">
<div class="colored-sidebar"></div>
<!-- content -->
<div class="previous-button"></div>
<div class="word-container" ng-controller="imageController as imageCtrl">
<h1><span>noun</span>words</h1>
<div class="category-body">
<p><span>noun</span>travel</p><hr>
<div class="category-section" ng-repeat="icon in imageCtrl.imageList.travel">
<!-- <div class="category-image" ng-include="icon.src"></div> -->
<div class="category-image">
<img image-click src="{{icon.src}}" />
</div>
</div>
</div>
</section>

Is it possible to access a function in a directive from a view?

I'm fairly new to AngularJS and trying to learn by doing.
There is a function in a directive I'm looking to access from the view. What I have in my HTML file is
<div collapse class="collapsed" ng-click="toggle()" ></div>
What's going on there is the toggle() function should be called on click and change the class to expanded, effectively changing the background image described in the CSS. toggle() is inside the collapse directive.
It doesn't seem to be accessing it though and I'm not sure why. Is there another way to do this or actually access said directive from the view? Could you explain why it's not accessing it?
Could this question possibly help? 15672709, it leads to this fiddle and goes beyond in case you nest your directives like below:
<div ng-controller="MyCtrl">
<div screen>
<div component>
<div widget>
<button ng-click="widgetIt()">Woo Hoo</button>
</div>
</div>
</div>
</div>

How to expand DIVs the "angular way" (using angular masonry)

I'm trying to expand a DIV element on my angular layout. I'm using angular-masonry to give a mason-style to my layout, but now I need to expand those boxes on click. I've tried a lot of stuff, but it kept overlapping my others elements. Soon figured out that I'll have to write it the "angular way" so I don't run into DOM manipulation conflicts.
Here's my code:
<div class="row" masonry>
<div
class="masonry-brick item-component col-sm-4 col-md-4"
ng-repeat="component in components.components | filter : components.filterByFilter | filter : searchText"
ng-click=" // expand #expandable // "
>
<div class="component-wrapper">
<div class="component">
<img ng-src="#{{ component.thumb }}"/>
</div>
<div class="component">
#{{ component.name_en }}
</div>
</div>
<div id="expandable" class="expand-me codes-wrapper">
<p>XXX</p>
<p>YYY</p>
<p>ZZZ</p>
</div>
</div>
</div>
Here's what I want to accomplish in the "angular way": http://codepen.io/desandro/pen/daKBo
In your example (http://codepen.io/desandro/pen/daKBo) if you click on an element there are two things that will be done:
(1) the style of the clicked item is changed
(2) the function masonry is called on the container element that keeps the divs.
I can't see such a function in angular-masonry pre builded. So i'll guess you have to do this by your self. Here are some hints how to solve this (i havn't try it in real)
Bind a function to ng-click. In this function set a state to the current component. This state shoud be used to toggle the css-class of the element. you can use ng-class for this.
The second part is little bit more complex. I would suggest write a direcive 'masonry-change-listener' and bind it to the element that is bound to the same element with the directive masonry. If you click on a component $emit an event, that something has changed. In the directive 'masonry-change-listener' listen to this event. if this event fires you have to call $element.masonry.apply($element) in the link function.

Conditional logic in AngularJS template

I have an angular template which looks like this...
<div ng-repeat="message in data.messages" ng-class="message.type">
<div class="info">
<div class="type"></div>
<div class="from">From Avatar</div>
<div class="createdBy">Created By Avatar</div>
<div class="arrowTo">
<div class="arrow"></div>
<div class="to">To Avatar</div>
</div>
<div class="date">
<div class="day">25</div>
<div class="month">Dec</div>
</div>
</div>
<div class="main">
<div class="content">
<div class="heading2">{{message.title}}</div>
<div ng-bind-html="message.content"></div>
</div>
</div>
<br />
<hr />
<br />
</div>
I have set up a JSfiddle to show the data being bound.
What I need to do is make the "from", "to" and "arrowTo" divs show conditionally, depending on the content of the data.
The log is is this...
If there is a "from" object in the data then show the "from" div and bind the data but don't show the "createdBy" div .
If there is no "from" object but there is a "createdBy" object then show the "createdBy" div and bind the data.
If there is a "to" object in the data then show the "arrowTo" div and bind it's data.
Or in plain English, if there is a from address, show it, otherwise show who created the record instead and if there is a to address then show that too.
I have looked into using ng-switch but I think I'd have to add extra markup which would leave an empty div if there was no data. Plus I'd need to nest switch directives and I'm not sure if that would work.
Any ideas?
UPDATE:
If I were to write my own directive (If I knew how!) then here is some pseudo code to show how I would want to use it...
<div ng-if="showFrom()">
From Template Goes Here
</div>
<div ng-if="showCreatedBy()">
CreatedBy Template Goes Here
</div>
<div ng-if="showTo()">
To Template Goes Here
</div>
Each of these would disappear if the function/expression evaluated to false.
Angular 1.1.5 introduced the ng-if directive. That's the best solution for this particular problem. If you are using an older version of Angular, consider using angular-ui's ui-if directive.
If you arrived here looking for answers to the general question of "conditional logic in templates" also consider:
1.1.5 also introduced a ternary operator
ng-switch can be used to conditionally add/remove elements from the DOM
see also How do I conditionally apply CSS styles in AngularJS?
Original answer:
Here is a not-so-great "ng-if" directive:
myApp.directive('ngIf', function() {
return {
link: function(scope, element, attrs) {
if(scope.$eval(attrs.ngIf)) {
// remove '<div ng-if...></div>'
element.replaceWith(element.children())
} else {
element.replaceWith(' ')
}
}
}
});
that allows for this HTML syntax:
<div ng-repeat="message in data.messages" ng-class="message.type">
<hr>
<div ng-if="showFrom(message)">
<div>From: {{message.from.name}}</div>
</div>
<div ng-if="showCreatedBy(message)">
<div>Created by: {{message.createdBy.name}}</div>
</div>
<div ng-if="showTo(message)">
<div>To: {{message.to.name}}</div>
</div>
</div>
Fiddle.
replaceWith() is used to remove unneeded content from the DOM.
Also, as I mentioned on Google+, ng-style can probably be used to conditionally load background images, should you want to use ng-show instead of a custom directive. (For the benefit of other readers, Jon stated on Google+: "both methods use ng-show which I'm trying to avoid because it uses display:none and leaves extra markup in the DOM. This is a particular problem in this scenario because the hidden element will have a background image which will still be loaded in most browsers."). See also How do I conditionally apply CSS styles in AngularJS?
The angular-ui ui-if directive watches for changes to the if condition/expression. Mine doesn't. So, while my simple implementation will update the view correctly if the model changes such that it only affects the template output, it won't update the view correctly if the condition/expression answer changes.
E.g., if the value of a from.name changes in the model, the view will update. But if you delete $scope.data.messages[0].from, the from name will be removed from the view, but the template will not be removed from the view because the if-condition/expression is not being watched.
You could use the ngSwitch directive:
<div ng-switch on="selection" >
<div ng-switch-when="settings">Settings Div</div>
<span ng-switch-when="home">Home Span</span>
<span ng-switch-default>default</span>
</div>
If you don't want the DOM to be loaded with empty divs, you need to create your custom directive using $http to load the (sub)templates and $compile to inject it in the DOM when a certain condition has reached.
This is just an (untested) example. It can and should be optimized:
HTML:
<conditional-template ng-model="element" template-url1="path/to/partial1" template-url2="path/to/partial2"></div>
Directive:
app.directive('conditionalTemplate', function($http, $compile) {
return {
restrict: 'E',
require: '^ngModel',
link: function(sope, element, attrs, ctrl) {
// get template with $http
// check model via ctrl.$viewValue
// compile with $compile
// replace element with element.replaceWith()
}
};
});
You can use ng-show on every div element in the loop. Is this what you've wanted: http://jsfiddle.net/pGwRu/2/ ?
<div class="from" ng-show="message.from">From: {{message.from.name}}</div>

Categories