button click does not work - angularjs

took this from the AngularJS tutorial egghead.io, it never seems to hit the logchore function:
app.controller("ChoreCtrl", function($scope) {
$scope.logChore = function() {
alert("is done");
}
})
jsfiddle: http://jsfiddle.net/dingen2010/sFnAr/1/

Just replace ngclick="done()" to ng-click="done()"
template: '<input type="text" ng-model="chore">' + ' {{chore}}' + ' <div class="button" ng-click="done()">i m done</div>'
taken from the fiddle

Related

Use a directive to display scope value and update scope

I have an array of vertex values that I loop over and display to the user.
The values are stored as a space delimited string, like this:
vrtx = ["10.3 20.3 10.5", "13.2 29.2 3.0", "16.3 2.3 20.2", ...]
I want to provide a friendly interface to the user to edit the values.
This requires me to split the strings into three separate numbers and put them into three individual inputs.
I want to create a directive, as I am not able to store the values as separate values, but after the editing is done, I want to join the values back and update the scope to store the new values as strings.
This is the directive I have so far:
myApp.directive('fieldValue', function () {
return {
scope: {
field:'='
},
link: function (scope, element, attrs) {
var fields = scope.field.split(' ');
element.html("<input type='number' value='"+ fields[0] +"' class='vertex-input'/><input type='number' value='"+ fields[1] +"' class='vertex-input'/><input type='number' value='"+ fields[2] +"' class='vertex-input'/>");
}
}
});
This splits the value and shows the three inputs with the individual values, but my question is how do I join these values and save them back to the scope? Any suggestions would be greatly appreciated! TIA
You could use formatters/parsers to achieve what you want, but you may have to adjust your input to use ngModel, and I'm not quite sure how it would work with 3 separate input fields:
myApp.directive('fieldValue', function () {
return {
scope: {
field:'='
},
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var fields = scope.field.split(' ');
element.html("<input type='number' value='"+ fields[0] +"' class='vertex-input'/><input type='number' value='"+ fields[1] +"' class='vertex-input'/><input type='number' value='"+ fields[2] +"' class='vertex-input'/>");
ngModel.$formatters.push(function(value){
//return the value as you want it displayed
});
ngModel.$parsers.push(function(value){
//return the value as you want it stored
});
}
}
});
This way gives you a lot more flexibility in my opinion to use any strategy you want. Read more about it here: https://alexperry.io/angularjs/2014/12/10/parsers-and-formatters-angular.html
And, more official docs here: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
I think that your question is the continuation of this one, so I would like to suggest you a solution without a directive, based on the answer of your precedent question.
When the value of an input is modified, call a function that merged all your inputs:
<input type="text" ng-model="arr[0]" ng-change="update()" />
<input type="text" ng-model="arr[1]" ng-change="update()" />
<input type="text" ng-model="arr[2]" ng-change="update()" />
$scope.update = function() {
$scope.myString = $scope.arr[0] + ' ' + $scope.arr[1] + ' ' + $scope.arr[2];
}
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', ['$scope', function($scope) {
$scope.myString = 'My awesome text';
$scope.arr = $scope.myString.split(/[ ]+/);
$scope.update = function() {
$scope.myString = $scope.arr[0] + ' ' + $scope.arr[1] + ' ' + $scope.arr[2];
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<input type="text" ng-model="arr[0]" ng-change="update()" />
<input type="text" ng-model="arr[1]" ng-change="update()" />
<input type="text" ng-model="arr[2]" ng-change="update()" />
<br/>Result: {{myString}}
</div>
</div>
Try it on JSFiddle.

AngulaJs ng-click in expression html [duplicate]

This question already has answers here:
How to make ng-bind-html compile angularjs code
(8 answers)
Closed 3 years ago.
I am displaying HTML in a AngularJS expression.
In My HTML
<div ng-bind-html="myText"></div>
And In My Controller
$scope.myText = $sce.trustAsHtml("<div class='my-style' ng-click='onClick(10)'>click me</div>");
$scope.onClick = function(value)
{
console.log("onClick: value:"+value);
}
But I am not able to get the click event
UPDATE
In one of the question I see it has be described about how to add HTML tags in the text or how to compile HTML. But my question is related to how to get ng-click event from the complied HTML. I don't see any of the answers providing this solution.
Sorry for again writing the code :
First give a unique id to your div :
<div id="test"></div>
Then Change that code to below one:
var myText = "<div class='my-style' ng-click='onClick(10)'>click me</div>";
var element = angular.element(document.querySelector('#test'));
var generated = element.html(myText);
$compile(generated.contents())($scope);
This will manage your code to work. cheers!
Use directive to implement this functionality. Check demo here http://plnkr.co/edit/tTgVCMt7JqvaQOrjhLLL?p=preview
JS:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.testClick = function(value) {
alert("onClick: value:"+value);
};
});
app.directive("clickDynamic", function(){
return {
template:"<div class='my-style' ng-click='testClick(10)'>click me</div>"
};
});
HTML:
<body ng-controller="MainCtrl">
<div click-dynamic></div>
</body>
I have append the html using id or class
var add = angular.element( document.querySelector( '#updatechoose' ) );
add.append('<div class="form-group" ng-repeat="updatedata in getdata">'+
'<div>'+
'<input type="file" id="imgupload" ng-model="attachment" imgepath="{{updatedata.imagepath}}" imgname="{{updatedata.imagename}}" name="file[]" style="padding-left: 15px;" class="imgupload col-md-4 cropit-image-input" />'+
'<i class="glyphicon glyphicon-trash" **ng-click="remove()"**></i>'+
'</div>'+
'</div>');
One solution is to use plain javascript and get the scope. This way you can call your inside methods.
<div ng-bind-html="myText"></div>
$scope.myText = $sce.trustAsHtml( '<div class="button" onClick="var scope = angular.element(this).scope();scope.testClick(10);">click me</div>' );
Please note that I'm using onClick instead of ng-click, then I get the scope, this way I can call the inner methods.
It's a dirty solution, but maybe it can help you.
I've added a snippet, which shows an implementation via a directive and the $compile service.
angular.module('TestApp', [])
.directive('compileDir', function($compile) {
return {
restrict: 'E',
scope: {
dynTpl: '='
},
template: 'Num clicked: {{numClicked}}',
link: function(scope, elem) {
scope.numClicked = 0;
scope.click = function() {
scope.numClicked++;
};
var tpl = $compile(scope.dynTpl)(scope);
elem.append(tpl);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.12/angular.min.js"></script>
<div ng-app="TestApp">
<compile-dir dyn-tpl="'<button ng-click=\'click()\'>Click</button>'"></compile-dir>
</div>

ngTagsInput with autocomplete in a form and add to db

I've been trying to use ngTagsInput with its Autocomplete feature (mbenford.github.io/ngTagsInput/demos) in a html form to submit recipes in Mongodb.
Basically, I'm using ngTagsInput with Autocomplete to query my ingredients db and display ingredients tags in the 'Ingredients in the recipe'.
It works fine, up until I save the recipe, and the ingredients are not saved.
I know where the problem is but I haven't found a solution yet.
Here is the 'ingredients' field of my add recipe page without ngTagsInput, just a normal text field:
<div class="form-group">
<label for="ingredients">List of ingredients</label>
<input type="text" class="form-control" id="ingredients" ng-model="form.ingredients">
</div>
And here is the 'ingredients' field using ngTagsInput (Working fine, but not saving):
<div class="form-group" ng-controller="recipeApiController">
<tags-input for="Ingredients" id="Ingredients" ng-model="tags" display-property="ingredientID" placeholder="Commence à taper le nom d'un ingrédient..." add-from-autocomplete-only="true">
<auto-complete source="loadTags($query)"></auto-complete>
</tags-input>
</div>
Because I'm replacing ng-model="form.ingredients" with ng-model="tags" required to use ngTagsInput, those ingredient tags are not saved when clicking my "Add recipe" button.
Here is the "save to db" part of my recipeApiController, used on the "add recipe" form page:
$scope.addToDatabase = function(){
RecipeApi.Recipe.save({}, $scope.form,
function(data){
$scope.recipe.push(data);
},
function(err){
bootbox.alert('Error: ' + err);
});
}
Do you have any idea how I could fix that, and save those tags?
Thanks in advance guys. I didn't want this post to be too long but if you need more info, code, I'll be super reactive to provide it. This would help me greatly.
I found a solution in another post:
https://stackoverflow.com/a/38383917/6858949
Basically, I couldn't get those tags to save because ng-model didn't work inside the <tags-input> tags. I therefore used this guy's directive to change the <tags-input> to an attribute:
<div elem-as-attr="tags-input"></div>
Here's the directive code:
app.directive('elemAsAttr', function($compile) {
return {
restrict: 'A',
require: '?ngModel',
replace: true,
scope: true,
compile: function(tElement, tAttrs) {
return function($scope) {
var attrs = tElement[0].attributes;
var attrsText = '';
for (var i=0; i < attrs.length; i++) {
var attr = attrs.item(i);
if (attr.nodeName === "elem-as-attr") {
continue;
}
attrsText += " " + attr.nodeName + "='" + attr.nodeValue + "'";
}
var hasModel = $(tElement)[0].hasAttribute("ng-model");
var innerHtml = $(tElement)[0].innerHTML;
var html = '<' + tAttrs.elemAsAttr + attrsText + '>' + innerHtml + '</' + tAttrs.elemAsAttr + '>';
var e = hasModel ? $compile(html)($scope) : html;
$(tElement).replaceWith(e);
};
}
}
});
I don't think this is optimal, but with my current knowledge of code, I'm thankful I found this solution. ✌🏼
EDIT:
I am now using ui-select: https://github.com/angular-ui/ui-select
And definitely recommend it
EDIT:
I put the code in the code box

AngularJs. Change model in directive too slow

As a newbie in AngularJs can't understand the problem.
This is a simple example that shows the action. Actually about 50 items and after clicking on "Without the string" after clicking on the elements "String #" to visually remove the item takes about 1-2 seconds.
Fiddle
I have the controller code:
testApp.controller('TestController', ['$scope', function ($scope) {
$scope.category = [
{id:1, name: "Category 1"},
...
];
$scope.items = [
{id: 1, category: {id: 1},name: "Test 1"},
...
];
$scope.list = [
{id: 1,name: "String 1"}
...
];
angular.forEach($scope.category, function(categoryItem, i) {
categoryHash[categoryItem.id] = i;
});
angular.forEach(menuItems, function(item) {
var catCategory = categoryHash[item.category.id];
if (!$scope.category[catCategory].items) {
$scope.category[catCategory].items = [];
}
$scope.category[catCategory].items.push(item);
});
}])
Directive code:
.directive('listItems', function() {
return {
restrict: 'E',
scope: {
listArray: '=',
listItemId: '=',
listFlag: '='
},
template: '<ul>' +
'<li ng-repeat="listStr in listArray track by listStr.id">' +
'<input type="radio" ' +
'id="list_{{ listItemId }}_{{ listStr.id }}" name="list_{{ listItemId }}" ' +
'ng-model="$parent.$parent.item.string" ng-value="listStr" ng-change="stringSelect()">' +
'<label for="list_{{ listItemId }}_{{ listStr.id }}" ng-bind="listStr.name"></label>' +
'</li>' +
'<li>' +
'<input type="radio" ' +
'id="list_{{ listItemId }}_0" name="list_{{ listItemId }}" ' +
'ng-model="$parent.$parent.item.string" ng-value="" ng-change="stringSelect()">' +
'<label for="list_{{ listItemId }}_0">Without string</label>' +
'</li>' +
'</ul>',
link: function(scope, iElement, iAttrs) {
scope.stringSelect = function() {
scope.listFlag = false;
};
}
}
})
Template:
<div ng-app="test" ng-controller="TestController">
<div ng-repeat="collection in category track by $index" >
<h3 ng-bind="collection.name"></h3>
<ul>
<li ng-repeat="item in collection.items track by $index">
<strong ng-bind="item.name"></strong>
<span ng-if="item.string" ng-bind="item.string.name"></span>
<button ng-click="addString = true" ng-hide="addString">Add String</button>
<div ng-if="addString">
<list-items
list-array="list"
list-item-id="$parent.item.id"
list-flag="$parent.addString"></list-items>
</div>
</li>
</ul>
</div>
</div>
First remove all $parent, they're not needed, angular will look for value in the parent scope himself and if your directive is used inside others directive, this won't even point to the right scope.
2nd with angular you can create a lot of watches, this can slow down really your application :
when angular watch for list, he watches for deep inspection. This may slow down as hell things when you have some list with complex objects.
When you use {{}} or ng-bind, it creates a watch
3 : Are you using an old version of IE ? i think the 8 one was really slow with angular 1.2 and scopes.
So you may try to not use default watch for array but handle it yourself watching only for length for instance. For the {{}} part, since 1.3 you can prefix your binding by '::' to have a One time binding (https://docs.angularjs.org/#!/guide/expression) so he won't watch for changes
4 : When you use ng-if it destroy the object when not needed and recreate it when the condition is true again maybe you should use ng-show instead ng-if there
<div ng-if="addString">

How can I force a tab stop in my AngularJS directive?

I am creating a custom checkbox directive for my app, however, tab skips right over it. How can I force the tab key to stop at my directive?
Code: (Note: Plunker seems to be on the fritz right now...)
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div ng-controller='TestCtrl'>
<input type='text' />
<test-checkbox ng-model='isChecked'></test-checkbox>
<input type='text' />
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script>
var app = angular.module("testApp", []);
app.controller('TestCtrl', ['$scope', function($scope) {
$scope.isChecked = true;
}]);
app.directive('testCheckbox', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: {
isChecked: "=ngModel"
},
link: function (scope, element) {
var html =
"<div class='slgCheckbox' ng-click='onClick(isChecked = !isChecked)'>" +
$(element).html() +
"<i class='checkboxIcon glyphicon' ng-class='{\"glyphicon-check\" : isChecked === true, \"glyphicon-unchecked\" : isChecked === false }'></i>" +
"</div>";
var compiledHtml = $compile(html)(scope);
$(element).replaceWith(compiledHtml);
}
}
}]);
</script>
</body>
</html>
You can add a tabindex attribute to the div element and that will make it selectable.
In your template above, you can do something like this:
var html =
"<div tabindex='1' class='slgCheckbox' ng-click='onClick(isChecked = !isChecked)'>" +
$(element).html() +
"<i class='checkboxIcon glyphicon' ng-class='{\"glyphicon-check\" : isChecked === true, \"glyphicon-unchecked\" : isChecked === false }'></i>" +
"</div>";
Note that the tabindex attribute must go on the div element.
Thought, this is a bit of a simplistic answer for 2 reasons:
You will not always want a tabindex of 1. It will need to be relative to other elements of the page.
Just because it can be given focus, pressing space will not cause the onclick handler to be invoked. You will need some custom logic for that through using the onkeypress handler.
Your best bet is to use an input element as best as you can so that you can leverage native select handling.
For more information see msdn.

Resources