I am new to angularjs, just like many other developer, I was jquery developer. Now I have a question about directive.
example: if i have a directive like this:
app.directive('mydiv',function(){
return{
restrict:'AE',
template:'<div><ul><li></li><li></li><li></li></ul></div>', //of course in real case it will be use ng-repeat
link:function(scope,element,attr){
}
}
})
the confuse I have is,if I need to access any element,in jquery we can use $(this), how can we do it with angular way? Can I do like this:
link: function (scope, element, attrs) { //when hover show the delete icon for each msg and apply highlight class to it.
element.find('li').hover(
function(){
$(this).addClass('highlight');
scope.deleteicon=true;
},
function(){
$(this).removeClass('highlight');
scope.deleteicon=false;
});
}
You can access the element as the 1st argument (element argument in your case) of the link function itself. If you are using jquery with angular and loading jquery before angular the element` will be wrapped in jquery wrapper, i.e it will be a jquery object. If not angular uses a lighter subset of jquery called jqlite. It only provided a minimal functionality.
See element for details.
Instead of binding hover event manually you should use angular event binding and use ng-class instead of add/remove class. That way you perform things angular way and you do not need to manually invoke digest cycle via scope.$apply() for DOM updates with respect to scope updates (Which you need to do in the hover pseudo event in your example to reflect DOM updates based on scope property deleteicon).
An example implementation of your directive will look like this. There are numerous ways you can do this using angular built-in directives itself.
.directive('mydiv',function(){
return {
restrict:'AE',
scope:{items:"="}, //<-- data bound from parent scope via attribute
template:'<div><ul><li ng-mouseenter="toggleClass(true)" ng-mouseleave="toggleClass(false)" ng-class="{'highlight': action.deleteicon}" ng-repeat="item in items">{{item.name}}</li></ul></div>', //of course in real case it will be use ng-repeat
link:function(scope,element,attr){
scope.action = {deleteicon :true};
scope.toggleClass = function(show){
scope.action.deleteicon = show;
}
}
}
});
scope:{items:"="}, sets up 2 way binding if you wish to bind the data from the parent scope via attribute.
Instead of repeating li use a data model, say an array of items and use ng-repeat instead of duplicating the tag (unless you need to do it for sure). ex:- ng-repeat="item in items"
Use angular event bindings instead of binding the events manually, hover is nothing but nouseenter/mouseleave. So you can use respective angular directives and bind a function on the scope. i.e ng-mouseenter="toggleClass(true)" ng-mouseleave="toggleClass(false)".
Use ng-class bound to a scope variable to toggle the class. Let angular manage DOM manipulation for toggling the css class on the element. You just worry about the data being bound. i.e ng-class="{'highlight': action.deleteicon}"
You can find official documentation on angular built in directives/components.
Well, you used hover, but, you can use the MouseOver Directive.
As the following example:
<li ng-mouseover="high = true" ng-class="{'highlight':high}">Content</li>
Look at this plkr
Markup:
<div class="row" ng-app="myapp" ng-controller="myctrl">
<div class="col-lg-3 col-lg-push-3">
<ul class="list-group">
<li class="list-group-item" ng-init="myclass='hide'" ng-repeat="item in items"
ng-mouseenter="myclass = 'show'" ng-mouseleave="myclass='hide'">
<span>{{item}}</span>
<span ng-class="'pull-right glyphicon glyphicon-edit '+myclass"></span>
</li>
</ul>
</div>
</div>
Script:
angular.module('myapp', [])
.controller('myctrl', function ($scope) {
$scope.items = ['abc', 'xyz', 'klm'];
});
Related
In html i have a div
<div class="row" id='textdrag{{$index}}'>
I need to use the id to display context menu based on id,like.
$("#textdrag2").contextmenu(function (event) {
setcontextposition("text");
});
But for some reason the context menu is not displayed.
Its works just fine when i just use id="textdrag" without the $index
Why don't you append to the class instead and that way you wont have a flaky selector on a dynamic id. It is generally not a good idea to use a selector that will change.
<div class="row text-drag-cls" id='textdrag{{$index}}'>
$(".text-drag-cls").contextmenu(function (event) {
setcontextposition("text");
});
Hope that helps
Change your html to the following
<div class="row" id="{{ 'textdrag' + $index }}"></div>
Why it isn't working by current approach?
Basically your javascript is getting fire as soon as it loads, but that instance textdrag{{$index}} haven't evaluated $index value. Since the event haven't got attached to that DOM.
I'd rather recommend you to create directive for this functionality, which generally viable solution to play with DOM in AngularJS. By this approach you don't need to specify unique identifier to each element.
<div class="row" context-menu>
...
</div>
Directive
app.directive('contextMenu', [function(){
return {
link: function(scope, element, attrs) {
element.contextmenu(function (event) {
setcontextposition("text");
});
}
}
}])
I have posted more detailed question this is much clear and straightforward
here
hello, I am trying to set the value of list element to false by using knockout
this is my HTML
<li>
<a onclick="log(this)" data-bind="visible: true, text: $data"></a>
</li>
is there a way to say something like this :
myViewModel.items()[i].setVisible(false);
Don't set the visible binding to true set it to the variable you define in your viewModel. Also you can access individual elements of an observable array through the foreach binding. Lastly, if you want to use $data you can access the property of the individual array object directly using the "." operator. The documentation I referenced at the end of my post has more information. See below:
<div data-bind="foreach: shouldShowMessageArray">
<div data-bind="visible: $data.shouldShowMessage">
Message goes here.
</div>
</div>
<script type="text/javascript">
var myViewModel;
$(document).ready(function(){
myViewModel = new viewModel();
function viewModel() {
this.shouldShowMessage = ko.observable(false) // Message initially visible
this.shouldShowMessageArray = ko.observableArray([
{shouldShowMessage: ko.observable(true)},
{shouldShowMessage: ko.observable(false)}
]);
}
ko.applyBindings(myViewModel);
});
</script>
Knockout foreach / $data documentation
If you are looking to load the page with a starting value of visible:false, you may run into some issues with the "visible" binding.
I have had trouble with the "visible" binding when I want an element to be hidden on page load, and then made visible after some action taken by the user. You will see a flash of the hidden element while your knockout.js logic loads if you are using the visible binding. If you try to use inline CSS to set display:none on the element, then it will never become visible even when the KO logic results in a visible:true value. This is because KO applies the CSS rules after it has handled changing the element.style.display value.
One solution to this issue that I have found is to set up a CSS class that well set display to none, and then use a data binding to conditionally apply that CSS rule based on an observable.
CSS:
.hideMe { display: none; }
HTML:
<div class="hideContent" data-bind="css: { hideContent: !yourObservable() }">
<p>Content</p>
</div>
I have a Knockout.js viewmodel with a list of products, which are populated by an ASP.NET Web API call:
// Define viewmodel
var myViewModel = {
products = ko.observableArray([]);
};
// Apply knockout bindings
ko.applyBindings(vm);
// Function that obtains product data
function listProducts(viewmodel) {
var productsQuery = "api/products/get";
// Send an AJAX request
$.getJSON(productsQuery).done(viewmodel.products);
}
When I call listProducts, each element is added successfully to the ff. HTML <ul>:
<ul data-bind="foreach: products">
<li class="item">
<data-bind="text: productName">
</li>
</ul>
However, when each item is added, the jQuery function that applies to my .item elements:
$(".item").click(function () {
$(this).toggleClass("selected");
});
does not get applied to these newly added elements.
Question: How do I add elements to the Knockout.js observableArray that will also inherit their corresponding jQuery methods?
Basically you need to go back to the knockout docs and read, read, read! Forget doing this with jquery, it's just not the ko way. Knockout frees you from this kind of coding style, you just need to grok it.
hint:
use the click binding and the css binding
http://knockoutjs.com/documentation/css-binding.html
As an aside you can also do this:
$.getJSON(productsQuery).done(viewmodel.products);
I want to do a simple thing:
I have an app, which has certain divs that in need to show (Only the specific one) and hide if clicked somewhere outside of it (All with the specific class for example).
This is easy using jquery:
$('some-class').style('display','none') //psuedo code here
How should i do the same with angular js?
A specific reason to do so:
I want to build a simple select drop-down (I don't want to use one that exist, i want to understand this...), when i click one, it suppose to open a div right beneath the button, when i click outside of it, it suppose to close not only this one, but some other elements like it in the app.
one point worth mentioning: Not all my select boxes are pre-rendered, some are dynamically added (inside directive), so not all of the are in the $scope, but inside them directives which has isolated scope.
Its better to make directives for these kind of things:
Make a directive for toggleDisplay as following
app.directive('toggleDisplay', function() {
return function(scope, element) {
element.bind('click', function() {
element.toggleClass('unneeded-class'); // or something else
});
};
});
and then you can apply this directive to any element in the html:
<div toggle-display></div>
You can make a drop down logic or kindof accordion etc following this pattern
How do i listen to a click anywhere in the app, that when i click it
and "that" element is not the select box, close all the select boxes?
let suppose you have that opend/dispalyed div that you want to hide when you click outside of it . give it a class "divvy" and attach the following directive to its wrapping container:
app.directive('toggleFocus', function() {
return function(scope, element) {
element.bind('click', function() {
if(!element.hasClass('divvy'))
angular.element(document.querySelector('.divvy')).css({display:'none'})
});
};
});
html:
<div toggle-focus>
<div class="divvy"></div>
</div>
It's in the angular documentation: https://docs.angularjs.org/api/ng/directive/ngShow
<!-- when $scope.myValue is truthy (element is visible) -->
<div ng-show="myValue"></div>
<!-- when $scope.myValue is falsy (element is hidden) -->
<div ng-show="myValue" class="ng-hide"></div>
just attach ng-show="whateverValue" to each div you want to hide/show based on whether "whateverValue" is true/false
I wrote a jquery plugin that allows users to use either touch or mouse (kind of like this) to resort a ul. The plugin works fine for updating the DOM but I'm not sure how to tell Angular to "update" the model binding. Specifically, the ng-click i have in the li doesn't fire after an li is moved. I'd also like to update the $index and tell the server what the new order is. Any ideas?
HTML:
<ul class="unstyled" ng-sortable>
<li ng-repeat="item in list.Items">
{{item.QuantityType.Name}} {{item.Name}} {{index}}
<button class="close" ng-click="deleteItem($index)" aria-hidden="true">×</button></li></ul>
Directive:
.directive('ngSortable', function () {
return {
link: function (scope, element) {
angular.element(element).draggableTouch({ onSortEndCallback: function () { scope.finishedSorting(); } });
}
};
})
EDIT: added code for more context.
Assuming that ng-click="deleteItem($index)" is not working
I would suggest you to pass item instead of $index so change your code as
ng-click="deleteItem(item)"
You can use indexOf function to find elements index in the array
scope.list.Items.indexOf(item)
Call the controller in your plugin after a click or touch. You can access the scope from outside your controller angular.element(domElement).scope() to get the current scope for the element.
Then update a $scope.sort (to send to server) and $index with the data in the plugin.