Saturday, August 25, 2012

Is AngularJS very easy that there are very few blogs documenting it?

This app demonstrates how easy it is to structure your model with AngularJS and the UI automatically follow what happens to model seamlessly.

Live demo: http://jsfiddle.net/bpvjL/6/


HTML:

<div ng-app>
  <h2>Todo</h2>
  <div ng-controller="TodoCtrl">


    <ul class="unstyled">
      <li ng-repeat="todo in todos" >       
          <span class="isactive-{{activeTodo == todo}}" ng-click="changeActive(todo)" >
              <a href="#" style="none">{{todo.text}}</a>
          </span>
      </li>
    </ul>
      
      

    <form ng-submit="addTodo()">
      <input type="text" ng-model="todoText"  size="30"
             placeholder="add new todo here">
      <input class="btn-primary" type="submit" value="add">
    </form>
          
      <ul>
      <li ng-repeat="step in activeTodo.steps">
          {{$index + 1}} {{step.text}}
      </li>   
      </ul>

    <form ng-submit="addStep()">
      <input type="text" ng-model="stepText"  size="30"
             placeholder="add new step to: {{activeTodo.text}}">
      <input class="btn-primary" type="submit" value="add">
    </form>
          
  </div>
</div>
​


Javascript:

function TodoCtrl($scope) {
  $scope.todos = [
    {text:'learn angular',
     steps : [
         { text : 'learn' },
         { text : 'to' },           
         { text : 'build' }                    
     ]},
    {text:'build an angular app',
     steps : [ 
         { text : 'build' },         
         { text : 'and' }, 
         { text : 'they' }, 
         { text : 'will' }, 
         { text : 'come' }
     ]}
  ];
    
  $scope.activeTodo = $scope.todos[0];
  

  $scope.addTodo = function() {
        var todo = {text:$scope.todoText, steps: []};
        $scope.todos.push(todo);
        $scope.todoText = '';
        $scope.activeTodo = todo;
  };
    
  $scope.changeActive = function(active) {
        $scope.activeTodo = active;
  };


  $scope.addStep = function() {
        $scope.activeTodo.steps.push({text:$scope.stepText});
        $scope.stepText = '';
  };
    

}

​


CSS:

<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
<style>
.isactive-true {
    background: YELLOW;
}

Tuesday, August 7, 2012

jQuery selector nuances

​<select name="Beatle" id="Beatle"​​​​​​​​​​​​​​​​​​​​​​​>
    <option id=9>John</option>
    <option id=8>Paul</option>
    <option id=7 selected>George</option>
    <option id=6>Ringo</option>
</select>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​


<script>
 var selectedText = "";

 selectedText = $('#Beatle').text();
 alert('Not Correct: ' + selectedText);

 selectedText = $('option:selected','#Beatle').text();
 alert('Correct: ' + selectedText);

 selectedText = $('#Beatle:selected').text();
 alert('Not correct: ' + selectedText);

 selectedText = $('#Beatle :selected').text();
  alert('Correct: ' + selectedText);

});
</script>


Live test: http://jsfiddle.net/5XAYh/

Monday, August 6, 2012

Manual model and view synchronization with JavaScript

When you don't use an MVC javascript framework that can do two-way databinding, you are left on your own to synchronize the state between the model and the view.


Live test: http://jsfiddle.net/E7F7D/33/

<table id='dataHere' border='1'>
<thead>
    <!-- ID presence on HTML is optional --> 
    <!--<th style='visibility: collapse'>Id</th>-->
    
    <th>Lastname</th><th>Firstname</th><th>Asset</th><th>Actions</th>
</thead>
<tbody>
</tbody>
    
    
</table>

<hr/>

    <input type="button" id="viaModel" value="test via model"/>

    <input type="button" id="viaDom" value="test via DOM"/>
        
    <div id="debugLog">
     </div>


