Calling custom directive form controller - angularjs

I have written a custom directive to load 3 drop downs and handle the change event of the drop down inside the controller. On change of the dropdown value I am getting the changed value inside my controller function. I need to call my custom directive and pass the new value.
I am new to AngularJs custom directive. Please let me know how to invoke a directive from within the controller.
The view where I render the custom directive:
<div class="container">
<div class="main-header clearfix">
<div class="page-title">
<h3 class="no-margin">Search for a Host</h3>
</div>
<!-- /page-title -->
</div>
<div class="padding-md">
<div class="hostsFilters row">
<select ng-model="BU" ng-change="changeTheView()" class="form- control">
<option value="">BU</option>
<option value="1">BU1</option>
<option value="2">BU2</option>
<option value="3">BU3</option>
<option value="4">BU4</option>
</select>
<span>OR</span>
<select ng-model="Application" ng-change="changeTheView()" class="form-control">
<option value="">Application</option>
<option value="1">App1</option>
<option value="2">App2</option>
<option value="3">App3</option>
<option value="4">App4</option>
</select>
<span>OR</span>
<input type="text" class="form-text" ng-model="hostName.host_name" placeholder="Hostname">
</div>
</div>
<div class="row">
<i-data-grid></i-data-grid>
</div>
The code of my controller:
'use strict';
angular.module('angularFullstackApp')
.controller('MainCtrl', function ($scope) {
$scope.changeTheView=function(){
console.log('Came inside the Change the view function..');
//Now invoke the custom directive.
}
});
The code for my directive:
'use strict';
angular.module('angularFullstackApp')
.directive('iDataGrid', function () {
return {
templateUrl: 'app/iDataGrid/iDataGrid.html',
restrict: 'E',
link: function (scope, element, attrs) {
$(document).ready(function() {
$('#example').dataTable();
} );
}
};
});

You could take a use of attribute, & pass the values inside attribute for which you wanted to place a watch that would fire when any of the value changed
Markup
<i-data-grid watch-values="['BU', 'Application']"></i-data-grid>
Code
'use strict';
angular.module('angularFullstackApp')
.directive('iDataGrid', function () {
return {
templateUrl: 'app/iDataGrid/iDataGrid.html',
restrict: 'E',
link: function (scope, element, attrs) {
$(document).ready(function() {
$('#example').dataTable();
scope.$watch(attrs.watchValues,function(newVal, oldVal){
//newly changed value available here with same sequence
//as you passed ['BU', 'Application'] here, it fires fn when any of value changed,
//newVal[0] contains newly changed value of BU
//& newVal[1] will contain newly changed value of Application
//call what ever code on basis of this values
},true);
});
}
};
});

Related

AngularJS parent ng-model is not binding with custom directive when using ng-if in the template [duplicate]

