ng-model is not updated in ng-if - angularjs

I'm failing to retrieve the actual value of my selected option (ng-model=filter) in my select html bloc.
Note that if I don't set $scope.filter then $scope.filter stay undefined even if I select an option in the dropdown list.
My html template, filters.html :
<div ng-if="!showFilterTemplatePlz">
<button class="btn btn-primary" ng-click="showFilterTemplate()" type="button">Add Filter</button>
</div>
<div ng-if="showFilterTemplatePlz" class="inline">
<button class="btn btn-primary" ng-click="closeFilters()" type="button">Close filters</button>
<label>filters</label>
<select ng-model="filter" ng-options="filter.name for filter in filterOptions" class="form-control">
</select>
<p>{{filter.name}}</p>
<button ng-click="addFilter()" id="addFilterButton" class="btn btn-primary btn-sm btn-default" type="button"><i class="fa fa-plus"></i> Add</button>
</div>
In my directive:
.directive('filters', ['retrieveStaticDatasService','usersService', function(datasService, usersService) {
return {
restrict: 'E',
scope: false,
controller: function ($scope) {
/* init */
$scope.filterOptions = [
{name: "languages"},
{name: "nationality"},
{name: "location"}
];
$scope.filter = $scope.filterOptions[0];
/* end init */
$scope.showFilterTemplate = function(){
$scope.showFilterTemplatePlz=true;
}
$scope.closeFilters = function(){
$scope.showFilterTemplatePlz=false;
}
$scope.addFilter = function(){
console.log("filter to add : " + $scope.filter.name);
}
},
templateUrl: '/partials/components/filters.html'
}
}])
Result :
I can see the actual value appears in my html but i will always show "languages" for$scope.filter.name in my console, whatever option I selected! I took a screenshot to show you : http://hpics.li/4a37ae3
Thanks for your help.
[Edit : I made some test and my model are setted and updated only if they are not inside the "<div ng-if="..."></div>"]. Is it not allowed to put a ng-if directly in the directive?
Answer : Angularjs ng-model doesn't work inside ng-if

I found the solution, the problem was that ng-if creates a child scope. So I changed filter to $parent.filter in
<select ng-model="$parent.filter" ng-options="option.name for option in filterOptions" class="form-control">
</select>

I think you are confusing it by having to objects named 'filter'. You might be overwriting the ng-options object rather than your controller one.
Try changing to
<select ng-model="filter" ng-options="option.name for option in filterOptions" class="form-control"></select>
<p>{{filter.name}}</p>
<button ng-click="addFilter()" id="addFilterButton" class="btn btn-primary btn-sm btn-default" type="button"><i class="fa fa-plus"></i> Add</button>

Related

evaluate expression in scope

I am trying to create a directive for my date pickers here is the relevant code.
usage
<date-input open="openCalendar = true">
<input ng-model="ctrl.model.theDate"
uib-datepicker-popup="dd-MMMM-yyyy"
is-open="openCalendar">
</date-input>
directive
angular
.module('common.directives')
.directive('dateInput', function() {
return {
restrict: 'E',
transclude: true,
scope: {open:'&'},
template: 'date-input-template',
};
});
template
<script type="text/ng-template" id="date-input-template>
<div class="input-group">
<ng-transclude></ng-transclude>
<div class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="open()">
<i class="glyphicon glyphicon-calendar"></i>
</button>
</div>
</div>
</script>
This works once. If I try to open the calendar a 2nd time, the date picker does not display. I'm sure I'm doing something wrong, just not sure what it is.
Any suggestions or guidance is appreciated.
You should call the function with the expression
ng-click="open()">
Your expression for ng-click needs to invoke open:
ng-click="open()"
Edit: You may want to use an object like data.openCalendar = true to avoid prototypal scope inheritance issues, or move the openCalendar = true expression into a function and invoke that function.

How can I add uib-tooltip from within an attribute directive?

Instead of adding uib-tooltip="tooltip text" to the element I want to add only a tooltip attribute.
In the tooltip directive I want to do something along the lines of "on mouseenter if a condition is met then show my full text content in uib-tooltip"
You can use tooltip-enable.
JS
.controller("ctrl", function($scope){
$scope.isToolTipEnabled = false;
$scope.toggleToolTip = function(){
$scope.isToolTipEnabled = !$scope.isToolTipEnabled;
}
});
HTML
<div ng-controller="ctrl">
<div class="label label-info" class="btn btn-default"
tooltip-enable="isToolTipEnabled"
uib-tooltip="This is a conditional tooltip">Conditional Tooltip here</div>
<button type="button" class="btn btn-default" ng-click="toggleToolTip()" ng-class="{'btn-success': isToolTipEnabled, 'btn-danger': !isToolTipEnabled}">Tooltip is {{isToolTipEnabled ? 'enabled' : 'disabled'}}</button>
</div>