<script>
    var people = [
        { Id: 1, Lastname: 'Lennon', Firstname: 'John', Asset: 40090.1296 },
        { Id: 2, Lastname: 'McCartney', Firstname: 'Paul', Asset: 38000.34 },
        { Id: 3, Lastname: 'Harrison', Firstname: 'George', Asset: 37000.56 },
        { Id: 4, Lastname: 'Starr', Firstname: 'Ringo', Asset: 40000.78 },
        
        { Id: 5, Lastname: 'Buendia', Firstname: 'Ely', Asset: 40000.93 },
        { Id: 6, Lastname: 'Marasigan', Firstname: 'Raymund', Asset: 39000.12 },
        { Id: 7, Lastname: 'Zabala', Firstname: 'Buddy', Asset: 39000.13 },
        { Id: 8, Lastname: 'Adoro', Firstname: 'Marcus', Asset: 39000.14344 }    
        
        ];
    
    
    var tbl = $('#dataHere');
    
    $('#viaModel').click(function() {
        var dl = $('#debugLog');
        dl.html('Model-based approach. Data are in their pristine state');
        $.each(people, function() {
            var person = this;
            
            dl.append(person.Id + '-->' + person.Lastname + ', ' + person.Firstname + ': ' + this.Asset + '<p/>');
        });
    });
    
    $('#viaDom').click(function() {
        try {
            var dl = $('#debugLog');
            var tbl = $('#dataHere');
    
            dl.html('Using DOM approach for stashing and extracting data, the Id won\'t be available unless we stash it on HTML');
            
            
    
            var trs = $('tbody > tr', tbl);
    
    
            $.each(trs, function() {
                var tr = this;
                var tds = $('td', tr);
        
                // Alas! ID won't be available if you didn't put it in td tag or whatnot.
                // If we use DOM approach for stashing our data, 
                // we have to stash ID on the HTML itself, 
                // this is called Smart UI approach, this is abhored, 
                // as the code is tightly coupled to UI/presentation layer.
                
        
                var tdLastname = $(tds[0]);
                var tdFirstname = $(tds[1]);                       
                var tdAsset = $(tds[2]);
                
                
                
                var lastname = tdLastname.text();
                var firstname = tdFirstname.text();
                // another problem with Smart UI approach is the data gets mangled
                
                var asset = tdAsset.text().replace('Php ','').replace(',','');
                
                dl.append(lastname + ', '+ firstname + ': ' + asset);                
                
                
            })
                
            dl.append('And data gets mangled and truncated too. Check the rounding-offs');
        
        } catch(e) {
            alert(e);
        }
                       
    });
    
    
    $.each(people, function() {
        var person = this;
        
        var trPerson = $('<tr/>');
    
        
        // optional
        // var tdId = $('<td/>').css('visibility','collapse');
        
        var tdLastname = $('<td/>');
        var tdFirstname = $('<td/>');
        var tdAsset = $('<td/>');
        var tdActions = $('<td/>');
        
        var aEditAction = $('<a/>').prop('href','#').text('Edit');
        var aDeleteAction = $('<a/>').prop('href','#').text('Delete');
        
       
        // optional
        // tdId.text(this.Id);
        
        tdLastname.text(this.Lastname);
        tdFirstname.text(this.Firstname);
        tdAsset.text(formatMoney(this.Asset, 2, 'Php ', ',','.'));
    
        
        aEditAction.click(function() {
            // alert('Will edit ' + person.Id + ' ' + person.Lastname);
            
            
            var ln = prompt('Enter value', person.Lastname);
            person.Lastname = ln == null ? person.Lastname : ln;
            
            tdLastname.text(person.Lastname);
        });        
        
     
        
        aDeleteAction.click(function() {
            if (confirm('Will delete ' + person.Id + ' ' + person.Lastname)) {   
                
                people.splice(people.indexOf(person),1);
                
                trPerson.remove();
            }
        });
        
            
    
        
        trPerson
            // .append(tdId) // optional
            .append(tdLastname).append(tdFirstname)
            .append(tdAsset).append(tdActions);
        
        tdActions.append(aEditAction).append('|').append(aDeleteAction);
    
      
          
        tbl.append(trPerson);
    
            
    });
    
     // alert(8);
        // alert(formatMoney(92334.55, 2,'Php ',',','.'));
        
    // http://www.josscrowcroft.com/2011/code/format-unformat-money-currency-javascript/
    function formatMoney(number, places, symbol, thousand, decimal) {
        number = number || 0;
        places = !isNaN(places = Math.abs(places)) ? places : 2;
        symbol = symbol !== undefined ? symbol : "$";
        thousand = thousand || ",";
        decimal = decimal || ".";
        var negative = number < 0 ? "-" : "",
            i = parseInt(number = Math.abs(+number || 0).toFixed(places), 10) + "",
            j = (j = i.length) > 3 ? j % 3 : 0;
        return symbol + negative + (j ? i.substr(0, j) + thousand : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousand) + (places ? decimal + Math.abs(number - i).toFixed(places).slice(2) : "");
    }
</script>

Monitoring perfect attendance. Consecutive calendar date query

Postgresql implementation for Monitoring perfect attendance


create table tx
(
i serial not null primary key,
n varchar(10), d date,
constraint ux_tx unique(n,d)
);

insert into tx(n,d) values
('john','2012-7-3'),
('john','2012-7-5'),
('john','2012-7-6'),
('john','2012-7-9'),
('john','2012-7-12'),
('john','2012-7-13'),
('john','2012-7-16'),
('john','2012-7-17'),
('john','2012-7-18'),
('john','2012-7-20'),
('john','2012-7-30'),
('john','2012-7-31'),

('paul','2012-7-3'),
('paul','2012-7-5'),
('paul','2012-7-18'),
('paul','2012-7-19'),
('paul','2012-7-20'),
('paul','2012-7-23'),
('paul','2012-7-24'),
('paul','2012-7-25'),
('paul','2012-7-26'),
('paul','2012-7-27'),
('paul','2012-7-30'),
('paul','2012-7-31'),
('paul','2012-8-1'),
('paul','2012-8-3'),
('paul','2012-8-6'),
('paul','2012-8-7');

