Custom search filter in angularjs - javascript

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.

Related

Updating ngModel from Custom Directive

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>

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>

Correct way to access the scope from a directive on multiple controllers

I have a directive named add-tags than I am planning to use in different places of the app, I am using it to add tags to an array then save from the main ctrl, then the user can edit the list when previewing from a different view/ctrl (modal), on main page I have:
<add-tags tags="tags"></add-tags>
and my directive is set up as follow:
'use strict';
angular.module('theApp')
.directive('addTags', function() {
return {
templateUrl: 'components/add-tags/add-tags.html',
//restrict: 'EA',
scope: {
tags:'='
},
link: function($scope, $element, attrs) {
} //link
};
});
From the edit controller, how can access the previous tags data? when I do,
<add-tags tags="workDetails.tags"></add-tags>
the entire data from is gone from the scope, but when I do:
<span ng-repeat="x in workDetails.tags">
<h1>{{x.name}}</h1>
</span>
I can see the list of tags, any help will be appreciated :)
I am adding the add-tags.html example:
<div ng-repeat="tag in tags" class="text-center new-item">
<div class="input-group">
<input type="text" name="" class="form-control" ng-model="tag.name">
<span class="input-group-btn">
<button
type="button"
class="btn btn-default"
ng-click="deleteTag($index)">
Delete
</button>
</span> <!-- /.input-group-btn -->
</div> <!-- /.input-group -->
</div>
<div class="form-group" ng-hide="tags.length == tags_max">
<input type="text" class="form-control" placeholder="Enter tag:" ng-model="tag">
</div>
<!-- /.form-group -->
<button
type="button"
class="btn btn-primary center-block"
ng-disabled="tags.length == tags_max"
ng-class="{ 'btn-danger': tags.length == tags_max}"
ng-click="addTag()">
Add tag
</button>
<hr>
There is nothing wrong with the code you've given. However - unless I am missing some information - is it possible you're assuming that your "workDetails" object should be available everywhere? The only way I could get your scenario to work is by adding a "workDetailsService" that holds the tag state.
Here's a working plunkr of the code bits you provided, but with a workDetailsService added to maintain state and some routing.
Basically I added a service to maintain the tags:
theApp.factory('workDetailsService', function() {
return {
tags: [{
name: "Tag 1"
}, {
name: "Tag 2"
}, {
name: "Tag 3"
}]
}
});
And injected that service into two directives, "listTags" and "editTags":
theApp.directive('editTags', ['workDetailsService', function(workDetailsService) {
return {
templateUrl: '/edit-tags.html',
link: function($scope, $element, attrs) {
$scope.tags_max = 4;
$scope.tags = workDetailsService.tags;
$scope.addTag = function() {
workDetailsService.tags.push({
name: $scope.tag
})
}
$scope.deleteTag = function(index) {
workDetailsService.tags.splice(index, 1)
}
}
};
}]);
theApp.directive('listTags', ['workDetailsService', function(workDetailsService) {
return {
templateUrl: '/list-tags.html',
link: function($scope, $element, attrs) {
$scope.tags = workDetailsService.tags;
}
};
}]);
This way you have your tags state in one place, and the directives wrap the tags up instead of controllers so you can reuse everywhere.

Trouble creating search in an Angular directive

I'm trying to use Angular's search filter in the following way and cannot figure out what I am doing wrong.
This application should be able to use the search input to search all manufacturer names and items from the controller.
I know it is working somewhat correctly because my browser shows the image and manufacturer.name of both objects: manufacturer1 and manufacturer2, but it is not showing the items and the search function doesn't work.
Help is greatly appreciated! I'm pretty new to Angular.
<div ng-controller="SearchCtrl">
<input type="search" ng-model="search" class="form-control" placeholder="Search Manufacturers, Solutions, Equipment, Etc.">
<manufacturer info="manufacturer1"></manufacturer>
<manufacturer info="manufacturer2"></manufacturer>
</div>
Controller:
app.controller('SearchCtrl', ['$scope', function ($scope) {
$scope.manufacturer1 = {
name: 'Business Name',
items: [
'service1',
'service2',
'service3'
],
image: 'assets/images/image.png'
};
$scope.manufacturer2 = {
name: 'Other Business',
items: [
'product1',
'product2',
'product3'
],
image: 'assets/images/image.png'
};
}]);
Directive JS
app.directive('manufacturer', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'assets/js/directives/manufacturer.html'
};
});
Directive Template:
<div class="col-md-4">
<h2 class="business-title">{{ info.name }}</h2>
<img ng-src="{{ info.image }}" class="feature-img" alt="image"/>
<ul>
<li ng-repeat="item in items | filter:search">
{{ info.items }}
</li>
</ul>
</div>
As manufacturer directive has isolated scope, so you couldn't have access to search variable inside your directive you need to pass the search variable to the directive from the directive attribute & include that property inside directive isolated scope, as you have done for the info variable.
Basically you need to add serach: '=' inside your directive isolated scope, which will pass the search input box value to the directive by using attribute search="search". = is used for two way binding to update the search result as user will change the input the info will get filtered automatically.
Markup
<input type="search" ng-model="search" class="form-control" placeholder="Search Manufacturers, Solutions, Equipment, Etc.">
<manufacturer info="manufacturer1" search="search"></manufacturer>
<manufacturer info="manufacturer2" search="search"></manufacturer>
Directive
app.directive('manufacturer', function() {
return {
restrict: 'E',
scope: {
info: '=',
search: '='
},
templateUrl: 'assets/js/directives/manufacturer.html'
};
});
Update
You have wrong html inside your manufacturer.html, specifically the content which is showing items using ng-repeat
<ul>
<li ng-repeat="item in info.items | filter:search">
{{ item }}
</li>
</ul>
Demo Plunkr

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