I am new to angularjs. I want to dynamically add directives to a div based the titles from controller.. Below is what i tried...
Below is the directives and factory i used
QtTemplate.directive("shortanswer",function()
{
return{
restrict:"A",
templateUrl:'assets/directives/shortAnswer.html'
}
})
Template.factory("questionList",function()
{
var questionList={};
questionList.testid="1";
questionList.name="Maths";
questionList.Questions =
[
{"title":"truefalse"},
{"title":"shortanswer"}
]
return questionList;
})
Template.controller("questionControl",['$scope','questionList',function($scope,questionList)
{
$scope.name = questionList.name;
$scope.id = questionList.testid;
$scope.Questions = questionList.Questions;
}])
Template.directive('dynamicDirective',function($compile)
{
return {
restrict: 'A',scope:{},
replace: false,
terminal: true,
priority: 1000,
link:function(scope,element,attrs)
{
element.attr(scope.$eval(attrs.dynamicDirective),"");
element.removeAttr("dynamic-directive"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-dynamic-directive");
$compile(element)(scope);
}
};
});
QtTemplate.directive("shortanswer",function()
{
return{
restrict:"A",
templateUrl:'assets/directives/shortAnswer.html'
}
})
Below is how i use ng-repeat
<div ng-repeat="Questions in Questions" >
<div dynamicDirective="{{Questions.title}}" ></div>
</div>
This is not working
use-kebab-case-in-html
<div ng-repeat="Question in Questions" >
<div dynamic-directive="{{Question.title}}" ></div>
</div>
You need to write dynamic-directive instead of dynamicDirective in your HTML. Camelcase in Javascript needs to be written as snakecase kebab-case in HTML.
Here you are:
<div ng-repeat="Question in Questions" >
<div dynamicDirective="{{Question.title}}" ></div>
</div>
Hope this helps.
Related
There's this template that I call multiple times on the same page:
<div ng-controller="templateController">
<div class="source">
<div ng-repeat="item in info">
<div class="content" data-value="{{item.ID}}">{{item.name}}</div>
</div>
</div>
<br style="clear:both" />
<div class="receiver"></div>
</div>
and then I want to select all the elements with class="content" within each template scope in order to manipulate them.
How can I achieve this using JS?
EDIT :
Plunker
In this planker the console.log should print "1" twice and its printing "1" and then "2" when the template loads the second time
After more explanation here is a working example:
https://plnkr.co/edit/n5GOd6MDLyvG4ZAsuLvf?p=preview
The main idea is creating 2 lists and iterating over both and just moving data around between them on click.
angular.module("demo", []);
angular
.module("demo")
.controller("demoController", ["$scope", function($scope) {
}]);
angular
.module("demo")
.controller("templateController", ["$scope", "$timeout", function($scope, $timeout) {
$scope.sourceList = [
{
name: "john",
ID: 1
},
{
name: "Edward",
ID: 0
},
{
name: "Carl",
ID: 2
}
];
$scope.receiverList = [
{
name: "Bob",
ID: 1
}
];
$scope.moveToReceiver = function(item){
$scope.receiverList.push(item);
$.each($scope.sourceList, function(i){
if($scope.sourceList[i].name == item.name){
$scope.sourceList.splice(i, 1);
return false;
}
});
}
}]);
Most of the time you do not want to do DOM manipulation in Angularjs and instead hook into events with your controller. If you have to do DOM manipulation in AngularJS you would use directives
Docs on Creating a Directive that Manipulates the DOM
You could then use your link function to grab the children of your directive's element
function link(scope, element, attrs) {
var content = angular.element(element[0].querySelector('.content'));
}
https://stackoverflow.com/a/17329630/2033671
I have an external JSON file which is a collection of buttons/actions like this
{
"foo1":{
"name":"productDetails",
"displayAs":"button",
"action":"viewDetails()"
},
"foo2":{
"name":"sell",
"displayAs":"button",
"action":"sellProduct()"
}
}
Later, in my view, I create a <div> using ng-repeat for each object contained in that JSON.
My question is, can I set the ng-click property as it it in the JSON file like this?
<li ng-repeat = "field in fields">
<div ng-click = field.action > {{field.name}} </div>
</li>
you can do this using ng-bind-html.
<li ng-repeat = "field in fields">
<div bind-html-compile ng-bind-html="trust(field)" > </div>
</li>
Add bind-html-compile directive to compile the DOM so that changes will effect to DOM elements
Directive
.directive('bindHtmlCompile', ['$compile', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(function() {
return scope.$eval(attrs.bindHtmlCompile);
}, function(value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
In trust function pass the object as an argument and return the html trust element
$scope.trust = function(html) {
return $sce.trustAsHtml('<div ng-click = "' + html.action + '" > {{field.name}} </div>');
}
Demo
angular.module("app",[])
.controller("ctrl",function($scope,$sce){
$scope.viewDetails = function(){console.log("viewDetails")}
$scope.sellProduct = function(){console.log("sellProduct")}
$scope.fields = {
"foo1":{
"name":"productDetails",
"displayAs":"button",
"action":"viewDetails()"
},
"foo2":{
"name":"sell",
"displayAs":"button",
"action":"sellProduct()"
}
}
$scope.trust = function(html){
return $sce.trustAsHtml('<div ng-click = "'+html.action+'" > {{field.name}} </div>');
}
}).directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<li ng-repeat = "field in fields">
<div bind-html-compile ng-bind-html="trust(field)" > </div>
</li>
</div>
My directive works fine, but I'd like to use it in ng-click However, the function inside link can't be triggered.
http://jsfiddle.net/ovzyro1f/
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span>{{item.name}}</span>
<button ng-click="">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.editedItem = null;
$scope.items = [{
name: "item #1",
thing: "thing 1"
}, {
name: "item #2",
thing: "thing 2"
}, {
name: "item #3",
thing: "thing 3"
}];
$scope.show = false; //ignore this line
}
var editer = angular.module('editer', []);
editer.directive('sibs', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
})
scope.myClick = function() {
element.parent().children().addClass('unclicked');
element.removeClass('unclicked');
}
},
}
});
I'd like to call the function in ng-click please see this one http://jsfiddle.net/ovzyro1f/2/ to remove sib from div ng-repeat="item in items" class="wrap"
<button ng-click="myClick()">click</button>
You should avoid to manipulate the DOM like we do in jQuery.
In Angular we think differently: it's the data which transforms automatically the DOM when the data changes (https://docs.angularjs.org/guide/databinding). Most of the time you never have to make the changes manually.
In doing so, you generally don't need to use the link function. You can have a controller (like in your example) or a directive with a controller (https://docs.angularjs.org/guide/directive).
Finally I just modified a little bit your controller and your template.
HTML
<div ng-app="editer" ng-controller="myCtrl" class="container">
<div class="parents">
<div ng-repeat="item in items" class="wrap" sibs>
<span ng-class="{ unclicked: !item.selected }">{{ item.name }}</span>
<button ng-click="selectItem(item)">click</button>
</div>
</div>
</div>
JS
function myCtrl($scope) {
$scope.items = [...];
$scope.selectItem = function (item) {
// reset all the items
$scope.items.forEach(function (i) {
i.selected = false;
});
// set the new selected item
item.selected = true;
}
}
I have my ng-repeat in div tag:
<div ng-repeat="x in names">
<h1>{{x.product}}</h1>
<h2>{{x.brand}}</h2>
<h3>{{x.description}}</h3>
<h4>{{x.sum}}</h4>
<h5>{{x.productid}}</h5>
</div>
<button ng-click="addToCart()">Add To Cart</button>
And my AngularJS script:
$scope.addToCart = function () {
$scope.productId = $scope.x.productid;
$http.post("api/shoppingCart/" + $scope.productId);
}
My problem is what I can't access/get {{x.productid}} value for my $scope.productid.
You can pass the item reference(x) - or the production id x.productid to the addToCart method
var app = angular.module('my-app', [], function() {})
app.controller('AppController', function($scope) {
$scope.names = [{
product: 'a',
productid: 1
}, {
product: 'b',
productid: 2
}, {
product: 'c',
productid: 3
}, {
product: 'd',
productid: 4
}];
$scope.addToCart = function(x) {
$scope.productId = x.productid;
$http.post("api/shoppingCart/" + $scope.productId);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<div ng-repeat="x in names">
<h1>{{x.product}}</h1>
<h2>{{x.brand}}</h2>
<h3>{{x.description}}</h3>
<h4>{{x.sum}}</h4>
<h5>{{x.productid}}</h5>
<button ng-click="addToCart(x)">Add To Cart</button>
</div>
<p>productId: {{productId}}</p>
</div>
change button like this <button ng-click="addToCart(x.productid)">Add To Cart</button> and your function is like
`$scope.addToCart = function (productid) {
$http.post("api/shoppingCart/" + productid);
}`
I think the best possibility for you to make it work is to add the button in the ng-repeat:
HTML:
<div ng-repeat="x in names">
<h1>{{x.product}}</h1>
<h2>{{x.brand}}</h2>
<h3>{{x.description}}</h3>
<h4>{{x.sum}}</h4>
<h5>{{x.productid}}</h5>
<button ng-click="addToCart(x.productid)">Add To Cart</button>
</div>
JS:
$scope.addToCart = function(id) {
$scope.productId = id;
$http.post("api/shoppingCart/" + $scope.productId);
};
Your addToCart is out of
<div ng-repeat="x in names">
<h1>{{x.product}}</h1>
<h2>{{x.brand}}</h2>
<h3>{{x.description}}</h3>
<h4>{{x.sum}}</h4>
<h5>{{x.productid}}</h5>
<button ng-click="addToCart(x.productid)">Add To Cart</button>
</div>
in JS file
$scope.addToCart = function (pid) {
$http.post("api/shoppingCart/"+ pid);
}
ng-repeat creates new scopes for each iteration. In you case, the x is in one of the child scopes of your ng-repeat.
The only place where you have an access to this scope is inside your view, so what you can do is the following :
<button ng-click="addToCart({{x.productId}})">Add To Cart</button>
and modifying your controller like that :
$scope.addToCart = function (productId) {
//$scope.productId = $scope.x.productid;
$http.post("api/shoppingCart/" + productId);
}
You may be wondering why you have access to a parent scope inside the view, you can google angular prototypal inheritance for more info
You can pass productId as a function parameter and you can use ng-bind instead of {{}} that's give you better performance and not rendered unwanted expression in DOM. if you use {{}} some time you see {{x.product}} in DOM while loading.
in HTML:
<div ng-repeat="x in names">
<h1 ng-bind="x.product"></h1>
<h2 ng-bind="x.brand"></h2>
<h3 ng-bind="x.description"></h3>
<h4 ng-bind="x.sum"></h4>
<h5 ng-bind="x.productid"></h5>
<button ng-click="addToCart(x.productid)">Add To Cart</button>
</div>
in controller:
$scope.addToCart = function (productId) {
$http.post("api/shoppingCart/"+ productId);
}
How can I fill an input text field with a dropdown-menu ?
Text Input:
<input type="text" ng-model="storagePlaceModel" lms-dropdown class="form-control lms-input-text-disable lms-dropdown-toggle" id="storagePlace" placeholder="Standort" autocomplete="off" readonly>
Own written dropdown:
<div class="lms-dropdown">
<div class="lms-dropdown-scrollbar">
<li ng-repeat="data in data_input_fields.input_cat_music_book_storage_place">
<span>{{data.input_value}}</span>
<i ng-show="storagePlaceModel == data.input_value" class="glyphicon glyphicon-ok"></i>
</li>
</div>
</div>
If I select an li element I want to update the storagePlaceModel with the li value.
Updated question:
Because I have more than one of these text inputs with dropdowns I need a solution where the conroller/directive does not know the model name exactly.
Directive could look like:
lmsApp.directive('updateInputField', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
$(elem).click(function(e) {
// Read out the model name of the html text input field
});
}
};
});
Thank you for your help! I would appreciate every answer.
I've edited the entire question to create a directive to wrap your desired structure. You'll pass to the directive the model you want, and that way, each model will be independent on different directive usages:
myApp.directive('myDirective', function() {
return {
restrict: "E",
scope: {
model: "=",
datas: "="
},
templateUrl: "directive.html",
link: function(scope, element, attr) {
scope.updateValue = function(val) {
scope.model.storagePlaceModel = val;
}
}
}
});
The directive.html contains your text input and the dropdown.
Controller:
function MyCtrl($scope) {
$scope.wrapper = {};
$scope.wrapper2 = {};
$scope.wrapper3 = {};
$scope.datas = [
{ "input_value" : "1" },
{ "input_value" : "2" },
{ "input_value" : "3" },
{ "input_value" : "4" }
];
}
HTML usage:
<div ng-app="myApp" ng-controller="MyCtrl">
<my-directive model="wrapper" datas="datas"></my-directive>
<my-directive model="wrapper2" datas="datas"></my-directive>
<my-directive model="wrapper3" datas="datas"></my-directive>
</div>
Working Fiddle