Dynamic layout using angularjs (the angularjs way) - angularjs

I just recently started tinkering with AngularJs, so my question is fairly basic. For concreteness's sake, let me start with the minimal setting: a page with banner, body and footer. The goal is to make banner and footer stay at the top and bottem of the page (css position: fixed) and the body will fill in the rest.
If I were to use jQuery, I could set position: fixed for body, and listen to window's resize event to determine where I should put my body.
Presumably, I could do the same in AngularJs. But I've read many places that one shouldn't try to manipulate the layout from the code, since it's not in the philosophy of AngularJs. So, what's the best way to achieve this in the AngularJs way? Thanks.

Dynamic layout usually refers to a layout that changes depending upon the current route. Routing is how AngularJS associates a URL with a Controller and data. It becomes an important part of an app once you go beyond just 1 page.
Multiple Views
If you use something like ui-router (http://angular-ui.github.io/ui-router/) it allows you to define views that can have child views.
In your example, the header, body and footer would all be child views of a parent view. Each view would have it's own template, controller and data. When routing changes you can have just one or more of those view change, while the other views remain the same.
Binding Events
You can do all the same event bindings in AngularJS as you can in jQuery. The difference is the life-cycle of those bindings.
If you bind to the click event for a directive using the directive's element, then you don't have to worry about that binding when the directive is destroyed. AngularJS will remove the binding automatically.
When you bind to something like the window.resize event, then you have to be more careful.
link: function($scope, $el, $attrs) {
// this doesn't have to be unbind
$el.bind('click',function(e) {
// do stuff
});
// this has to be unbind on destroy
angular.element($window).bind('resize.mybinding',function(e) {
// do stuff
});
$scope.$on('$destroy',function(){
angular.element($window).unbind('resize.mybinding');
});
});
I've seen a lot of AngularJS source code bind to window.resize when it wasn't necessary. Only you know when it's needed, but sometimes you can get away with just using a watcher.
link: function($scope, $el, $attrs) {
$scope.$watchGroup(function(){
return [$window.innerWidth,$window,innerHeight];
},function(values){
// do stuff
});
});
The above does the same thing as binding. It executes a closure function when the size of the window changes, but the difference is that it's triggered only during digest and not by the window resize event. This can sometimes be more efficient for performance. As the resize is relative to only when the directive is digested.
A lot of AngularJS applications use JavaScript code to manage the layout of an application. How much JavaScript code you use, and why you do it that way is up to you, but keeping layout separated from the JavaScript side has a lot of advantages. It's better to let CSS handle it.

Bootstrap provides a nice plug-in called Affix that does just that.
<div id="banner" data-spy="affix">Fixed Banner</div>
<div>Body</div>
<div id="footer" data-spy="affix">Fixed Footer</div>
More on affix here for example:
http://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-affix.php

Related

Default Content before Route Resolution

I'm re-working a website that has a very nested interface requiring several child views. Think of a shopping site, with paging, results and filters. What I'd like to do is render default content for said children views while the results are being resolved from the back-end. However, I can't find a way to insert default content past the first <ui-view>, which of course, makes sense.
To get around this, we are currently using $broadcast in the child state controllers. We moved the resolution out of the resolve event into the controller, which is working, but requires us to make all of our directives use $broadcast as well, or they don't work since the data isn't loaded before they are. It also seems like a very inelegant solution to the issue.
What's weird, is that when I move the resolve function into a child view, neither the parent nor it's siblings views load before the results child view loads. I can't understand that whereas I can understand children views not loading before the parent is resolved.
Is there a way we can work around this? Building in broadcasting into all aspects of our code base seems like an extremely poor practice. Is there a way to show children default content before the parent is resolved, or even render child states' views before their controllers are instantiated?
Use ng-cloak class:
https://docs.angularjs.org/api/ng/directive/ngCloak
The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.

Using AngularJS 1.2, how to animate items inside partials (when using single ng-view in your index file)?

I had an app that I got 80% through building a few weeks back and it uses a lot of jQuery to do animations. I stopped working on that and started rebuilding it from scratch using AngularJS.
I'm now at the point where I'd like to try to add some of the animations that I WAS using in the old app. When leaving from the "main page" to the "details page", I used to have one div go flying offscreen to the left, while the main table seemed to shrink upwards and sort of "merge with" a drop-down box that was coming into view for that new details page. (The table and the drop-down have essentially the same information, which is why the animation made sense. The drop-down allows them to jump from record to record, without having to go back to the main table to navigate.)
Anyways, the way I built this app in Angular is that it is working off of one index file with a single "ng-view" div in it. And then the router determines what template-page to pull in.
I see from the various tutorials out there on animating with AngularJS 1.2 that the "ng-view" fires off events that can be harnessed for animation. But that's ONLY if you're going to add your .css class to be animated to the div that holds the "ng-view".
<div class='whatever' ng-view>
and then your css would contain something like:
.whatever.ng-enter{
opacity: 0;
}
But what can I do if I want to animate the way various PIECES of a view template enter or leave the view? (Most are just divs that act as containers for small tables of data [ as divs, not tables].) I'm using "ng-repeat" in those templates to populate the tables, but I really don't want to animate any of the rows. That's about the only other directive that fires Angular's animation that I'm currently using in the templates.
You can set animations to different parts of your template in it's controller using Jquery itself.
myApp.controller('sampleController', function($scope, $timeout, $location){
$("#submit_button").click(function(){
$("#yourdiv").fadeOut();
$timeout(function() { $location.path('/newurl'); }, 3000);
});
});
Display all your animations then change the route. I have used $timeout so as to set a delay for displaying the animations.