This question already has answers here:
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
(3 answers)
Closed 4 years ago.
I am trying to create a custom directive to render dropdown(select).
app.directive("uiDropdown", function () {
return {
restrict: "E",
replace: true,
scope: {
'model': '=ngModel',
'readOnly':'=?'
},
templateUrl : 'template/dropdownTemplate.html',
link: function (scope, elem, attrs) {
}
};
});
the template is
<span ng-if="!readOnly">
<select ng-model="model" >
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
Html code to use the directive is
<ui-dropdown ng-model="region" read-only='readOnly'>
The plunker code is plunker
If I remove the code 'ng-if="!readOnly"' from the template file, it is working as expected. If I change from "ng-if" to "ng-show" it is working as well.
Am I missing something here? Actually, the directive is supposed to do much more functionality than the one shown in this example. I perfer to use ng-if instead of ng-show. Please help in resolving this issue.
It has to do with the fact that ng-if creates its own child scope and then you're using a primitive directly. ng-if will actually create a local model boolean that has no relation to the parent. This would be an issue with ng-if even if it weren't being used via a directive, too.
You can work around this by passing an object and reading/setting a value on that object. Here's a simple example showing that your ng-if issue is not related to the directive and then how you can fix this using an object:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.readOnly = false;
$scope.primitive = "1";
$scope.object = {
selectedValue: "1"
};
})
.directive('uiDropdown', function() {
return {
restrict: 'E',
templateUrl: 'dropdownTemplate.html',
scope: {
model: '=ngModel',
fieldName: '#',
readOnly: '=?'
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div>
<label>Readonly: <input type="checkbox" ng-model="readOnly" /></label>
</div>
<div>
<h1>ng-if with primitive - no directive</h1>
<h2>This will not work</h2>
<div>
Value: {{ primitive }}
</div>
<div ng-if="!readOnly">
<select ng-model="primitive">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
</div>
<div>
<h1>ng-if with object - directive</h1>
<h2>This will work</h2>
<div>
Value: {{ object.selectedValue }}
</div>
<ui-dropdown ng-model="object" read-only="readOnly" field-name="selectedValue"></ui-dropdown>
</div>
<script type="text/ng-template" id="dropdownTemplate.html">
<div ng-if="!readOnly">
<select ng-model="model[fieldName]">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
</script>
</div>
ng-if contains a statement that's either true or false
Try
<ui-dropdown ng-model="region" read-only='true'>

How to add conditional add html attribute?

How to add conditionally attribute in angularjs?
For example I only want to set the multiple attribute on a <select> if my component has a binding set to true. This means if the binding is not given the multiple attribute should not exist on my <select>.
The only solution I found was with ng-if.
You can achieve this by implementing a directive (aka ng-multiple) to handle the property multiple of the select element. The following snippet implements this solution. However, you may want to control the state of your model inside this directive, once the multiple prop will produce an array of selected values, the non-multiple will produce a single object, so this may cause an issue when switching between multiple and non-multiple.
angular.module('myApp', [])
.directive('ngMultiple', function () {
return {
require: ['select', '?ngModel'],
restrict: 'A',
scope: {
multiple: '=ngMultiple'
},
link: function (scope, element, attrs, ctrls) {
element.prop('multiple', scope.multiple);
scope.$watch('multiple', function (multiple) {
if(element.prop('multiple') != multiple){
// may be would be convenient change the ngModel
// to [] or {} depending on the scenario
element.prop('multiple', multiple);
}
});
}
};
})
.controller('myController', function ($scope) {
$scope.condition = true;
$scope.myOptions = [];
});
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-controller="myController">
<label>
<input type="checkbox" ng-model="condition" /> multiple?
</label>
<br>
<select ng-multiple="condition" ng-model="myOptions">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</select>
<br>
<tt>myOptions: {{ myOptions }}</tt>
</div>
if boolean condition is true then multiple, else not
<select ng-if="condition" ng-model="some.model" multiple></select>
<select ng-if="!condition" ng-model="some.model"></select>
<select ng-show="condition" ng-model="some.model" multiple></select>
<select ng-hide="condition" ng-model="some.model"></select>
controller,
$scope.condition = true

Angular JS Directive loaded in expression

I'm new to Angular, and have found a ton of resources about directives and nesting, but can't seem to get this simple example to work. So basically I am working on a tabset, I have an HTML template:
tabset.html
<div>
<ul>
<li ng-repeat="tab in tabset.tabs" ng-class="{active:tabset.current()==$index}">
<a href ng-click="tabset.current($index)">{{tab}}</a>
</li>
</ul>
<div>
<div ng-repeat="pane in tabset.panes">
<div ng-show="tabset.current()==$index">
{{pane.contents}}
</div>
</div>
</div>
</div>
And a search form template:
search-form.html
<div>
<form name="ytSearch" ng-submit="YTCtrl.submit()" novalidate>
<label for="search_box">Search For: </label>
<input id="search_box" ng-model="YTCtrl.searchString"/>
<br>
<label for="location">Location: </label>
<input id="location" ng-model="YTCtrl.location"/>
within
<input type="numeric" value="100" ng-model="YTCtrl.locationRadius" />
<select ng-model="YTCtrl.locationUnit">
<option value="ft">Feet</option>
<option value="m">Meters</option>
<option value="mi">Miles</option>
<option value="km">Kilometers</option>
</select>
<br>
<label for="search_order">Sort By: </label>
<select id="search_order" ng-model="YTCtrl.order">
<option value="relevance">Relevance</option>
<option value="date">Date</option>
<option value="rating">Rating</option>
</select>
<br>
<button id="search">
Search
</button>
</form>
</div>
And a simple app file with 2 directives to handle each of the templates:
app.js
(function() {
angular.module("JHYT", [])
.directive("sidebarTabset", function($compile) {
return {
restrict : 'E',
templateUrl : 'tabset.html',
controller : function($scope, $compile, $http) {
this._current = 0;
this.current = function(i) {
if (i != null)
this._current = i;
return this._current;
};
this.tabs = ['Search', 'Favorite'];
this.panes = [{
contents : "<search-form></search-form>"
}, {
contents : "Favorite Pane"
}];
},
controllerAs : 'tabset',
};
}).
directive("searchForm", function() {
return {
restrict : 'E',
templateUrl : 'search-form.html',
controller : function($scope, $compile, $http) {
this.searchString = '';
this.location = '';
this.locationRadius = '';
this.locationUnit = 'mi';
this.order = 'relevance';
this.submit = function() {
console.log("Submit");
};
},
controllerAs : 'YTCtrl',
}
});
})();
So as you can probably tell, the idea is to be able to send a JSON object into the tabset (through a service probably) and have it build out a dynamic tabset, that actually works exactly as I expected it to. What isn't working is that in the first tab, the content, which is <search-form></search-form> is not processed, and the tag is rendered as plain text in the content area.
Since this is a tabset, the "child" doesn't need anything from the "parent", the search form and the tab itself have no scope dependencies. I tried playing with the link and compile functions after seeing some examples of nested structures, but can't seem tog et them to work.
How can I process the content of that variable so that element directives are rendered using their templates?
EDIT:
#sielakos Gave me exactly what I was hoping for, a reusable method for doing this.
I added a directive to my module called compile, which adds a wrapper to allow me to use plain text:
.directive("compile", function($compile){
return {
restrict: 'A',
link: function(scope, element, attr){
attr.$observe("compile", function(str){
var compiled = $compile("<div>"+str+"</div>")(scope);
jQuery(element).replaceWith(compiled);
})
}
}
})
And I changed my tabset to use this directive:
<div>
<ul>
<li ng-repeat="tab in tabset.tabs" ng-class="{active:tabset.current()==$index}">
<a href ng-click="tabset.current($index)">{{tab}}</a>
</li>
</ul>
<div>
<div ng-repeat="pane in tabset.panes">
<div ng-show="tabset.current()==$index">
<div compile="{{pane.contents}}"></div>
</div>
</div>
</div>
</div>
You will need to compile your string using $compile service if you wish to use it as you would use template. Otherwise it will be treated as normal string and displayed as it is.
Here is example how to use it inside directive:
var compiled = $compile(str)(scope);
element.empty();
element.append(compiled);
If you wish you can look at this fiddle for more complex example:
https://jsfiddle.net/x78uuwp2/
Here I created simple compile directive that takes string compiles it and puts as element body with current scope.

Getting selected model in angular select directive

I've created a directive to display dropdowns (form selects).
However I cannot find a way to mark the selected option.
html form
<div content-selects ng-model="ctrl.contentSelects.riskStatus" selection="oneWMS.riskStatusId"></div> <!-- oneWMS.riskStatusId -->
directive
function contentSelects(){
return {
restrict: 'AE',
templateUrl: '/app/Directives/contentSelects.tpl.html',
replace:true,
scope: {
ngModel: '=',
selection: '='
},
controller:function($scope){
},
link: function (scope, element, attrs) {
scope.selectedModel = scope.ngModel[attrs.selection];
scope.isChanged = function () {
//console.log("changed");
}
element.removeAttr('id');
}
};
}// end function contentSelects
This is where I don't understand: the directive template
<div class="input-group">
<select id="{{id}}">
<option value="model.refId" ng-repeat="model in ngModel track by model.refId" ng-model="ngModel[selection]" >{{model.value}} *** {{selection}} *** {{ngModel[selection]}}</option>
</select>
</div>
In the actual value, {{ngModel[selection]}} gives me exactly what I want (the target model row), but when tied to ng-model it doesn't retrieve anything :/
ng-model="ngModel[selection]"
What is wrong with it? Using curly brackets break it of course...
Your problem is because of ngModel included in option element. You should move it to select element.
Demo
<div class="input-group">
<select ng-model="selectedModel" >
<option ng-value="model" ng-repeat="model in ngModel">{{model}}
</option>
</select>
</div>
And also look at https://docs.angularjs.org/api/ng/directive/ngOptions

How to create a directive for disable all elements into div element

how to create a directive for disable all elements into div element ?
something like this :
<div div-disabled div-disabled-condition="state=='Stack'||state=='Over'||state=='Flow'">
<input type="text"/>
<input type="url"/>
<div>
<input type="text"/>
<input type="url"/>
</div>
<div>
Is it possible? I have no idea .
angular
.module('uiRouterApp.ctrl.add', ['uiRouterApp.ctrl.customDirective'])
.controller('addCtrl', [
'$scope',
'$location',
'$stateParams',
'$state',
function ($scope, $location, $stateParams, $state) {
$scope.state = {};
}
]).directive('divDisabled', function () {
return {
scope: {
divDisabledCondition: '#'
},
link: function (scope, element, attrs) {
}
};
});
Update :
please see this :
<div class="col-sm-12 ng-isolate-scope" selected-object="SelectedAutoComplete" local-data="requirements.Item1" search-fields="NameFa,NameEn" title-field="NameFa" minlength="2" field-required="true" image-field="ImageUrl" disable-auto-compelete="response.State=='Success'||response.State=='Error'||response.State=='Warning'">
<div class="angucomplete-holder">
<input id="_value" ng-model="searchStr" type="text" placeholder="select" class="form-control ng-dirty" ng-focus="resetHideResults()" ng-blur="hideResults()" autocapitalize="off" autocorrect="off" autocomplete="off" ng-change="inputChangeHandler(searchStr)" ng-disabled="response.State=='Success'||response.State=='Error'||response.State=='Warning'" style="">
<!-- ngIf: showDropdown -->
</div>
</div>
directive :
.directive('contentsDisabled', function() {
return {
compile: function(tElem, tAttrs) {
var inputs = tElem.find('input');
for (var i = 0; i < inputs.length; i++) {
inputs.attr('ng-disabled', tAttrs['disableAutoCompelete']);
}
}
}
})
why When the state is 'Success' or 'Error' or 'Warning' Input not disabled ?
You can create a directive that alters its content during compile time by adding the condition. Something along these lines (untested):
module.directive('contentsDisabled', function() {
return {
compile: function(tElem, tAttrs) {
var inputs = tElem.find('input');
inputs.attr('ng-disabled', tAttrs['contentsDisabled']);
}
};
});
See a JSFiddle here: http://jsfiddle.net/HB7LU/6380/
This has the drawback that you just copy the expression from contents-disabled into ng-disabled attributes of any inputs - if somebody uses a directive that in turn creates <input> elements, you won't pick them up.
It'd be less fragile to get hold of the FormController instance and iterate through all its controls, but sadly AngularJS doesn't expose the controls in a form. Maybe file a feature request?
You also can use a tag fieldset :
<form>
<fieldset ng-disable="foo">
<input name="the_one"/>
<input name="the_second"/>
</fieldset>
<input name="the_thrid"/>
</form>
With this way, when the variable foo is TRUE, inputs "the_one" and "the_second" will be disabled.
Why don't you use ng-disabled on your required expression on each input?
https://docs.angularjs.org/api/ng/directive/ngDisabled
If you truly do want a grouping directive, use the compile function of the directive to insert the ng-disabled attribute on each child. Or use a paren't child directive to signify which children to apply the ng-disabled to.
There is a new option to control enable/disable input field for angucomplete-alt.
http://ghiden.github.io/angucomplete-alt/#example13

Resources