Tuesday, December 29, 2015

Dropdown and Popover services in AngularStrap

As per a previous post, I have been using AngularStrap to combine the strength of AngularJS with the beauty of Bootstrap widgets. I think it is fair to say that it has not been a smooth ride. Part of the reason is the API docs for AngularStrap are far from complete or even correct.

Today, I was trying to incorporate a dropdown (a context menu, really) and ran into a case where the documentation does not show how exactly to use the $dropdown service (to launch the popup from the controller).

Looking at the source, I found that there is a show() method similar to other services in their collection. However, this did not quite work and I ended up with a runtime error:

TypeError: Cannot read property 'link' of undefined

Googling around I found this plunk that supposedly showed the correct way of using the $dropdown service. However, this did not help my situation and I was left with the same error.

Scratching my head some more and looking around, I found this post about using the $popover service, which works in the same way as the $dropdown one. It suggested that $promise has to be used rather than just calling show() directly. It was unclear what $promise exactly is, and again looking at the source for the tooltip module, which is the "base" for the dropdown module, it turns be a property computed from the Angular compilation of bootstrap content. Looking further, the $bsCompiler module does a bunch of stuff with the template asynchronously (using the $q service). So that was the explanation for why I needed to show the dropdown in this way:

    dropdown.$promise.then(function () {
dropdown.show();
   });

This did work and my dropdown (context menu) did show and work properly.

Sunday, December 13, 2015

Bazillion New Technologies

Here are slides of a talk I gave today. The talk aims to give the attendees a broad overview of "hot" technologies in various fields of IT. The fields are by no means extensive, as time was limited. 

Thursday, November 26, 2015

Drag and Drop with Angular

In my last post, I implemented drag and drop with clone using JQuery. Now, it was time to integrate it into an application I am building, and thus had to "angularize" the implementation.

There are many drag and drop implementations on Angular out there.

The Angular docs include a simple example on directives that add/handle event listeners to implement dragging. Unfortunately, it is pretty rudimentary and it does not support cloning the draggable.

This one by Ben Parker builds from scratch on top of angular without using JQuery UI. It is an interesting approach but it is more pedagogical than anything else since it misses out on all the goodies provided by JQuery UI and its plugins.

This one by Amit Gharat combines the power of JQuery UI and Angular declarative clarity. However, it was too constraining for my needs. To use it, the drag source and drop target both have to have an underlying model and all manipulation happens to the model. If you needed to further manipulate to the dropped object, you would have to write some custom handlers. Besides, I found some oddities when trying it out, like behaving in a strange way if you did not add certain attributes.

Other implementations such as ngDraggable had similar issues.

After this tour, I decided to roll my own. I did not need something generic so I wrote a narrow solution that fit my needs. The code is basically the same that I posted in my last post but wrapped in directives and controllers.

angular.module('coach.field', [])
.directive("cloneDraggable", function() {
return {
link: function(scope, element, attrs, controller) {
element.draggable({ helper:'clone', 
                                                          revert: 'invalid', 
                                                          cursor: "crosshair"});
}
}
})
.controller("soccerFieldCtrl", function(tacticsService, $scope) {
$scope.idCounter = 0;
this.nextId = function() {
return $scope.idCounter++;
};
})
.directive("soccerField", function(tacticsService) {
return {
templateUrl: "field/field.html",
controller: "soccerFieldCtrl",
                link: function(scope, element, attrs, controller) {
          element.droppable({
drop: function(ev, ui) {
if (! ui.helper.hasClass("toolbar-gadget"))
return;

var dropped=$(ui.draggable).clone();
var newId = "player" + controller.nextId();
dropped.attr("id", newId);

var pos=$(ui.helper).offset();

var newX = pos.left- element.offset().left;
var newY = pos.top - element.offset().top;

dropped.css({"left":newX,"top":newY, "z-index": 100});
dropped.removeClass("toolbar-gadget");
dropped.addClass("player");

$(this).append(dropped);
       
                dropped.draggable({ cursor: "crosshair", 
                                                                          containment: 'parent'});
}
     });
}
}
});


Sunday, November 22, 2015

jQuery UI Drag & Drop with "Clone"