Angular UI datepicker popup open without ng-click

According to official Angular UI documentation for datepicker users in popup style I need to create additional button with ng-click event on it to change $scope property which was binded to is-open attribute like so:
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="popup1.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
In my application it is possible to have more than 10 such datepickers per view so I need to implement property for is-open attribute per each.
Is there any way to open datepicker popup without is-open attribute?
If you have +10 datepickers and repeat the same markup over and over, and need to create $scope functions without any real purpose - then it is almost screaming for a directive to do the trivial tasks! The markup you are repeating can be placed in a template :
<script type="text/ng-template" id="dateAutomater.html">
<input type="text" class="form-control"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default">
<i class="glyphicon glyphicon-calendar"></i>
</button>
</span>
</script>
The directive (the very basics) could look like this :
.directive('dateAutomater', ['$compile', function($compile) {
return {
transclude: true,
templateUrl: 'dateAutomater.html',
restrict: 'AE',
link: function ($scope, element, attrs) {
$scope.dateInfo = $scope.dateInfo || {};
var dateInfo = $scope.dateInfo,
input = element.find('input'),
button = element.find('button'),
name = input.name || 'date'+Object.keys($scope.dateInfo).length,
info = {
open: false,
click: function() {
this.open = true
}
}
dateInfo[name] = info;
input.attr('ng-model', attrs.dateAutomater);
input.attr('uib-datepicker-popup', 'dd-MMMM-yyyy');
input.attr('is-open', 'dateInfo[\"'+name+'\"].open')
button.attr('ng-click', 'dateInfo[\"'+name+'\"].click()');
$compile(element.contents())($scope);
}
}
}])
It simply takes the model as argument, injects the markup from the template and bind the important variable is-open and ng-click function to a self maintained object, $scope.dateInfo. Usage
<p class="input-group" date-automater="dt"></p>
<p class="input-group" date-automater="date"></p>
<p class="input-group" date-automater="yesterDay"></p>
...
demo -> http://plnkr.co/edit/H6hgYdF420R4IKdjCBGM?p=preview
Now expand the directive / template to set other default properties you want on the datepicker, like min-date and so on.

Angularjs getting the button value

