Can not reflect changes in model - angularjs

I have an AngularJS controller like this
var myModule = angular.module('test', []);
myModule.controller('PendataanMainController',
function($scope, $http)
{
$scope.stat1 = "";
$scope.stat2 = "stat2";
$scope.AmbilStat1 = function()
{
$scope.stat1 = "Hallo stat1";
};
$scope.AmbilStat2 = function()
{
$scope.stat2 = "Hallo stat2";
}
}
);
myModule
.directive(
'fonloaded',
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.bind(
'click',
function()
{
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
}
);
scope.$apply();
}
}
}
);
and a HTML like this
<div ng-app="test" ng-controller="PendataanMainController" class="columns clear bt-space15">
<div class="col1-2 fl-space2">
<div class="content-box">
<div class="box-body">
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
{{AmbilStat2()}}
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
<div class="col1-2 lastcol">
<div class="content-box">
<div class="box-body">
<div class="box-header clear">
<h2>Stat 2</h2>
</div>
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
</div>
I made a custom directive 'fonloaded' and put it on this part:
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
and when it got clicked, the directive link function change the value of stat1. I want that new stat1 reflected in the html. But it don't.
I am using AngularJS extension for Chrome. And I can see that the stat1 value has changed, but that change does not reflected on the html.
What would be the problem?

http://plnkr.co/edit/4kiJwrG8Xo0unrNc9Fw7?p=preview
please see fixed sample:
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.on('click',
function(e)
{
// alert('1');
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
scope.$apply();
} );
}
}
}

I must add that I am working within a HTML template (like one from theme forest). I have stripped the ng-app part out of the HTML template to make it stand alone. And now it reflect the changes in the model.
Somehow the HTML template using jQuery to modify the tag which has fonloaded directive. As result {{stat1}} lost by HTML template transformation.

Related

Bootstrap DateTimePicker with AngularJS: Passing directive ngModel value to controller

I am trying to get angular bootstrap datetimepicker input value using a custom directive like shown below. I am able to get the value in directive. How can i access this directive scope value in angular controller.
HTML
<div class='input-group date' id='datetimepickerId' datetimez ng-model="dueDate" >
<input type='text' class="form-control" />
</div>
Controller
App.directive('datetimez', function(){
return {
require: '?ngModel',
restrict: 'A',
link: function(scope, element, attrs, ngModel){
if(!ngModel) return;
ngModel.$render = function(){
element.find('#datetimepickerId').val( ngModel.$viewValue || '' );
};
element.datetimepicker({
format : 'YYYY-MM-DD HH:mm:ss'
});
element.on('dp.change', function(){
scope.$apply(read);
});
read();
function read() {
var value = element.find('#datetimepickerId').val();
ngModel.$setViewValue(value);
console.log(scope.dueDate);
}
}
};
});
App.controller('myController', ['$scope', function($scope) {
console.log($scope.dueDate);
}]);
Log inside the directive prints value successfully. But log inside controller does not.
Here you go.
I checked the documentation of the bootstrap datetimepicker and come to know there are two implementation.
One with a icon to click and show the datetimepicker
Another one just with a textbox without a icon
For the first one, you have to use the parent div to initiate the plugin and for the second option, you have to use the textbox to initiate the plugin
You have used the second option but used the parent div to initiate the plugin with a directive.
Also, div elements won't support the ngModel, hence directive should be used with the input element.
I have extended your directive to handle both scenarios and also the date format and other options can be passed from the controller.
You can give a try with the below working snippet.
var App = angular.module('App', []);
App.directive('datetimez', function(){
return {
require: '?ngModel',
restrict: 'A',
link: function(scope, element, attrs, ngModel){
if(!ngModel) return;
ngModel.$render = function(){
element.val( ngModel.$viewValue || '' );
};
function read() {
var value = element.val();
ngModel.$setViewValue(value);
//console.log(scope.dueDate);
}
var options = scope.$eval(attrs.datetimez) || {};
if(element.next().is('.input-group-addon')) {
var parentElm = $(element).parent();
parentElm.datetimepicker(options);
parentElm.on('dp.change', function(){
scope.$apply(read);
});
} else {
element.datetimepicker(options);
element.on('dp.change', function(){
scope.$apply(read);
});
}
read();
}
};
});
App.controller('myController', ['$scope', function($scope) {
$scope.datePickerOptions = {
format : 'YYYY-MM-DD HH:mm:ss'
};
$scope.$watch('dueDate1', function(){
console.log($scope.dueDate1);
});
$scope.$watch('dueDate2', function(){
console.log($scope.dueDate2);
});
}]);
angular.bootstrap(document, ['App']);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/css/bootstrap-datetimepicker.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.16.0/moment.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/js/bootstrap-datetimepicker.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="container" ng-controller="myController">
<div class="row">
<div class='col-md-6'>
<div class="form-group">
<div class='input-group date' id='datetimepicker1'>
<input type='text' class="form-control" datetimez="datePickerOptions" ng-model="dueDate1" />
</div>
</div>
</div>
</div>
<div class="row">
<div class='col-md-6'>
<div class="form-group">
<div class='input-group date' id='datetimepicker1'>
<input type='text' class="form-control" datetimez="datePickerOptions" ng-model="dueDate2" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</div>
</div>