Angularjs directive + do I need to $compile

I'm in need of some pointers in my landingPage-builder project. (i'm currently stuck!).
The main issue is as follows:
Each element in the template (like the h1 and the paragraph) has attached a directive. What I need to get the directive to do is: create a template of HTML with some other directives attached like ng-click, ng-options etc, keep the bindings to the model intact (currently far away from working), update the model when changed.
I'm not trying to append to, or replace the element the directive is on, but make a html-template and inserting it into the DOM (almost like another view) so that the model on the left can be updated from the "settings" box on the right.
The project can be viewed here: http://193.107.29.196/~stian123/landingPageV3/app/#/pagebuilder/2
You may need Allow-Control-Allow-Origin for Chrome: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi/related
I'm a bit confused about $compile and doesn't really know when I need to use this part of the directives api.
Any suggestions?
Thank you!
If I understood your question correctly, you want to dynamically create templates, some of which have Angular attributes in them, then attach them to the DOM.
First, to (hopefully) answer your question, about when to call $compile:
Whenever you load in HTML from outside Angular's template system (like trying to set $(element).html(myHtmlString)), you need to let Angular compile it before you attach it to the DOM. In other words:
elem.append($compile(yourHTMLString)(scope));
This lets Angular traverse the DOM and parse any directives and bindings and attach them to the provided scope. If you don't $compile, Angular has no idea about those intended bindings at all, the HTML is never read by Angular.
Second, I don't know how flexible you want your templates to be, but if they're relatively fixed, but with some fixed customizable options (text, color, font-size etc), you might be better off creating a directive for each 'view', with the view options bound to the scope of the directive. Then you can just change the fields on the scope of the directive in the panel on the right side, and the view will update directly. You wouldn't even have to use $compile in this case.
If you want the user to be able to manually add the template HTML code, you will have to compile the HTML as described above.

Handling in View vs Handling in Controller Extjs

When we working on a extjs MVC project, we can handle a view event either inside the view or controller. How do we decide what events been handled in the view and what events been handled in the controller. What is the best practice.
For example, I need to pop up a window when a button is clicked. Should the creation of the window goes to controller or resides in the container view object.
One thing you can do is utilize the control() method within your controller definition. For example, take this code,
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
},
'#editform button[action=save]': {
click: this.onClick
}
});
}
});
This is from the MVC example. You'll notice that it is using the ComponentQuery Language (http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.ComponentQuery) to select a set of Ext objects to assign the render() event. This is saying, for all panels in the viewport, call onPanelRendered() for the render() event. So in your case, you want to select some buttons. So I added another example that selects some form with an id of editform and then select the button that contains the property action = save. You can get very specific with these component queries. I would suggest reading ComponentQuery portion of the API to see further examples. This depends on how you are attempting to select the buttons.
If you are new to MVC maybe you should only listen to events in the controller, once you have developed a couple of projects and you've gained discipline and enjoyed the benefits of listening events in a controller then you would be able to mix it up (when to use a handler and when to use a controller is gonna become obvious).
E.g If you have a FormPanel and you are only gonna listen to a button's click perhaps it doesn't make much sense to create a controller just for that, but in the other hand if you have a grid with a toolbar, action buttons, itemClick's etc then it would be a good idea to have a controller for that view.
The fact that you are using MVC does not mean you are not allowed to use handlers anymore, but do it wisely.
Best regards.

Is their a better way for a controller to 'call' a directive

I have a directive that creates and manages a bootstrap modal dialog.
Currently I have the directive watch a boolean held on the controller. The controller can then set this to true to have the modal dialog display.
This seems kinda messy. Is there a better way?
The directive in action:
<modal trigger="shouldDisplayModal" title="{{modalTitle}}"
message="{{modalMessage}}" positiveclick="okClicked()"
negativeclick="closed()"
positivelabel="Ok" negativelabel="Cancel"/>
The watch in the controller of the directive:
// watch the trigger value. expected to be boolean
$scope.$watch('trigger',function(newValue, oldValue){
if (newValue)
{
// enable any disabled buttons
modalElem.find('button').removeClass('disabled');
// show the dialog
modalElem.modal('show');
}
else
{
// hide the dialog
modalElem.modal('hide');
}
});
Here is a working example: http://jsfiddle.net/rabidgremlin/Ya96z/31/
UPDATE: Here is a fixed up example that corrects some issues with multiple directives on a page: http://jsfiddle.net/rabidgremlin/sjbCJ/1/
I was going to suggest using ng-show inside your directive's template (this what the dialog component on the directive page does, along with a visible attribute that is just like your trigger attribute), but then I saw that you also need to enable some buttons before modifying the visibility.
So, I think what you have is fine, and I don't see it as messy. Either your directive has to $watch for something, or you could create the dialog when an event happens -- this seems to be what the $dialog service does that #pkozlowski mentioned in the comments. The latter would not need a trigger attribute.
I blogged about working with angular and bootstrap modals just a couple weeks ago.
My solution involves a service, all of the hide/show magic for the modal is handled by bootstrap's javascript, and angular just worries about the data.
http://willvincent.com/blog/angularjs-and-twitter-bootstrap-playing-nicely

Resources