Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading Partial Page With Angular and Compile The Controller

In large scale application, our web application might be organize into separate partial page in order to increase the modularity of our application. In some case compiling a partial page loaded through XHR or Ajax request either using Angular $http.get or JQuery $.load will introduce an error.

Using my scenario as example, exactly I'm using Kohana PHP framework so i can control the modularity of my web application on the server level. As usual all template and page been separate into view, leaving all HTML, JS and CSS together on presentation layer.

This will giving an great flexibility for me to implement the Javascript MVW/MVC stack on client side processing as my web app are heavily depend on AJAX request to fetch data from back end application. In my scenario im using AngularJS and below is a simple pseudo on how the data from Model presented to client.

Kohana Model > Kohana Controller > Kohana View > XHR > JQuery\Angular > DOM

One of my part in my application that really give me bump and get me drink few bottles of metabolism drink to solve the application. Is where i have a Modal dialog and the partial page are load through XHR from server and attached it to selected DOM.

The problem is when Angular try to compile the partial page, when it found the ng-controller directive it will be looking for the function referring to the processed directive. Error were produce where the controller is not found as it not yet evaluated by DOM parser. But when you pre-delare the function somewhere in your application just before your load the partial page, everything is OK. Below is the example on how i setup a Dialog service that will be called from link directive when i clicked the said link.

var dialogService = angular.module('dialog.service', []);
dialogService.factory('Dialog', function($http,$compile){
    var dialogService = {};
    dialogService.load = function(url, scope){
        $("#dialog:ui-dialog").dialog( "destroy" );
        $("#dialog").attr('title','Atlantis');

        $http.get(url).success(function (data) {
            html = $compile(data)(scope);
            $('#dialog-content').html(html);

            $("#dialog").dialog({
                width: '600px',
                buttons: {
                    "Ok": function() {
                        $( this ).dialog( "close" );
                        return true;
                    },
                },
                close: function(){
                    if (typeof (onClose) == 'function') { onClose(); }
                },
            });
        });
    }

    return dialogService;
});

After some research i have found some solution and sharing it with fellas on my answer for others beginner like me. (sorry for my English).

like image 211
Azri Jamil Avatar asked Oct 20 '12 09:10

Azri Jamil


1 Answers

There's nothing wrong on AngularJS on this setup, others JS guru out there might already know the solution and very busy to share with us while inventing another cool web development tools or framework. That's OK keep doing that. This might not be a cool or ultimatum solution, please share any improvement or tips with us!

To overcome this problem we need s strategy to setup, let me start with an example code so our brain will digest while the information flowing through. Below code is the placeholder where i create the the modal dialog using JQuery and the Ajax content will be insert.

<div ng-app="asng" id="dialog" title="" style="display:none">
     <div id="dialog-content"></div>
</div>

As a basic knowledge, we have to understand on how DOM parser are working. We might think that DOMP (DOM Parser) is a multi-threaded and that's the reason we can load multiple external resource in parallel. Actually DOMP are single threaded while parsing the DOM elements index from top to bottom. Below is the example on the partial page that I'm gonna load into #dialog-content DIV element.

<script language="JavaScript" type="text/javascript">
    function Transaction ($scope,$http){
        $scope.items = [{"country":"VN","quantity":"100"}];
        $scope.country_name = $scope.items;
    }
</script>

<style>
</style>

<div id="transaction-panel" class="user" data-ng-controller="Transaction">
        <form id="{{ form_name }}" action="">
        Country : [[ items.country ]] </br>
        Total : [[ items.quantity ]]
    </form>
</div>

Actually these partial still giving an error, although we have put the script block just before the element with ng-controller directive. Actually that's not really the case, the part that we need to tackle is how AngularJS compile service are compiling the partial DOM. Let go back on my question part above and inspect where the line that we do the compile thing.

html = $compile(data)(scope);
$('#dialog-content').html(html);

First line above will compile DOM in data variable, and insert into the root DOM unfortunately first line will shout an error : Controller Transaction not found.

This happen because, the Script Block in your partial page is not yet evaluate by DOMP parser because is not inserted into the root DOMP. Now you see the light OK, so we have to change the compiling strategy a bit, by inserting the new DOM and then we will parse back the inserted DOM look example below:-

html = $('#dialog-content').html(data);
$compile(html)(scope);

Slim and simple solution, it took me few head banging morning to solve this problem just because ignoring the simple concept on DOM parsing.

like image 131
Azri Jamil Avatar answered Oct 25 '22 00:10

Azri Jamil