How to apped a `directive` by click

Say, I have multiple directives, on click how can i add the "directive" what i want to "popup". i have number of scenario, but using only one pop-up for all. whenever the appropiate element is clicked, using the same pop-up, i would like to update the other 'directives'
for sample, i created only one. click on the red bordered title
here is my code :
<div ng-controller="main">
{{name}}
<div class="info" ng-click="popIt()">
<info-board></info-board>
</div>
<div class="popup" ng-if="show" ng-click="popIt()">
//add directive dynamically - how?
</div>
</div>
</body>
<script type="text/ng-template" id="modalPopup.html">
<div class='ng-modal'>
some information here.
</div>
</script>
js:
var myApp = angular.module("myApp", []);
myApp.controller("main", function($scope) {
$scope.name = "Mo!";
$scope.show = false;
$scope.popIt = function (show) {
$scope.show = !$scope.show;
}
})
myApp.directive("infoBoard", function() {
return {
replace : true,
templateUrl: "modalPopup.html",
scope : {},
link : function (scope, element, attrs) {
element.append("Hi there!")
}
}
})
Demo Onlie

Angular directive to append or delete a input element with unique id based on button click

I'm new to Angular and am trying to do this the Angular Way. I have a list with several <li> elements created using ng-repeat. In each <li> I have a button that increments a counter and one that decrements the counter and initially one <input type=text>. I want to append or remove a duplicate <input type=text> with a unique id corresponding to whether the increment or decrement button is clicked.
Here is the markup for the portion of my page template I'm working with
<ul class="list">
<li class="item" ng-repeat="resource in resources">
<div class="resource-desc">{{resource.description}}</div>
<img ng-src="{{resource.thumb}}">
<div class="incrementer" ng-controller="CounterCtrl">
<button id='{{$index}}' class="count-btn ion-arrow-up-b" ng-click="increment()" ng-init="count=0">
<span class="ion-plus-round"></span>
</button>
<div id='{{$index}}'class="count-btn count">{{count}}</div>
<button id='{{$index}}' class="ion-arrow-down-b" ng-click="decrement()">
<span class="ion-minus-round"></span>
</button>
</div>
<div id='{{$index}}' class="multi-name-input">
<div class="input_wrapper">
<input type="text" name="{{$index}}resource" required>
<label for="{{$index}}resource">Enter a Name to Display(ie Bay 1)</label>
</div>
</div>
</li>
</ul>
And my Counter controller
'use strict';
angular.module('myapp.controllers', ['ionic', 'ui.bootstrap'])
.controller('CounterCtrl', function($scope) {
$scope.decrement = function() {
$scope.count = $scope.count - 1;
if ($scope.count < 0){
$scope.count = 0;
}
};
$scope.increment = function() {
$scope.count = $scope.count + 1;
};
})
The input markup for the partial I want appended
<div class="input_wrapper">
<input id='{{$index}}' type="text" name="resource{{$index}}" required>
<label for="resource{{$index}}">Enter a Name to Display</label>
</div>
I think my directive should look something like this
.directive('bw-input-append',function($compile){
return {
templateUrl: '../../templates/partials/text_input.html',
transclude: true
link: function(scope, element){
element.click(function(){
element.parent().find('.multi-name-input').append($compile(template)(scope));
});
}
};
});
But I'm really not sure if that's right at all or what options I might need or want in the directive.
I was able to figure out how to write directives for both appending and removing input elements and attached those to their respective increment and decrement buttons on the counter.
Here is the markup for the buttons
<section ng-app="myApp" ng-controller="MainCtrl">
<addinputsbutton></addinputsbutton>
<removeinputsbutton></removeinputsbutton>
<div id="space-for-inputs"></div>
</section>
..and here are the directives.
var myApp = angular.module('myApp', []);
function MainCtrl($scope) {
$scope.count = 0;
}
//Directive that returns an element which adds inputs on click
myApp.directive("addinputsbutton", function(){
return {
restrict: "E",
template: "<button id='{{$index}}' class=\"count-btn ion-arrow-up-b\" ng-click=\"increment()\" ng-init=\"count=0\" addinputs><span class=\"ion-plus-round\">+</span></button>"
}
});
//Directive for adding inputs on click
myApp.directive("addinputs", function($compile){
return function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-inputs')).append($compile("<div class='resource-input'><input id='{{$index}}' type=\"text\" name=\"resource{{$index}}\" required><label for=\"resource{{$index}}\">Enter a Name to Display</label></div>")(scope));
});
};
});
//_______________________________________________________________________
//Directive that returns an element which removes inputs on click
myApp.directive("removeinputsbutton", function(){
return {
restrict: "E",
template: "<button id='{{$index}}' class=\"count-btn ion-arrow-down-b\" ng-click=\"decrement()\" ng-init=\"count=0\" removeinputs><span class=\"ion-minus-round\">-</span></button>"
}
});
//Directive for removing inputs on click
myApp.directive('removeinputs', function(){
return function(scope, element){
element.bind('click', function(){
document.getElementById('space-for-inputs').removeChild(document.getElementById('space-for-inputs').lastChild);
});
};
});
For some reason I couldn't get the 'addinput' and 'removeinput' directives to work in JSFiddle unless I added them to a custom button created from the element directives. But the 'addinput' and 'removeinput' elements worked fine in plain button tags in my application. Here is the JSFiddle in case any one is interested.

