Updating ngModel from Custom Directive - javascript

I've built a custom directive for generating and populating a dropdown from JSON Data.
However I want to react to a dropdown selection with a function. Therefore I want to make use of ngChange (and ngModel).
So I try to start by updating the ng-model from the directive.
The link function in the Directive should give me everything I need.
BUT the fourth parameter is undefined therefore I can't use it or even compile.
Directive TS:
module lsw{
lswApp.lswAppModule.directive("guiDropdown",
() => ({
restrict: "E",
require: "ngModel",
scope: {
data: "=",
ctrl: "="
},
link(scope, element, attrs, ctrls) {
element.bind("change", () => {
});
},
templateUrl: "../../App_Scripts/Directives/guiDropdown.html"
}));}
"ctrls:{}"
My Idea was to make use of ctrls.$setViewValue to update ngModel
Directive HTML
<div class="elements">
<select name="{{data.name}}" class="dropdown-box">
<option ng-repeat="option in ctrl" ng-value="option">{{option}}</option>
</select>
Main View HTML
<div ng-repeat="element in HomeController.wrapper" ng-if="HomeController.trigger">
<div ng-switch="element.type">
<div ng-switch-when="dropdown">
<gui-dropdown data="element" ctrl="HomeController.content" ng-model="element" ng-change="HomeController.test()"></gui-dropdown>
</div>
<div ng-switch-when="textfield">
<gui-textfield data="element"></gui-textfield>
</div>
<div ng-switch-when="checkbox">
<gui-checkbox data="element"></gui-checkbox>
</div>
</div>
</div>

Related

Use directive with different controllers in AngularJS?

I have created a directive for a search box which i want to use with different views. Here is the directive -
angular.module('jobSeekerApp')
.directive('searchBoxDirective', function () {
return {
restrict: 'E',
templateUrl: 'templates/searchbox-template.html',
};
});
template for the directive -
<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="search()" ng-model="searchTerm" ng-keydown="deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>
I want to use this directive on two views which look like this -
View 1 -
<div class="panel panel-default companies" ng-repeat="company in companies.companiesList">
<div class="panel-heading text-center"><a ng-href="/companies/{{company.id}}" class="hvr-sink"><h3 class="well">{{company.company_name}}</h3></a></div>
<div class="panel-body text-center flexcontainer">
<div>Location: {{company.location}}</div>
<div>Founded In: {{company.founded_year}}</div>
<div ng-if="company.opening">Opening: Yes</div>
<div ng-if="!company.opening">Opening: No</div>
<div>Number Of Openings: {{company.no_openings}}</div>
</div>
</div>
View 2 -
<div class="panel panel-default jobs" ng-repeat="job in jobs.jobsList">
<div class="panel-heading text-center"><h3 class="well">{{job.job_name}}</h3></div>
<div class="panel-body text-center flexcontainer">
<div>Company: {{job.company_name}}</div>
</div>
</div>
As you can see i am using aliases companies and jobs in my views, due to this my directive is not able to affect the view it is contained in. If i use the companies or jobs in my template , then it works fine. So for example if change the template to -
<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="companies.search()" ng-model="companies.searchTerm" ng-keydown="companies.deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>
Then it works with the view associated with companies controller and similarly for jobs.
How can i use the directive with the respective controller instance?
Thank you.
Create a simple view for Search
Create it's own controller
Search record by this controller in service and put data in service variable
this.searchResult = [];
this.search = function(searchText){
// code to search
this.searchResult = Result;
}
Now where ever you want to use this result use the watch on this service variable in current controller, like:
$scope.$watch(function() { return servicename.searchResult ; }, function(newVal, oldval) {
if(newval != oldval){
$scope.data = servicename.searchResult;
/* Do the rest of stuff */
}
}, true);
Since the search function is asynchronous, I recommend avoiding the use of ng-change to invoke it. Instead use ng-click on the search icon.
<span class="searchButton" ng-click="$ctrl.searchFn()">
<i class="fa fa-search fa-2x"></i>
</span>
<input ng-model="$ctrl.searchTerm" type="text" id="search-box">
In the directive, use isolate scope and bindToController.
app.directive('searchBoxDirective', function () {
return {
restrict: 'E',
scope: { 'searchTerm': "=",
'searchFn': "&"
},
controller: function () {},
controllerAs: '$ctrl',
bindToController: true,
templateUrl: 'templates/searchbox-template.html'
};
});
Usage
<search-box-directive search-term="companies.searchTerm"
search-fn="companies.search()" >
</search-box-directive>
<search-box-directive search-term="jobs.searchTerm"
search-fn="jobs.search()" >
</search-box-directive>
The search-term attribute creates a bidirectional binding from parent scope to the directive isolate scope. The search-fn attribute creates an expression binding from parent scope to isolate scope.
Use isolate scope and explicitly pass the search term to your directive. Something like:
angular.module('jobSeekerApp')
.directive('searchBoxDirective', function () {
return {
restrict: 'E',
scope: {
searchTerm: '=' <-- you can use '#' if you don't need two-way binding
},
templateUrl: 'templates/searchbox-template.html',
};
});
You didn't show where you are actually using your directive, but you would pass the scope property through an attribute (this is using your directive on a <div>):
<div search-box-directive search-term="companies.searchTerm"></div>

