Saturday, May 14, 2016

AngularJS with dynamically generated content

This post is about using Angular with dynamically loaded markup (in this particular case it was an entire form).

TL;DR

The problem

Angular can only interact with elements present at its bootstrap time, so it doesn’t know about new generated content from an ajax call for example.


The (real-life) situation is like this, we have a main page that has a button which makes an ajax request when clicked to get the form contents and inject the generated HMTL into a <div> element, no Angular, just jQuery, and yeah, I know it’s not cool, but I’ve mixed Angular and jQuery to solve this problem.
This was a template that must not be changed, and to complicate things further, the toolbar that has the “New entry” button is included with the items grid which - as you guessed- loads with an ajax request after the main page loads (document ready event) ..

The options here are to:
  1. Add the script tags to the form file (template).
  2. Add them to the toolbar that has the “New entry” button.
  3. Add them to the main page.

I’ve chosen the third approach because of the drawbacks of the other two approaches.

The first approach will result in multiple inclusions of the scripts, because every click on the button will inject the HTML of the form template which includes the scripts every time.

The second approach has the same problem, plus, Angular won't see the form as it shows because it loads dynamically, so no advantage here.

The third approach made most sense, it includes the scripts one time when the user clicks the menu entry to open that page, so the only problem here is making angular deal with the newly generated content.


The solution

  • Include the Angular and the app (module) scripts into the main page.
  • Assign the module and the main controller to the wrapper div which also contains a hidden textarea with the ngChange calling the function “recompile”, which we going to define in a moment.
  • After the Ajax request that gets the form contents, insert the generated markup into the textarea and invoke the change event, something like:
    $("#formcontent").val(data).change();

  • At the “mainCtrl” controller, define the “recompile” function:
$scope.recompile = function(){
              $("#the-form-div").html($compile($scope.formcontent)($scope));
              $scope.formcontent = "";
            }



As the Angular documentation mentions about the $compile service:

Compiles an HTML string or DOM into a template and produces a template function, which can then be used to link scope and the template together.

And: The compilation is a process of walking the DOM tree and matching DOM elements to directives.

So basically what’s happening here is the we tell Angular to inspect whatever is inside of the textarea and work its magic on it before showing that to the user.


That’s it, thanks.

Tuesday, April 19, 2016

ngOptions that works - Populating select boxes using AngularJS

A quick post explaining how to populate select box using AngularJS with the values that you really want, I’ve found that the way to achieve that is not too obvious in the official documentation for ngOptions.


Let’s assume that you’ve got your data from the server or whatever, it’s a JSON array that looks like this

items = [
          {“itemId”:1, “itemName”:”Item 1”},
          {“itemId”:2, “itemName”:”Item 2”}
        ];


You can populate the values by using this syntax

<select ng-options="item.itemId as item.itemName for item in items">
  <option value="">Choose an item</option>
</select>


This way the value will be the “itemId” and “itemName” is the displayed name.

The empty option is used as a placeholder.

That’s it.

Tuesday, April 12, 2016

Sending emails through gmail smtp - Laravel

In this post I'm going through a couple of problems that I've faced while trying to send emails from my web application hosted on Google Cloud (Compute engine), the Laravel version that I'm using is 4.2 .

Swift_TransportException: 

Expected response code 250 but got code "", with message "" 

This Exception was thrown because the port that was used was 465 instead of 587.
Apparently, Google Cloud supports only port 587 by default for outgoing mail, maybe because port 465 is legacy now.
Here's the Stack Overflow link for you: 
http://stackoverflow.com/questions/15796530/what-is-the-difference-between-ports-465-and-587

Solution: Use 587 as the port.



Swift_TransportException:

Expected response code 250 but got code "535", with message "535-5.7.8 Username and Password not accepted. Learn more at...

This error is sent by Google refusing to log in to your account from an untrusted app (yours :D ).

Solution: Enable two-step verification for your google account (the one that's used to send the emails in your web app).
 if you haven't already, go to https://www.google.com/landing/2step/ and hit "Get started" button and follow the instructions to set it up.
  • After everything is set up, open "App-specific passwords" tab.
  • click "Manage application-specific passwords".
  • From there, open the "select app" select box and choose "Other".
  • Type a descriptive name for you app and hit "Generate".
  • You'll be given a password, copy that to your Laravel mail configuration file (config/mail.php) in the password entry and you're good to go.
Here's how the configuration file looks like

return array(
    'driver' => 'smtp',
    'host' => 'smtp.gmail.com',
    'port' => 587,
    'encryption' => 'tls',
    'username' => 'your_gmail_username',
    'password' => 'your_generated_password',
    'sendmail' => '/usr/sbin/sendmail -bs',
    'pretend' => false,
);

That's it, your app should now be able to send emails without problems.