Get event information using angularjs

I am trying to track button clicks a user performs on a page/view. I am trying to get the element idwhich was clicked.
Is there a way to do this without having to tie a function or directive to each element?
ng-controller and ng-click
HTML:
<body ng-controller="MyController" ng-click="go($event)">
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
</body>
JS:
$scope.go = function(e) {
var clickedElement = e.target;
console.log(clickedElement);
console.log(clickedElement.id);
};
Demo: http://plnkr.co/edit/1O6pCVrvgu7Bl8b6TlPF?p=preview
Directive:
HTML:
<body my-directive>
<div id="box1">
<div id="box2">
<div id="box3"></div>
</div>
</div>
</body>
JS:
app.directive('myDirective', function () {
return {
link: function (scope, element, attrs) {
element.bind('click', function (e) {
var clickedElement = e.target;
console.log(clickedElement);
console.log(clickedElement.id);
});
}
}
});
Demo: http://plnkr.co/edit/eevnMFu2YBj3QqRraeZh?p=preview

What is the *Angular* way to get an elements siblings?

What is the idiomatic way to get an elements siblings when it is clicked using AngularJS?
So far I've got this:
<div ng-controller="FooCtrl">
<div ng-click="clicked()">One</div>
<div ng-click="clicked()">Two</div>
<div ng-click="clicked()">Three</div>
</div>
<script>
function FooCtrl($scope){
$scope.clicked = function()
{
console.log("Clicked", this, arguments);
};
}
</script>
here's a jQuery implementation as a concrete example:
<div id="foo">
<div>One</div>
<div>two</div>
<div>three</div>
</div>
<script>
$(function(){
$('#foo div').on('click', function(){
$(this).siblings('div').removeClass('clicked');
$(this).addClass('clicked');
});
});
</script>
Use a directive, since you want to traverse the DOM:
app.directive('sibs', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().children().removeClass('clicked');
element.addClass('clicked');
})
},
}
});
<div sibs>One</div>
<div sibs>Two</div>
<div sibs>Three</div>
Note that jQuery is not required.
fiddle
Here is an angular version of the jQuery sample that you provided:
HTML:
<div ng-controller="FooCtrl">
<div ng-click="selected.item='One'"
ng-class="{clicked:selected.item=='One'}">One</div>
<div ng-click="selected.item='Two'"
ng-class="{clicked:selected.item=='Two'}">Two</div>
<div ng-click="selected.item='Three'"
ng-class="{clicked:selected.item=='Three'}">Three</div>
</div>
JS:
function FooCtrl($scope, $rootScope) {
$scope.selected = {
item:""
}
}
NOTE: You dont strictly need to access DOM for this. However if you still want to then you can write a simple directive. Something like below:
HTML:
<div ng-controller="FooCtrl">
<div ng-click="clicked()" get-siblings>One</div>
<div ng-click="clicked()" get-siblings>Two</div>
<div ng-click="clicked()" get-siblings>Three</div>
</div>
JS:
yourApp.directive('getSiblings', function() {
return {
scope: true,
link: function(scope,element,attrs){
scope.clicked = function () {
element.siblings('div').removeClass('clicked');
element.addClass('clicked');
}
}
}
});
fiddle
Following is a directive built exclusively with Angular grammar (borrowing from jqLite):
link: function(scope, iElement, iAttributes, controllers) {
var parentChildren,
mySiblings = [];
// add a marker to this element to distinguish it from its siblings
// this could be a lot more robust
iElement.attr('rsFindMySiblings', 'anchor');
// get my parent's children, it will include me!
parentChildren = iElement.parent().children();
// remove myself
scope.siblings = [];
for (var i=0; i < parentChildren.length; i++) {
var child = angular.element(parentChildren[i]);
var attr = child.attr('rsFindMySiblings');
if (!attr) {
scope.siblings.push({name: child[0].textContent});
}
}
}
Note that it uses a controller to store the results. See this plunker for a detailed example

Resources