I ran into a situation in AngularJS where I want to use a template that appends to the same template when data is changed instead of replacing the existing template. Directive can be something like this:
<my-template data="myData"></my-template>
template.html can be like:
<p>ID: {{data.id}}, Name: {{data.name}}</p>
The data will contain single data, so when I change data I want above template to append instead of replacing it. So the output will be like this:
ID: 1, Name: John
ID: 2, Name: Michael
ID: 3, Name: Abraham
I also want to handle onClick when the user tap on any name above.
How can I achieve this?
Ok i got it ! try this
angular.module('myApp', [])
.controller('myController', function($scope, $interval) {
var count = 0;
var addPerson = function() {
count++
$scope.person = {
name: 'person' + count,
age: 20 + count
};
};
var timer = $interval(addPerson, 1000);
})
.directive('myDirective', function($compile) {
return {
restrict: "A",
transclude: true,
scope: "=",
link: function(scope, element, attrs) {
scope.handleClick = function(event) {
console.log(event.target.innerText)
}
scope.$watch(function() {
return scope.person;
}, function(obj) {
var elementToRender = angular.element('<div ng-click="handleClick($event)">' + obj.name + '</div>');
function renderDiv() {
return elementToRender
}
element.append(renderDiv());
$compile(elementToRender)(scope);
}, false);
}
};
});
template
<div ng-app="myApp" ng-controller="myController">
<div my-directive data="{{person}}"></div>
</div>
Related
i am beginner in angular and i have a dynamic list like this
{.name:superParent,id:0,parrentId:null}
{.name:a,id:1,parrentId:0}
{.name:b,id:2,parrentId:0}
{.name:c,id:3,parrentId:1}
{.name:e,id:4,parrentId:3}
{.name:d,id:5,parrentId:2}
how can i implement it as nested list using angular and Recursive template? and also i want to keep each item-id in it self in the some attribute or property
i create this , but it dose not show the tree ,i can see the data only when i use ng-repeat in li
<ul>
<li ng-repeat="col in tree">{{col.Name}}</li>
</ul>
</div>
</div>
<script>
var app = angular.module('APP', []);
app.controller('IndexCtrl', function ($scope, $http) {
$scope.tree = [];
$scope.getList = function () {
//get data from server
$http.get("/person/GetTree")
.then(function (data) {
console.log("data:", data);
$scope.tree = data.data;
$scope.loading = false;
} )
}
$scope.getList();
});
app.directive('treeView', ['$compile', function($compile) {
return {
priority : 0,
restrict : "E",
replace : true,
scope : {
head: "=head",
children: "=children"
},
template: '<div><h4 id="{{head.id}}" ng-show="head.id>0">{{head.Name}}</h4> <ul> <li ng-repeat="item in items"> <tree-view head="item" children="children"></tree-view> </li> </ul></div>',
controller : ['$scope', function ($scope) {
var array = $scope.children;
var head = $scope.head;
//I used plugin linq.js
$scope.items = array.filter(function(val){
return val.parrentId==head.id;
});
}],
compile : function compile(element) {
var contents = element.contents().remove();
var contentsLinker;
return function (scope, iElement) {
if (angular.isUndefined(contentsLinker)) {
contentsLinker = $compile(contents);
}
contentsLinker(scope, function (clonedElement) {
iElement.append(clonedElement);
});
};
}
};
}]);
You can see example.
First I added main head item into your collection (it will be super parent for all other items):
{ name: "superparent", id: 0, parentId: null }
Then your collection will be looks like this:
tree = [
{name:'superparent',id:0,parrentId:Null},
{name:'a',id:1,parrentId:0},
{name:'b',id:2,parrentId:0},
{name:'c',id:3,parrentId:1},
{name:'e',id:4,parrentId:3},
{name:'d',id:5,parrentId:2},
];
All what we can do - create recursive directive (treeView) with such html template:
<div>
<h4 id="{{head.id}}" ng-show="head.id>0">{{head.name}}</h4>
<ul>
<li ng-repeat="item in items">
<tree-view head="item" children="children"></tree-view>
</li>
</ul>
</div>
I used TypeScript, but I sure you will easily undestand this code (d.compile function was taken from this answer):
module Directives {
export interface TreeItem {
name: string;
parrentId?: number
id: number;
}
TreeView.$inject = ['$compile'];
export function TreeView($compile): ng.IDirective {
var d: ng.IDirective = {};
d.priority = 0;
d.restrict = "E";
d.replace = true;
d.scope = {
head: "=head",
children: "=children"
};
d.templateUrl = '/somepath/treeView.html';
d.controller = ['$scope', function ($scope) {
var array: Array<TreeItem> = $scope.children;
var head: TreeItem = $scope.head;
$scope.items = array.filter(function(val){
return val.parrentId==head.id;
});
}];
d.compile = function compile(element) {
var contents = element.contents().remove();
var contentsLinker;
return function (scope, iElement) {
if (angular.isUndefined(contentsLinker)) {
contentsLinker = $compile(contents);
}
contentsLinker(scope, function (clonedElement) {
iElement.append(clonedElement);
});
};
};
return d;
}
}
At the end, into your html page, directive will be inserted this way, where tree[0] as you guessed is super parent:
<tree-view head="tree[0]" children="tree" ng-if="tree"></tree-view>
I have a component (a directive with isolate scope) that maps a list of objects to a list of inputs that allow to modify one property of that objects.
But I want to make that component universal.
So it should accept path to deeply nested property of each object in a list that should be bound to input.
For example we have a list of people, each of those have name spoken in different languages:
var people = [
{
age: 31,
multilang_attributes: {
en: {name: 'John'},
ru: {name: 'Иван'}
}
},
{
age: 29,
multilang_attributes: {
en: {name: 'Peter'},
ru: {name: 'Пётр'}
}
},
];
I want to apply my universal component to that list of people like the following:
<input-list
items="people",
item-model="multilang_attributes[locale].name"
></input-list>
I tried to create scope property with & that would allow me to execute expression in parent scope and then pass that expression to ngModel, but that does not work. You can look at this attempt in plunkr.
How that task can be approached in angular?
One option would be to $parse the accessor-string and use a getter/setter for the model. For this:
change the directive-html to:
item-accessor="'multilang_attributes.' + app.locale + '.name'"
This will make something like multilang_attributes.en.name available.
change the directive-code to:
app.directive('inputList', function () {
return {
restrict: 'E',
scope: {
items: '=',
itemAccessor: '='
},
template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
controller: function ($scope, $parse) {
$scope.getModel = function(item) {
return function (newValue) {
var getter = $parse($scope.itemAccessor);
if (arguments.length > 0) {
getter.assign(item, newValue);
}
return getter(item);
};
};
}
};
});
External demo: http://plnkr.co/edit/VzFrBxNcsA5BarIVr6oG?p=preview
var app = angular.module('TestApp', [])
app.controller('AppCtrl', function AppController() {
this.locales = ['en', 'fr']
this.locale = 'en';
this.people = [
{
age: 31,
multilang_attributes: {
en: {name: 'John (en)'},
fr: {name: 'John (fr)'}
}
},
{
age: 29,
multilang_attributes: {
en: {name: 'Fred (en)'},
fr: {name: 'Fred (fr)'}
}
},
];
});
app.directive('inputList', function () {
return {
restrict: 'E',
scope: {
items: '=',
itemAccessor: '='
},
template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
controller: function ($scope, $parse) {
$scope.getModel = function(item) {
return function (newValue) {
var getter = $parse($scope.itemAccessor);
if (arguments.length > 0) {
getter.assign(item, newValue);
}
return getter(item);
};
};
}
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.7" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
</head>
<body ng-app="TestApp" ng-controller="AppCtrl as app">
<div class="container">
<select ng-model="app.locale" ng-options="v as v for v in app.locales"></select>
<hr>
<input-list
items="app.people"
item-accessor="'multilang_attributes.' + app.locale + '.name'"
></input-list>
<hr>
<pre>{{ app.people|json }}</pre>
</div>
</body>
</html>
note that you should propably use proper injection-syntax and controllerAs/bindToController.
I have checked all the other posts and trust me, this is not a duplicate. Hope someone can help me. Here is the code.
HTML Code- When user clicks on Report, buildData gets executed. multiselect directive name is mu-ls.
<button ng-click="buildData(selected_items)">Report</button>
<div>Universe:</div>
<div><mu-ls pre-selected="member.roles" model="selected_items" options="roles"></muls></div>
Directive Code- The directive name is muLs. User can select multiple options using this directive. The $scope.model gives the array of ids which user selected.
angular.module('Select').directive('muLs', function () {
return {
restrict: 'E',
scope: {
model: '=',
options: '=',
pre_selected: '=preSelected'
},
template: "<div data-ng-class='{open: open}'>" +
"<button data-ng-click='open=!open;openDropdown()'>Select...</button>" +
"<ul aria-labelledby='dropdownMenu'>" +
"<li data-ng-repeat='option in options'> <a data-ng-click='setSelectedItem()'>{{option.name}}<span data-ng-class='isChecked(option.id)'></span></a></li>" +
"</ul>" +
"</div>",
controller: function ($scope) {
$scope.openDropdown = function () {
$scope.selected_items = [];
for (var i = 0; i < $scope.pre_selected.length; i++) {
$scope.selected_items.push($scope.pre_selected[i].id);
}
};
$scope.setSelectedItem = function () {
var id = this.option.id;
if (_.contains($scope.model, id)) {
$scope.model = _.without($scope.model, id);
} else {
$scope.model.push(id);
}
console.log($scope.model);
return false;
};
$scope.isChecked = function (id) {
if (_.contains($scope.model, id)) {
return 'glyphicon glyphicon-ok pull-right';
}
return false;
};
}
}
});
Controller Code- This should show the list of selected items listed above in the controller side. It shows undefined at the moment.
'use strict'
var Modd= angular.module('Select', []);
Modd.controller('SelectController', function ($scope, $timeout, $rootScope) {
$scope.roles = [
{ "id": 1, "name": "USA" },
{ "id": 2, "name": "France" },
{ "id": 3, "name": "Russia" }
];
$scope.member = { roles: [] };
$scope.selected_items = [];
$scope.buildData = function (selected_items) {
console.log("This is", $scope.model);
}
});
QUESTION- How can i use this directive value $scope.model in the controller side ??? Please suggest guys !!!
I tried $scope.selected_items first. It gives a list of selected items only once. Once i click Report button, it will give the list. If i again start clicking and deselecting list items, it would still show the previous values. not the current ones.
$scope.model continues to show the latest values selected.
You are passing selected_items to your directive so it will contain the value of model in your controller.
$scope.buildData = function () {
console.log("This is", $scope.selected_items);
}
model is two-way bound. So if you assign a $scope variable from your controller to the module attribute, it will be updated when the selected value changes.
Therefore you can console.log($scope.selected_items);
So I have this filter directive:
app.directive('filter', function(){
return {
restrict: 'E',
transclude: true,
scope: {
callFunc: '&'
},
template:
' <div>' +
' <div ng-transclude></div>' +
' </div>',
controller: function($scope, $element, $attrs){
this.getData = function() {
$scope.callFunc()
}
}
}
});
app.directive('positions', function(){
return {
require: '^filter',
scope: {
selectedPos: '='
},
template:
' Positions: {{selectedPos}}' +
' <ul>' +
' <li ng-repeat="pos in positions">' +
' {{pos.name}}</a>' +
' </li>' +
' </ul>',
controller: function($scope, $element, $attrs){
$scope.positions = [
{name: '1'},
{name: '2'},
{name: '3'},
{name: '4'},
{name: '5'}
];
$scope.selectedPos = $scope.positions[0].name;
$scope.setPosition = function(pos){
$scope.selectedPos = pos.name;
};
},
link: function(scope, element, attrs, filterCtrl) {
scope.posRegData = function() {
filterCtrl.getData();
}
}
}
})
And the controller:
app.controller('keyCtrl', ['$scope', function($scope) {
var key = this;
key.callFunc = function() {
key.value = key.selectedPos;
console.log(key.selectedPos)
}
}]);
The main question is why the key.selectedPos in the controller get's the right value only on the second click?
Here is a plunker replicating my issue.
One way of doing it is to send a param when calling callFunc()
Then, I update the func in the ctrl: key.callFunc = function(filterParams), but also, I am updating the passed method call-func="key.callFunc(filterParams)
Then in filter directive I change getData method to:
this.getData = function(val) {
$scope.callFunc({filterParams: val})
}
In positions directive I pass the value that I need:
scope.posRegData = function() {
filterCtrl.getData({position: scope.selectedPos});
}
Finally, in keyCtrl I get the value like this:
key.callFunc = function(filterParams) {
key.value = filterParams.position;
console.log(filterPrams.position)
}
Here is a plunker demonstrating this attempt.
The question in this case is if this is a good way of doing it, keeping in mind it's within a very large application.
That's because how isolated scopes work. The parent scope (the controller in your case) will be updated when the digest cycle runs, which is after your ng-click function called the callFunc. So you can put your callFunc code in a $timeout and it will work (but will cause another digest cycle).
Another solution will be to put the value in an object, so when you change the object the controller (which have the reference) will see the update immediately:
In the controller:
key.selectedPos = { value: {}};
key.callFunc = function() {
key.value = key.selectedPos.value;
console.log(key.selectedPos.value)
}
In the directive:
$scope.selectedPos.value = $scope.positions[0].name;
$scope.setPosition = function(pos){
$scope.selectedPos.value = pos.name;
};
See this plunker.
I'm trying to send an event when an item gets selected, from directive to controller using $emit. I've two update functions for organizations and another for people. My directive should specify which event should emit.
Here is my update functions:
// For organization
$scope.updateOrgs = function(selectedVal) {
}
// For people
$scope.updatepeople = function(selectedVal, type) {
}
When it is people my directive should raise an emit event for updatepeople (), if it was org it should raise updateorg().
My directive looks like:
.directive('search', function ($timeout) {
return {
restrict: 'AEC',
scope: {
model: '=',
searchobj: '#',
},
link: function (scope, elem, attrs, index) {
scope.handleSelection = function (selectedItem) {
scope.model = selectedItem;
scope.searchModel="";
scope.current = 0;
scope.selected = true;
$timeout(function () {
scope.onSelectupdate();
}, 200);
};
scope.Delete = function (index) {
scope.selectedIndex = index;
scope.delete({ index: index });
};
scope.Search = function (searchitem,event,searchobj) {
// alert('item entered'+name)
scope.searching = searchitem;
scope.searchobject = searchobj;
scope.onSearch({ searchitem: searchitem , searchobj:searchobj});
};
scope.current = 0;
scope.selected = true;
scope.isCurrent = function (index) {
return scope.current == index;
};
scope.setCurrent = function (index) {
scope.current = index;
};
},
controller: ['$scope','$element','$rootScope','SearchOrg', function($scope,$element,$rootScope,SearchOrg) {
$scope.searchItem = function(filter,searchobj){
//alert('search'+searchobj);
SearchOrg().fetch({'filter': filter, 'searchType': searchobj}).$promise.then(function(value){
$scope.searchData = value.data;
console.info($scope.searchData);
},
function(err) {
});
}
}],
templateUrl: TAPPLENT_CONFIG.HTML_ENDPOINT[0] + 'home/genericsearch.html'
}
});;
HTML snippet
<search searchobj=“tei-org” selectedItems=“arrayofIds” search-id=”someidtoIdentify”/>
How can I do this both functions are in different controllers, and also I need to send parameters from directive to the controller using $emit?
Working with $scope.$emit and $scope.$on
I'm guessing that your other controllers are not parents, so look at the second option using $broadcast.
var app = angular.module('app', []);
app.controller('firstController', function($scope) {
$scope.selectedOrgs = []
$scope.$on('updateorgs', function(evt, data) {
$scope.selectedOrgs.push(data);
});
});
app.controller('secondController', function($scope) {
$scope.selectedPeople = []
$scope.$on('updatepeople', function(evt, data) {
$scope.selectedPeople.push(data);
});
});
app.directive('someDirective', function($rootScope) {
return {
scope: {},
link: function(scope) {
scope.options = [{
id: 1,
label: 'org a',
type: 'org'
}, {
id: 2,
label: 'org b',
type: 'org'
}, {
id: 3,
label: 'person a',
type: 'person'
}, {
id: 4,
label: 'person b',
type: 'person'
}];
scope.changed = function() {
if (scope.selected) {
var updatetype = scope.selected.type;
if (updatetype === 'person') {
$rootScope.$broadcast('updatepeople', scope.selected);
} else if (updatetype === 'org') {
$rootScope.$broadcast('updateorgs', scope.selected);
}
}
};
},
template: '<select ng-change="changed()" ng-model="selected" ng-options="option.label for option in options"><option value="">Select</option></select>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app'>
<some-directive></some-directive>
<div ng-controller='firstController'>
<div>ORGS:</div>
<div>
{{ selectedOrgs }}
</div>
</div>
<div ng-controller='secondController'>
<div>PEOPLE:</div>
<div>
{{ selectedPeople }}
</div>
</div>
</div>