HTML :
<div ng-app="myApp" ng-controller="someController as Ctrl">
<div class="clickme" ng-repeat="elems in Ctrl.elem" ng-click="Ctrl.click(elems.title)">
{{elems.title}}
<span>click me</span>
<div id="container">
<test-Input title="elems.title" data="elems.id" ng-if="Ctrl.myId==" >/test-Input>
</div>
</div>
JS :
var Elems = [
{
title : "First",
id : 1
},
{
title : "Second",
id : 2
},
{
title : "Third",
id : 3
}
];
var myApp = angular.module('myApp', []);
myApp.controller('someController', function($scope) {
var self = this;
self.elem = Elems;
self.myId = false;
self.click = function(data){
self.myId = data;
};
});
myApp.directive('testInput',function(){
return {
restrict: 'E',
scope: {
myTitle: '=title',
myId: '=data'
},
template: '<div>{{myTitle}}</div>',
controller: function($scope) {
}
};
});
I'm new to angular js. when I click the "click me" div then I want to make ng-if = true result. then show (not ng-show it will renders every elements) the directive. is there any ways to do it angular way?
Here is a fiddle: http://jsfiddle.net/4L6qbpoy/5/
You can use something like:
<test-Input title="elems.title" data="elems.id" ng-if="elems.isVisible"></test-Input>
and toggle that on click
Check out this jsfiddle
You need to have the ng-if evaluate to true within the ng-repeat. So you need a condition that evaluates the unique value for each object in the array.
ng-if="Ctrl.myId==elems.title"
To understand, each instance of ng-repeat falls under the controller's scope. That means the ng-repeated elements are pushed to the boundaries and are only evaluated within your template. Within those bounds, you have access to a tiny local scope which includes $index, $first, $odd, etc..
You can read more here: https://docs.angularjs.org/api/ng/directive/ngRepeat
Related
I am building two AngularJS (version 1.6.5) components and I am not able to get filtering working when I use transclusion.
The first component is a simple container which uses transclusion to populate a <div> content:
app.component('panelWithTitle', {
template: '<div><h1>{{$ctrl.title}}</h1><div class="content" ng-transclude></div></div>',
bindings: {
title: '#'
},
require: 'title',
transclude: true
});
The second component uses the container (<panel-with-title>) and feed it with a simple filtered (from an input field) list:
app.component('mainComponent', {
controller: function($scope) {
$scope.allItems = ["10", "100", "200", "42", "1337"];
// Simple filter function (may be more complicated)
$scope.filterFunc = function (item) {
if (!$scope.filterValue) {
// No value
return true;
}
return item.indexOf($scope.filterValue) !== -1;
};
},
template: '<panel-with-title title="MyTitle">' // Replace by <div>
+ '<input type="text" ng-model="filterValue">'
+ '<ul><li ng-repeat="item in allItems | filter:filterFunc">{{item}}</li></ul>'
+ '</panel-with-title>' // Replace by </div>
});
In that state, the filtering is not working because $scope.filterValue is undefined. Here is a demo Plunkr. I noticed:
The filtering is working if I do not use transclusion (for instance: if I replace the <panel-with-title> tags by simple <div> tags).
In any case, $scope.allItems is correctly defined.
What did I make wrong to get it not working? Why $scope.filterValue is undefined while $scope.allItems IS defined?
Thanks.
Your $scope.filterValue always undefined and filter returns true because your template uses different scope.
So add $root to filterValue like:
<input type="text" ng-model="$root.filterValue">
and in component use $scope.$parent.filterValue:
return item.indexOf($scope.$parent.filterValue) !== -1;
Demo Plunker
BTW, nice question :)
If you don't want to write more code means (filterFunc function) then
You should connect this code to the model (ng-model="filterValue") for direct filter via the model.
Please find my below plunker link -
http://plnkr.co/edit/sp9XFUMXLdmSwESujMQD?p=preview
app.component('mainComponent', {
controller: function($scope) {
$scope.allItems = ["10", "100", "200", "42", "1337"];
},
template: '<panel-with-title title="MyTitle">' // Replace by <div>
+ '<input type="text" ng-model="filterValue">'
+ '<ul><li ng-repeat="item in allItems | filter:filterValue">{{item}}</li></ul>'
+ '</panel-with-title>' // Replace by </div>
});
I try to update the list using directive template. But its not update the data after http request.
test.html:
<div ng-repeat=" comment in [{name:"A"},{name:"B"},{name:"C"}]">
<div lookup-product-icon lookup="lookupProduct(comment)" products="products"></div>
</div>
<div lookup-products></div>
....
Directive:
var app = angular.module('myApp');
app.directive('lookupProductIcon', function() {
return {
scope : {
lookup : "&"
},
template : '<div ng-click="lookup()">Use me!</div>',
};
});
app.directive('lookupProducts', function() {
return {
restrict : 'EA',
transclude : false,
scope : {
products : '='
},
templateUrl : 'lists.html'
};
});
Controller
$scope.products = [];
$scope.lookupProduct = function(lists) {
var details = {
name : lists.name,
host : $scope.host_name
};
productService.lookupProduct(details).then(function(data) {
$scope.products = data.list;
console.log($scope.products);
//Here display the lists in developer tools.
}).catch(function(data) {
if (data.message) {
alert(data.message);
}
});
};
List.html:
<ul>
<li ng-repeat = "product in products"> {{product.name}} </li>
</ul>
Once I click the "Use me!" means then i need to send the http request and then display the lists for the respective content in list.html.
lookupProduct function working but only thing is the products not updating.
Details:
I added two directives.
1. lookupProductIcon - Display the text. Once this text clicked means need to the http get request and then response need to update in list.html (lookupProducts directive)
2. lookupProducts - Here the the data not updating.
Your lookupProducts directive has a scope variable products that is not being passed in your html markup.
You need to pass in an array of products to your lookupProducts directive.
<div lookup-products products="products"></div>
I am trying to create buttons (using ng-repeat) that when clicked will create other buttons that when these are clicked will display the information I am looking for. I was told that an Angular Directive would do the trick. I have created a custom directive and am trying to incorporate the ng-repeat directive inside of my new directive. I have already looked at this StackOverflow discussion StackOverflow Discussion 2, but I am still having some confusion on how to best get his implemented. As it stands the new directive is being made, but no text is being appended to the button. Also only one button is being generated instead of two in this case. Below is my code (HTML and JavaScript)
HTML:
<div ng-app="anniversaries" class="row" ng-controller="annList">
<yearbuttons></yearbuttons>
</div>
JavaScript:
var annApp = angular.module('anniversaries', []);
annApp.controller('annList', function ($scope) {
$scope.anns = [
//January
{'date':'January 2015','year': '45',
'names': ['Sample Name']},
{'date':'January 2015','year': '34',
'names': ['Sample Name2']}
];
});
annApp.directive('yearbuttons',function(){
return {
restrict: "E",
compile: function compile(element, attrs)
{
element.attr('ng-repeat', 'years in anns');
element.attr('class', 'btn btn-default');
element.append('{{year}} Years');
}
}
});
I personally wouldn't use a directive for this at all unless it needed to be used repeatedly. But if it does, this could easily be converted to a directive just by sticking the template and controller into a directive. Directives can take controllers instead of link functions.
JavaScript
(function(){
angular.module('myApp', [])
.controller('BunchOfButtonsController', BunchOfButtonsController);
BunchOfButtonsController.$inject = ['$scope', '$log'];
function BunchOfButtonsController($scope, $log){
$scope.buttons = [];
$scope.nextLabel = "Some Text";
$scope.firstButtonClick = function(labelValue){
$scope.buttons.push({
label: labelValue,
click: function(){
$log.info(labelValue + " was clicked.");
}
});
$scope.nextLabel = "Another " + $scope.nextLabel;
};
}
})();
HTML
<div ng-app="myApp" ng-controller="BunchOfButtonsController">
Label: <input type="text" ng-model="nextLabel"/>
<button ng-click="firstButtonClick(nextLabel)">Click Me!</button>
<button ng-repeat="button in buttons" ng-click="button.click()">{{button.label}}</button>
</div>
I have a directive rendering inputs based on a configuration sent by the server. Everything is working great except for the 'select' input.
No matter what I try, the ng-model does not update.
I have trimmed my code a lot to isolate the problem :
Javascript :
var myApp = angular.module('example', []);
myApp.factory('DynamicData', [function() {
return {
data: {
backup_frequency: 604800
}
};
}])
.directive('dynamicInput',
['$compile', 'DynamicData', function($compile, DynamicData) {
/**
* Render the input
*/
var render = function render() {
var input = angular.element('<select class="form-control" ng-model="inner.backup_frequency" ng-options="option.value as option.title for option in options"></select>');
return input;
};
var getInput = function ()
{
var input = render();
return input ? input[0].outerHTML : '';
};
var getTemplate = function(){
var template = '<div class="form-group">' +
'Select input ' +
'<div class="col-md-7">' + getInput() + '</div>' +
'</div>';
return template;
};
return {
restrict : 'E',
scope: {
content:'=content',
},
link : function(scope, element, attrs) {
var template = getTemplate();
scope.options = [
{title: "Daily", value: 86400},
{title: "Weekly", value: 604800},
{title: "Monthly", value: 2678400},
];
scope.inner = DynamicData.data;
console.info('inner data', scope.inner);
element.html(template);
element.replaceWith($compile(element.contents())(scope));
}
};
}])
.controller('FormCtrl', ['DynamicData', '$scope', function (DynamicData, $scope){
$scope.app = {};
$scope.save = function save() {
$scope.value = DynamicData.data.backup_frequency;
console.info('DynamicData', DynamicData.data);
};
}]);
HTML :
<head>
<script data-require="angular.js#1.3.8" data-semver="1.3.8" src="https://code.angularjs.org/1.3.8/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Dynamic Input :</h1>
<div data-ng-controller="FormCtrl">
<dynamic-input class="form-group" content="app"></dynamic-input>
<span data-ng-bind="value"></span><br/>
<button class="btn btn-primary" ng-click="save()">Save</button>
</div>
</body>
</html>
A working plunker is available : http://plnkr.co/edit/mNBTJzZXjX6mLyPz6NCI?p=preview
Do you have any ideas why the ng-model in the select is not updated ?
EDIT : What I want to achieve is updating the variable "inner.backup_frequency" (the reference to my data object returned by my factory DynamicData). As you can see in the plunker, whenever I change the option in the select, the variable contained in the ng-model is not updated.
Thank you for your time.
I had a similar issue and find if you directly bind ng-model to a scope variable as below, the scope variable selection somehow will not be updated.
<select ng-model="selection" ng-options="option for option in options">
Instead, define a view model in your scope and bind ng-model to a field of view model the view model field will be updated.
<select ng-model="viewmodel.selection" ng-options="option for option in options">
In your controller, you should do this:
app.controller('SomeCtrl', ['$scope'
function($scope)
{
$scope.viewmodel = {};
});
I've fixed it. What you needed to do was not to replace the element's content with the compiled content, but rather replace it with the raw HTML and only then compile it.
element.html(template);
$compile(element.contents())(scope);
Working Plunker.
EDIT: I've edited your code to work without the directive:
Plunker without directive.
EDIT 2: Also a version where it works with the directive, but without the compile:
Plunker.
I have a problem instanciating controller with Angular. I have a main controller AlkeTypeDefListController from which I want to dynamically create/remove controllers of type AlkeTypeDefController, so I have done that :
Code of AlkeTypeDefListController:
// Create main controller
Alke.controller('AlkeTypeDefListController', ['$scope', '$controller', function($scope, $controller)
{
var primitives =
[
];
// Add some properties to the scope
angular.extend($scope,
{
typedefs : primitives,
addTypeDef : function()
{
var controller = $controller("AlkeTypeDefController", {$scope:$scope.$new()});
$scope.typedefs.push(controller);
}
});
}]);
Code of AlkeTypeDefController:
// Create main controller
Alke.controller('AlkeTypeDefController', ['$scope', '$controller', function($scope, $controller)
{
// Add some properties to the scope
angular.extend($scope,
{
name : "New Type",
fields : [],
addField : function()
{
}
});
}]);
The html code is this one:
<div id="typedefs-editor" ng:controller="AlkeTypeDefListController">
<button ng:click="addTypeDef()">Add</button>
<button>Remove</button>
<div id="typedef-list">
<ul class="list">
<li ng:repeat="typedef in typedefs">{{typedef.name}}</li>
</ul>
</div>
</div>
The problem does not really come from the instantiation (which works fine), but from the initialization. In fact, when the new "li" appears when I push the "Add" button, the text "New type" (initialized in the controller) does not appear.
I think it is about the scope or something like that, but I can't really find how to fix this.
I wanted to know if this method seems correct, and also how could I fix the problem I have.
Thanks
Reading the code, I understand that you want to create typedefs dynamically and those typedef items have to be controlled by an AlkeTypeDefController.
In that case I would create AlkeTypeDefController using ng:controller directive, so you don't need to create the controller programmatically, because then you would need to attached it to the view and that's just what the ngController directive does for you.
Notice AlkeTypeDefListController does not create a AlkeTypeDefController controller, this is done in the view
Demo on Plunker
Controllers:
.controller('AlkeTypeDefListController', ['$scope', function($scope) {
var primitives = [];
$scope.typedefs = primitives;
$scope.addTypeDef = function() {
var typeDef = { name: 'New Type' };
$scope.typedefs.push(typeDef);
}
}])
.controller('AlkeTypeDefController', ['$scope', function($scope) {
$scope.addField = function() {
alert('add Field');
}
}]);
View (notice how ng-controller directive is specified in li element):
<div id="typedefs-editor" ng:controller="AlkeTypeDefListController">
<button ng:click="addTypeDef()">Add</button>
<button>Remove</button>
<div id="typedef-list">
<ul class="list">
<li ng:repeat="typedef in typedefs" ng:controller="AlkeTypeDefController">
{{typedef.name}}
</li>
</ul>
</div>
In the code above, ngRepeat is going to create a new $scope for each typedef. AlkeTypeDefController then decorates that scope with functions and values.
I hope it helps
When you call $controller("AlkeTypeDefController") it will essentially call new on the AlkeTypeDefController constructor and give you back the return value not the scope. You are assign the name attrubute to the scope though so it is not being accessed in your html when you have typedef.name.
Try changing your AlkeTypeDefController to this:
Alke.controller('AlkeTypeDefController', function() {
this.name = "New Type";
this.fields = [];
this.addField = function() {};
});
Then you can instantiate it with: var controller = $controller("AlkeTypeDefController"); and you shouldn't need to worry about creating nested scopes.
If I get what you're saying correctly then I think I'd try to leverage the power of a custom directive here instead of dynamically generating controllers.
plunker
Controller:
Alke.controller('alkeTypeDefListController', ['$scope', '$controller',
function($scope, $controller) {
var primitives = [];
var addTypeDef = function() {
$scope.typedefs.push({
name: 'new name'
});
};
var removeTypeDef = function(){
$scope.typedefs.pop();
};
var properties = {
typedefs: primitives,
addTypeDef: addTypeDef,
removeTypeDef: removeTypeDef
};
// Add some properties to the scope
angular.extend($scope, properties);
}
]);
Directive:
Alke.directive('alkeTypeDef', function() {
return {
restrict: 'A',
scope: {
typeDef: '=alkeTypeDef'
},
template: '{{typeDef.name}}',
link: function(scope, element, attr) {
var properties = {
fields: [],
addField: function() {
}
};
angular.extend(scope, properties);
}
};
});
HTML:
<div ng-app='Alke'>
<div id="typedefs-editor" ng-controller="alkeTypeDefListController">
<button ng-click="addTypeDef()">Add</button>
<button ng-click="removeTypeDef()">Remove</button>
<div id="typedef-list">
<ul class="list">
<li alke-type-def='typedef' ng-repeat="typedef in typedefs"></li>
</ul>
</div>
</div>
</div>
If you want a controller then you can use one in the directive instead of a linking function.