ngDirective is not watching

I am trying to make angular works with semantic ui, specifically dropdown searching box. I have tried multiple things and I found that because the input type is set to be hidden, ng-model is not going to work. And because the change to the element is not directly triggered by the user, the ng-change also won't work.
My idea is to use a directive and try to monitor the input element's attribute, because after selecting something, the value attribute changes. I think ngDirective should be able to implement the ng-model and ng-change by using scope.$watch
I tried this but the scope value has not been changed.
var app = angular.module('myApp', [
'my.controllers'
]);
var controllers = angular.module('my.controllers', []);
controllers.controller('MyController', function($scope) {
$scope.checked = true;
});
app.directive("ngSearch", function(){
function link($scope, element, attrs, ctrl) {
console.log($scope.value);
$scope.$watch("value", function(newValue, oldValue){
console.log(newValue, oldValue);
console.log("value updated");
ctrl.$setViewValue($scope.value);
}, true);
setTimeout(function() {console.log(attrs.value);}, 6000);
}
return {
restrict: 'A',
require: 'ngModel',
scope: {
"value": "#",
},
link: link,
}
})
$(".ui.dropdown").dropdown();
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.js"></script>
<div ng-app="myApp">
<div ng-conroller="MyController">
<div class="ui fluid multiple search normal selection dropdown">
<input type="hidden" name="country" value="test1" ng-search ng-model="users">
<i class="dropdown icon"></i>
<div class="default text">Select testing</div>
<div class="menu">
<div class="item" data-value="test1">test1</div>
<div class="item" data-value="test2">test2</div>
<div class="item" data-value="test3">test3</div>
</div>
</div>
</div>
</div>
same version in jsfiddle: http://jsfiddle.net/e5aes1fe/1/

AngularJs, accessing parent scope from directive inside a nested ng-repeat

That's a noob question. I'm looking for the correct way to access the parent scope inside a directive in a nested ng-repeat. This is exactly what i mean:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
</div>
And the directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.$parent.section.SectionId);
}
}
});
The directive is attached to an item in the inner ng-repeat, and i need to access a property of the parent object. The problem is that i cannot access directly to the parent properties (with scope.$parent), because ng-repeat creates a new scope, and i must append the name of the object i set in the ng-repeat (in this case scope.$parent.section.):
<div ng-repeat="section in sections">
console.log(scope.$parent.section.SectionId);
JsFiddle: http://jsfiddle.net/7Lra7Loy/2/
As i want the directive to be generic, so it can be used inside other ng-repeat blocks, without being forced to use the same names in the ng-repeat, the only way i found is to use an ng-init, that would be the same in all ng-repeat blocks (ng-init="parent = section"):
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.parent.SectionId);
}
}
});
JsFiddle: http://jsfiddle.net/7Lra7Loy/1/
Is there a better way to handle this situation? Or am i just missing something? I searched a bit, but i couldn't find anything really useful.
Template:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items">
<span class="menuItem" section="{{section}}">{{item}}</span>
</div>
</div>
</div>
And directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
scope: {
section: '#' // text-binding
//section: '&' //one-way binding
//section: '=' //two-way binding
},
link: function ($scope, element, attrs) {
console.log($scope.section);
}
}
});
JsFiddle: https://jsfiddle.net/nrkmn/26zhqbjg/

Custom search filter in angularjs