The problem I was trying to solve is how to drag an element, make a clone on drop, then make the dropped element draggable with different parameters. Seemed like a simple task, given the basics that are built into jQuery UI. It turns out that there a lot of posts out there that give part of the answer, but I have not found one that gives a complete example. Marcos Placona came close with his example, but it seemed overly complex and it also had an interesting bug:
  1. Drag one of the widgets on the left and drop in the grid (fine).
  2. Drag the widget you just dropped (fine).
  3. Now, drag any widget on the left to an invalid place, so as to force an animation back, and see what happens!
There seems to be some phantom association between the previously dropped element and the draggable element used by jQuery UI, either through direct reference or an ID.

I was about to adopt his code, but having found this bug I thought I should try on my own, given all the examples I have seen. I ended up building a much simpler working example with half the code.

A few notes on the code:
  1. The condition in the beginning of the drop() function filters out the case where you drag a widget that has already been dropped in the drop area.
  2. I set the position of the dropped element to "absolute" so that it can float inside the drop area.
  3. To calculate the drop position within the drop area, you need to calculate the offset of the dropped element from its parent, rather than from the page origin (which are the coordinates you get in the event).
  4. Note that you have to call css() on element before you append it or else the position may not take effect!
  5. Finally, for the code nazis out there, I deal with the repetition in the CSS via SASS when I develop locally! 

Tuesday, November 3, 2015

Padding and Margins with Bootstrap Grid

While constructing a page using the Bootstrap grid system, I noticed some extra padding in one of the rows. Documentation said that each column is given a 15px horizontal padding to create a gutter. It also said that rows have a -15 margin on both ends to offset this effect for the first and last columns.

This is all fine. However, I still noticed an extra padding even though my rows and columns seemed to be nested properly. After trying to remove various pieces from the HTML, it turns out that any element with the "container" or "container-fluid" classes adds yet another 15px horizontal padding. Removing the extra container fixed my issue.

Wednesday, October 28, 2015

AngularStrap and AngularMotion

Yesterday, I wrote about using AngularStrap. Today, I managed to get going with it and implement a couple of components. However, I faced a problem with the $alert service, where the alert was not animating in or out. The docs on the web site are very brief with only one example. I looked around for examples and I ran across a couple of people complaining that animation is not working for them either, but no solution worked for me (including ngAnimate) did not help. After looking through the source code it still was not clear what was going wrong. I searched some more and came across AngularMotion which is a framework for building animations in Angular. On that site (which happens to be maintained by the same developer), I found an example that uses AngularStrap. It turns out that all I needed was to include the AngularMotion CSS, which solved the problem. There is no mention whatsoever on the AngularStrap site so I reported a documentation bug.

Summary: to use AngularStrap you must include the AngularMotion CSS.

Tuesday, October 27, 2015

Angular JS and Bootstrap Together

I started a project using Angular JS and needed some nice widgets so I incorporated Bootstrap. I included the NavBar component from Bootstrap, only to realize that it was no good without the corresponding JavaScript bits that are needed to switch the highlight to the selected nav page. I needed to add code like this:

    $('ul.nav li a').click(function(e) {

        var $parent = $(this).parent();

        $parent.siblings().removeClass('active');

        $parent.addClass('active');

    });

I felt that this was not right. With such two popular frameworks, why did I have towrite code for such a basic task?

After a quick search, I found what seems to be the answer. There are two frameworks that combine the strength and cleanliness of Angular with the beauty of Bootstrap: Bootstrap UI and AngularStrap. The former seems to have more following but feature-wise they look similar. Both allow you to use Bootstrap components without the need to go around Angular declarative directives and write JQuery code. They provide Angular directives that wrap Bootstrap component so you can get the best of both worlds. 

For my needs, I will probably go with AngularStrap since they have a nav bar component whereas Bootstrap UI does not. 
  

Saturday, October 24, 2015

My Slides on Slideshare

I have now shared slides for my previous presentations on slideshare. The first, I gave in Summer 2013 and the second I gave in Summer 2015, both at the Faculty of Engineering, Alexandria University.

Thursday, October 22, 2015

With the merging of Blogger and Blogspot and the Google transition, I am not sure what happened to my old blog posts. Anyways, I am starting a new blog today. I intend to keep it a technical blog, but I cannot promise that I won't rant about politics and other stuff too!