create table holiday(d date);

insert into holiday(d) values
('2012-7-4');


-- Monday logic sourced here: http://www.ienablemuch.com/2010/12/finding-previous-day-of-week.html




with first_date as
(
--  select dateadd( ww, datediff(ww,0,min(d)), 0 ) as first_date -- get the monday of the earliest date

select previous_date_of_day(min(d), 1) as first_date 
from tx 
)
,shifted as
(
 select
  tx.n, tx.d, 
     
  (tx.d - fd.first_date) - ( (tx.d - fd.first_date) / 7 * 2 ) as diff
   
 from tx
 cross join first_date fd
 union
 select
  xxx.n, h.d, 
   
  
  (h.d - fd.first_date) - ((h.d - fd.first_date) / 7 * 2) as diff
  
 from holiday h 
 cross join first_date fd
 cross join (select distinct n from tx) as xxx
)
,grouped as
(
 select
  *
  , diff - row_number() over(partition by n order by d) as grp
 from shifted
)
select
     
    -- remove staging columns from the output...
    -- * 
     
    -- ...just output what the user will see:
    d, n
     
 ,dense_rank() over (partition by n order by grp) as nth_streak
 ,count(*) over (partition by n, grp) as streak
from grouped
where d not in (select d from holiday)  -- remove the holidays



Get the previous day of the date if the day is not exactly on that date:
create or replace function previous_date_of_day(the_date date, dow int) returns date
as
$$
select
    case when extract(dow from $1) < $2 then
        $1 - ( extract(dow from $1) + (7 - $2) )::int
    else
        $1 - ( extract(dow from $1) - $2)::int
    end;
$$ language 'sql';

Sunday, August 5, 2012

AngularJS dropdown list

How not to do dropdown list:

 
<select ng-model="anotherLocationId">
    <option ng-repeat="location in locations" value="{{location.LocationId}}">{{location.LocationName}}</option>
</select>  


Although that can also populate the dropdown list, the UI won't be able to respond to initial model value. When you use that, even anotherLocationId has value, the dropdown will still show you blank item on its initial load.


Should do this instead:


<select ng-model="locationId" ng-options="item.LocationId as item.LocationName for item in locations"></select>



Complete code:


View
<div ng-controller="TheController" ng-app>

Location:     
<select ng-model="locationId" ng-options="item.LocationId as item.LocationName for item in locations"></select>
   
    <p/>
 
Another Location:    
<select ng-model="anotherLocationId">
    <option ng-repeat="location in locations" value="{{location.LocationId}}">{{location.LocationName}}</option>
</select>    
    
    <hr/>
    
    Location: {{locationId}} <br/>
    Another Location: {{anotherLocationId}}
    
    
</div>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​


Model
​function TheController($scope) {
    
    $scope.locations = [
        {LocationId : 7, LocationName : 'Philippines' },       
        {LocationId : 8, LocationName : 'Canada' },
        {LocationId : 9, LocationName : 'China' } ];

    $scope.locationId = 8;
    $scope.anotherLocationId = 8;
}​

Live test: http://jsfiddle.net/ELkn6/1/


Here's how to add empty value with ng-options. The second dropdown, which uses ng-repeat instead of ng-options, won't have a way to receive null value.


<div ng-app="demoApp" ng-controller="MainController as c">
<select ng-model="c.locationId" ng-options="item.LocationId as item.LocationName for item in c.locations">
  <option value>All</option>
</select>
   
    <p/>
 
Another Location:    
<select ng-model="c.locationCode">
    <option value>Unknown value</option>    
    <option>Doesn't work, ng-model can't receive null</option>    
    <option ng-value="null">Doesn't work, ng-model can't receive null too</option>
    <option value=''>Like null, empty string is a good value to denote all. However, empty string won't work on data type like number or date</option>
    <option value="ALL">Literal ALL, not a good value, as it can be a valid country code value, any code value for that matter</option>
    <option ng-repeat="location in c.locations" value="{{location.locationCode}}">{{location.LocationName}}</option>
</select>    

<hr/>

Location Id: {{c.locationId}}, Is Empty: {{c.locationId == null}}<br/>
Location Code: {{c.locationCode}}, Is Empty: {{c.locationCode == null}}

</div>

----



angular.module('demoApp', [])
 .controller('MainController', MainController);

function MainController() {
  
    this.locations = [
        {LocationId : 7, locationCode : 'CAD', LocationName : 'Canada' },       
        {LocationId : 8, locationCode : 'PHL', LocationName : 'Philippines' },
        {LocationId : 9, locationCode : 'CHN', LocationName : 'China' } 
    ];

    this.locationId = 8;
    this.locationCode = 'PHL';    
        
}



Live test: https://jsfiddle.net/d38ypmx0/


Happy Coding!