I am using the inbuilt search filter of angular js like this-
<!--header starts-->
<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search">
<!--header ends-->
<!--content area-->
<div ng-repeat="user in users | filter:search">
{{ user.name }}
{{user.l}}
{{user.time}}
</div>
<!--content area ends-->
Now i removed the header template codes and created the header directive.
<!--header starts-->
<div site-header>
</div>
<!--header ends-->
<!--content area-->
<div ng-repeat="user in users | filter:search">
{{ user.name }}
{{user.l}}
{{user.time}}
</div>
<!--content area ends-->
site-header.js:
'use strict';
angular.module('myApp')
.directive('siteHeader', function () {
return {
templateUrl: 'views/header-view.html',
scope: {
},
restrict: 'A',
controller: [ '$scope', '$rootScope', '$location', function($scope, $rootScope, $location) {
console.log($scope.data);
}],
link: function postLink(scope, element, attrs) {
}
};
});
header-view.html
<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search">
All html templates is loading correctly. but the search filter is not working. I dont understand how to bind search.name to the directive in order to work. I tried like this-
<div site-header="search.name">
</div>
but how to access this data in directive and bind it to ng-model?
Change your directive definition:
app.directive('myDirective', function(){
return {
restrict: 'A',
template: [
'<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search"/>'
].join(''),
scope: true
}
});
here is a demo:http://plnkr.co/edit/JNCjzs?p=preview
Directives get their own scopes in angular. Scopes prototypically inherit from their parent scopes, which can be confusing and takes some time and knowledge of js inheritance to get your head around. If you can't be bothered with that, a quick solution to your problem would be to use an object to store the search string and instantiate it in the parent scope, which will make it available in both scopes. I think all you need is to put the line:
`var search = {};`
in your controller.

Create a list of items built using angularjs

I would like to create a list of items built in Directive and share through controllers.
Here is my example in plunker: Example of my code
Here is my code in js:
var app = angular.module('app', []);
app.controller("BrunchesCtrl", function ($scope, $log) {
$scope.brunches = [];
$scope.addNew = function () {
$scope.brunches.push({ address: "" });
};
$scope.$watch('brunch.address', function(value) {
$scope.address = value;
$log.info($scope.address);
});
$scope.$watch(function() { return $scope.brunches.length; }, function() {
$scope.brunches = $scope.brunches.map(function (brunch) {
return brunch;
});
});
});
app.directive('brunchesView', function ($compile) {
return {
restrict: 'E',
templateUrl: 'brunch.html',
controller: 'BrunchesCtrl',
replace: true,
link: function (scope, element, attrs, controller) {
}
};
});
app.directive('businessSubmit', function ($log, $compile, formManagerService) {
return {
restrict: 'AE',
controller: 'BrunchesCtrl',
link: function (scope, element, attrs, controller) {
formManagerService.init();
var hasError;
element.on('click', function (e) {
e.preventDefault();
$log.info("brunches: \n");
$log.info(scope.brunches);
});
}
};
});
Here is an HTML:
<!DOCTYPE html>
<div class="controls">
<a class="btn btn-danger" id="addBrunch" data-ng-click="addNew()">
<i class="icon-plus-sign"></i>
add new...
</a>
</div>
</div>
<brunches-view class="well" data-ng-repeat="brunch in brunches">{{brunch}}</brunches-view>
<br/>
<p class="well">
JSON: {{brunches | json}}
</p>
<div class="control-group">
<div class="controls">
<a class="btn btn-primary" href="#" data-business-submit>
<i class="icon-save"></i>
Save...
</a>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="script.js"></script>
And here is the template:
<div class="fc-border-separate">
<div class="control-group">
<label class="control-label">Address</label>
<div class="controls">
<input type="text" class="span6 m-wrap"
name="Address" data-ng-model="address"
value="{{address}}" />
</div>
</div>
</div>
The final thing I want to save the whole data inside the BusinessSubmit directive.
Help please...
Several issues with your code.
First, your ng-model for the <input> is set to address, but the object you are wanting to bind it to a brunch object that has an address property. Important to realize that ng-repeat will create a child scope for every repeated item
<input data-ng-model="brunch.address"/>
Another issue is you are assigning the parent controller to a directive as well. Important to understand that controllers are singletons so if you use controller more than once , each is a separate instance. Therefore nesting the parent controller in a directive makes no sense.
DEMO- [ng-model] fix
If you want the data shared with other controllers you should set up a service that holds the brunches data by injecting it into whatever controllers will need access

Categories