I need to get the button id value using angularjs.I have written the code but i am getting the value as "undefined".So please suggest me the code which i need to use.
My code is:
<button type="button" class="btn btn-primary ole" data-toggle="tooltip" id="buttonvalue" name="rdoResult" value="SUN" ng-click="addvalue()" ng-model="testDate23" data-placement="left" data-original-title="this is a left tooltip">
SUN
</button>
<button type="button" ng-value="2" class="btn btn-primary ole two" data-toggle="tooltip" ng-click="addvalue()"
ng-model="testDate23" value="MON" id="buttonvalue" data-placement="top" data-original-title="this is a top tooltip">
MON
</button>
Script code:
$scope.addvalue = function() {
var datevaluee=$scope.testDate23;
}
You need to pass the $event in ng-click function like below..
ng-click="addvalue($event)"
below will be the function implementation
$scope.addvalue = function(element) {
$scope.testDate23 = element.currentTarget.value; // this will return the value of the button
console.log( $scope.testDate23)
};
you can try this.
<button ng-click="addvalue(testDate23='SUN')">
button
</button>
or
<button ng-click="addvalue(testDate23='MON')">
btn2
</button>
Find fiddle Fiddle example
Hope it will help.
Button is an input for submitting data, not setting data.
You need use directive.
Live example on jsfiddle.
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope) {
$scope.getValue = function() {
$scope.value = $scope.testDate23;
}
});
myApp.directive('buttonValue', function() {
return {
restrict: 'A',
scope: {
buttonValue:"="
},
link: function(scope, element, attr) {
scope.$watch(function(){return attr.ngValue},function(newVal){
scope.buttonValue = newVal;
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<input ng-model="btnValue">
<button ng-value="{{btnValue}}" ng-click="getValue()" button-value="testDate23">
MON
</button>
<br>
<pre>testDate23= {{testDate23}}</pre>
<pre>value= {{value}}</pre>
</body>
If you want to set hardcoded value then use ng-init
<button type="button" ng-init="value=2" class="btn btn-primary ole two" data-toggle="tooltip" ng-click="addvalue()"
ng-model="testDate23" value="MON" id="buttonvalue" data-placement="top" data-original-title="this is a top tooltip">
MON
</button>
You can use value aywhere in your same template.

Broadcast from directive to controller

I am trying to modify a form in a view by means of a button in my directive's template (template is in another file), this is for a basic CRUD where each item has a delete/edit button.
In order to avoid replicating the form I decided to that on edit's click a function would send the item to the controller in questions in order to be updated with the new information.
But I've been having troubles making the connection, so far I tried changing $root, to $rootScope back and forth and using , $broadcast or $emit.
So how can I send the function onChange my item's information based on the template's button click?
Template:
<strong>{{item.type}}</strong> {{item.description}}
<div class="material-switch pull-right">
<button type="button" class="btn btn-warning btn-circle" ng-show="item.editable" ng-click="onChange()">
<span class="glyphicon glyphicon-edit" ></span>
</button>
<button type="button" class="btn btn-danger btn-circle" ng-controller="View1Ctrl" ng-show="item.editable" ng-click="EliminarItem(item)">
<span class="glyphicon glyphicon-minus" ></span>
</button>
<input ng-model="item.isDone"
id="someSwitchOptionDefault{{itemIndex}}"
name="someSwitchOption001{{itemIndex}}"
type="checkbox" />
<label for="someSwitchOptionDefault{{itemIndex}}" class="label-info"></label>
</div>
Directive:
'use strict';
angular.module('myApp.items.directive', [])
.directive('itemSwitch', [ function() {
return {
restrict: 'A',
scope: {
item: '=',
itemIndex: "="
},
templateUrl: 'templates/itemSwitchTemplate.html',
link : function($scope){
$scope.$broadcast('onChange', item);
}
}
}]);
Controller
.controller('View1Ctrl', ['$scope','itemsService',function($scope,itemsService) {
$scope.items = itemsService.getItems();
$scope.classMap = {GROCERIES:"success",CAR:"danger",UNIVERSITY:"warning",PAYMENTS:"info"};
$scope.newItem = {};
$scope.$on('onChange', function(event, args) {
if ($scope.btnEdit) {
$scope.newItem = args;
} else {
$scope.newItem = {};
}
});
$scope.enableEdit = function (item) {
item.editable = true;
};
$scope.disableEdit = function (item) {
item.editable = false;
};
}]);
View
<div class="col-xs-12">
<div ng-model="currentItem" ng-repeat="item in items" item-switch item="item" item-index="$index" class="notice notice-{{classMap[item.type]}}" ng-mouseover="enableEdit(item)" ng-mouseleave="disableEdit(item)">
</div>
<!-- FORMULARIO -->
<form name = "myForm" class="form-horizontal">
<fieldset>
<div id="legend">
<legend class="">Task</legend>
</div>
<div class="control-group">
<!-- Name-->
<label class="control-label">Name</label>
<div class="controls">
<input type="text" name="itemName" ng-model="newItem.name" placeholder="Task Name" class="input-xlarge" ng-required="true" >
<p class="help-block"></p>
</div>
</div>
<div class="control-group">
<!-- Description -->
<label class="control-label">Description</label>
<div class="controls" >
<input type="text" ng-model="newItem.description" placeholder="Task Description" class="input-xlarge">
<p class="help-block"></p>
</div>
</div>
<div class="control-group">
<!-- Button -->
<div class="controls">
<a class="btn icon-btn btn-success" ng-disabled="myForm.$invalid" ng-click="addOrSaveItem()">
<span class="glyphicon btn-glyphicon glyphicon-save img-circle text-success"></span>Save</a>
</div>
</div>
</fieldset>
</form>
</div>
FiddleJS
Look nd Feel
Using "onChange" as an event name is a poor choice as it is likely to conflict with other events with that name. My recommendation is to use the directive's name as part of the event name.
In your directive
angular.module('myApp.items.directive', [])
.directive('itemSwitch', function() {
return {
restrict: 'A',
scope: {
item: '=',
itemIndex: "="
},
template: '<button ng-click="doIt()">Do It</button>',
link : function(scope){
scope.doIt = function() {
scope.$emit('itemSwitch.doIt', scope.item, scope.itemIndex);
};
}
}
});
In your controller
$scope.doItItems = [];
$scope.$on("itemSwitch.doIt", function(item, itemIndex) {
doItItems.push(item);
});
In this example, on each click of the Do It button, an item is pushed to the doItItems list.
In your itemSwitch directive, you can do
$rootScope.$broadcast('onChange', item);
And then you can pick it up in any scope that is listening (in this case, your controller), with
$scope.$on('onChange', function(event, args) { ... }
This works because $broadcast moves downward from parent to children, while $emit moves upward from child to parents.
So for example, $rootScope.$emit would only be picked up by $rootScope.$on since $rootScope has no parents, while $scope.$broadcast would only be available to that scope's children and not to $rootScope.$on, since $rootScope is not a child of $scope

Resources