Using AngularJS select ng-options with Semantic UI's dropdown - angularjs

So Semantic UI appears to the latest 'hot' UI framework which I'm impressed with; however, their dropdown isn't an implementation of the HTML 'select' and 'option' tags but instead custom. For my project, I'm using AngularJS which is phenomenal JavaScript MVW framework.
How can I integrate AngularJS select ng-option with Semantic UI's dropdown? I'm not much of a JS pro. Here's the JSfidde: http://jsfiddle.net/fMUy3/
<!doctype html>
<html ng-app="App">
<body ng-controller="MainCtrl">
<h3>Option 1 (standard)</h3>
<select ng-model="selectedItem" ng-options="c as (c.id + ' - ' + c.name) for c in containers">
<option value="">-- Pick A Container --</option>
</select>
<br>ID: {{selectedItem.id}}
<br>Name: {{selectedItem.name}}
<h3> Semantic UI Dropdown</h3>
<div class="ui selection dropdown ">
<input name="id" type="hidden" value="0">
<div class="text">-- Pick A Container --</div> <i class="dropdown icon"></i>
<div class="menu transition hidden">
<div class="item active">-- Pick A Container --</div>
<div data-value="{{container.id}}" class="item" ng-repeat="container in containers">{{container.name}}</div>
</div>
</body>
</html>
JavaScript:
var app = angular.module('App', []);
app.controller('MainCtrl', function($scope) {
$scope.containers = [
{id: 1, name: 'Box1'},
{id: 2, name: 'Box2'},
{id: 3, name: 'Box3'}];
//$scope.selectedItem = $scope.containers[0];
});
$('.ui.dropdown').dropdown();
Much appreciated!

There were a few things wrong with your fiddle. The first was that the semantic-ui JavaScript was being loaded before jQuery. It depends on jQuery so jQuery must be loaded first. (Also, loading jQuery before AngularJS is recommended.)
The second thing is that the semantic dropdown function was being called before AngularJS had a chance to unroll the repeater that creates the options. This means the options were not there when semantic did its dropdown thing. As a simple solution I made a timeout that would fire on the next processing loop; after AngularJS has done its thing. You can see that semantic dropdown now works
This is more of a simple proof of concept to demo the timing of things and should not be used.
http://jsfiddle.net/fMUy3/1/
$timeout(function(){
$('.ui.dropdown').dropdown();
},0)
How this should be handled is with a directive that will take care of the timings so that you do not need to use the timeout and would not need to specify each dropdown in the controller. Here is the example using a directive (which would handle all cases)
http://jsfiddle.net/fMUy3/7/
app.directive('dropdown', function ($timeout) {
return {
restrict: "C",
link: function (scope, elm, attr) {
$timeout(function () {
$(elm).dropdown().dropdown('setting', {
onChange: function (value) {
scope.$parent[attr.ngModel] = value;
scope.$parent.$apply();
}
});
}, 0);
}
};
});

Your first problem is you need to include semantic.js after jquery.
Second, you can't use ng-options on a div repeater but you can simulate it by using a ng-click
HTML
<div data-value="{{container.id}}" class="item" ng-repeat="container in containers" ng-click="select(container)">
{{container.name}}
</div>
JS
$scope.select = function(container) {
$scope.selectedItem = container;
};
see this updated fiddle

Related

Trigger ng-submit on ng-click

I am following this answer (and this fiddle) on how to accomplish this.
View:
<section>
<form style="display: none" id="addFriendForm" name="vm.form.addFriendForm" class="form-horizontal" submit-on="submitAddFriendForm" ng-submit="vm.save(vm.form.addFriendForm.$valid)" novalidate>
<input ng-model="vm.userInput" required>
</form>
<div class="list-group">
<a ng-repeat="user in vm.users" ng-click="vm.triggerSubmit(user)" class="list-group-item">
...
</a>
</div>
</section>
Directive:
function submitOn() {
return {
link: function postLink(scope, element, attrs) {
scope.$on(attrs.submitOn, function() {
setTimeout(function() {
// Error:
element.trigger('submit');
});
});
}
};
}
Controller: (triggerSubmit)
// Trigger submit of the form
function triggerSubmit(user) {
vm.userInput = user;
$scope.$broadcast('submitAddFriendForm');
}
When walking through this, I get the error:
TypeError: element.trigger is not a function
...on the line highlighted in the directive. Looking at docs, this seems correct. What's the reason for it failing?
ANSWER:
(if a jQuery answer is wanted, look at the answer below)
A pure angular way of doing it is:
angular.element(element).triggerHandler('submit');
Looks like trigger is not a part of JQLite which ships with angular.
You might have to load JQuery before loading angular.
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
OR
As part of your build pipeline (grunt/gulp/webpack etc)
See the other answer for a jQuery approach. For a pure angular approach I used:
angular.element(element).triggerHandler('submit');

Live search in AngularJS: updating the results

I want a live search: the results are queried from web api and updated as the user types.
The problem is that the list flickers and the "No results" text appears for a fraction of second, even if the list of results stays the same. I guess I need to remove and add items with special code to avoid this, calculating differences between arrays, etc.
Is there a simpler way to avoid this flicker at least, and probably to have possibility to animate the changes?
It looks like this now:
The html part is:
<div class="list-group">
<a ng-repeat="test in tests track by test.id | orderBy: '-id'" ng-href="#/test/{{test.id}}" class="list-group-item">
<h4 class="list-group-item-heading">{{test.name}}</h4>
{{test.description}}
</a>
</div>
<div ng-show="!tests.length" class="panel panel-danger">
<div class="panel-body">
No tests found.
</div>
<div class="panel-footer">Try a different search or clear the text to view new tests.</div>
</div>
And the controller:
testerControllers.controller('TestSearchListCtrl', ['$scope', 'TestSearch',
function($scope, TestSearch) {
$scope.tests = TestSearch.query();
$scope.$watch('search', function() {
$scope.tests = TestSearch.query({'q':$scope.search});
});
}]);
You should use ng-animate module to get the classes you need for smooth animation. For each ng-repeat item that's moved, added, or removed - angular will add specific classes. Then you can style those classes via CSS or JS so they don’t flicker.
An alternative way of doing what you require is to use the angular-ui bootstrap Typeahead component (check at the bottom of the post). It has a type-ahead-wait property in milliseconds and also a template url for customising it.
<div ng-app>
<div ng-controller="MyController">
<input type="search" ng-model="search" placeholder="Search...">
<button ng-click="fun()">search</button>
<ul>
<li ng-repeat="name in names">{{ name }}</li>
</ul>
<p>Tips: Try searching for <code>ann</code> or <code>lol</code>
</p>
</div>
</div>
function MyController($scope, $filter) {
$scope.names = [
'Lolita Dipietro',
'Annice Guernsey',
'Gerri Rall',
'Ginette Pinales',
'Lon Rondon',
'Jennine Marcos',
'Roxann Hooser',
'Brendon Loth',
'Ilda Bogdan',
'Jani Fan',
'Grace Soller',
'Everette Costantino',
'Andy Hume',
'Omar Davie',
'Jerrica Hillery',
'Charline Cogar',
'Melda Diorio',
'Rita Abbott',
'Setsuko Minger',
'Aretha Paige'];
$scope.fun = function () {
console.log($scope.search);
$scope.names = $filter('filter')($scope.names, $scope.search);
};
}

How to Use ng-Option on div tags - AngularJs

I am new to angularjs and just started learning it. I am trying to build a dropdown equivalent from angularjs without using select.
Html
<div ng-app="App" ng-controller="OrderExportCtrl" >
<li class="box-ddl" >
<div ng-model="fruit" ng-options="f for f in fruits" class="ddlListSmall">
</div>
</li>
JavaScript
var app = angular.module('App', []);
app.controller('OrderExportCtrl', function ($scope) {
$scope.fruit = ['apple', 'orange', 'mango', 'grapefruit', 'banana', 'melon'];
});
Please find the JsFiddle here. I am not getting what mistake I am doing, but my dropdown is not binding.
Please guide me to fix my issue.
ngOptions is only for select elements (it is used by the select directive). You can use ngRepeat to achieve the same result. You can use ngClick to set your model directly.
<div class="list">
<div class="option" ng-repeat="f in fruits" ng-bind="f"
ng-click="fruit.selected = f"></div>
</div>
Make sure the value you're setting is inside an object that is defined in the controller, otherwise you'll just set it inside the row's scope rather than the controller's. Alternatively use ng-click="$parent.fruit = f" to reference the parent scope, which in this case is the controller's (but may not always be).

Angular JS with jquery masonry

I need to use angular and masonry and also implement sorting and filtering.
The fiddle here does use the masonry with Angular and has filtering and sorting working, however the layout does not seem like masonry. I do not think the masonry layout is applied at all.
http://jsfiddle.net/rdikshit/6swek/3/
<div ng-app="test">
<div ng-controller="MainCtrl">
<input type="text" ng-model="nameFilter" />
Order by id
Order by name
Order by age
<div class="items" masonry >
<div ng-repeat="item in items | filter: { name: nameFilter } | orderBy: order:reverse" class={{item.style}}>
<span>{{item.name}}</span>
<span>id: {{item.id}}</span>
<br /> <span>Age: {{item.age}}</span>
<br /> <span>Style: {{item.style}}</span>
</div>
</div>
</div>
</div>
Here is another fiddle with Passy's directive:
http://jsfiddle.net/rdikshit/6swek/5/
STill does nt work. Even the sorting and filtering are not working now.
I modified your JSFiddle and got it to work. Since columnWidth is not specified, the width of the first element is used. That's why there is gap between the elements sometimes
Few points to look out for:
Don't save the Masonry instance, since you are using the JQuery version. To access Masonry methods, simply get the container element then do container.masonry( 'methodName', arguments );
The watches in the controller are not ideal, in a real app you probably want to put them into a directive
The technique where you watch the number of children in the Masonry container doesn't work when you have ngAnimate as one of your dependency. It cause the DOM tree to be updated after $digest(), which make $watch miss
I am working on a simular project, but i am using Isotope with a mansory style.
here is the way i am using it:
Directive:
app.directive('isotope', function ($timeout) {
return {
scope: {
items: '=isotope'
},
templateUrl: 'social-item.html',
link: function (scope, element, attrs) {
var options = {
animationEngine : 'jquery',
itemSelector: '.social-item',
layoutMode: 'masonry',
masonry : {
"gutter": 10,
"isFitWidth": true
}
};
element.isotope(options);
scope.$watch('items', function() {
$timeout(function() {
element.isotope( 'reloadItems' ).isotope();
});
},true);
}
};
});
Directive template (social-item.html):
<div class="social-item" ng-repeat="item in items track by $index">
<!--
Do something with item, in your case something like this:
<span>{{item.name}}</span>
<span>id: {{item.id}}</span>
-->
</div>
HTML Markup:
<div class="social-wall-container" isotope="socials.posts">
<!-- repeat posts from directive -->
</div>
For more information about isotope checkout This link
If you decide to use isotope, make sure to include the necesary files located in the JS folder on the github page.

bootstrap-timepicker directive not working inside ng-repeat

Plunker demo
I'm trying to create an arbitrary number of timepicker, when hardcoded they work fine but it stops working when I put them into an ng-repeat. Is there a way to fix this?
I just updated my Plunker with a better solution.
Tiago answered AngularJS ng-repeat finish event with a great directive that will solve your issue without using the $timeout
Here is a copy of the directive:
app.directive('myRepeatDirective', function() {
return function(scope, element, attrs) {
if (scope.$last) {
$('.bootstrap-timepicker').timepicker();
}
};
});
HTML:
<li ng-repeat="list in lists" my-repeat-directive>
{{list.name}}
<div class="input-append">
<input type="text" class="bootstrap-timepicker">
<span class="add-on"><i class="icon-time"></i></span>
</div>
</li>
This is better because if you repeater requires data from an ajax request than your timeout could have to be altered and cause an ugly user experience.
Tiago's method seems to handle it best, in my opinion, you might also want to give him an upvote :)
I solved this using the ng-init because solution provided by Asok was not working for me. I also didn't want to use $scope
AngularCode
angular.module('MyApp', [])
.controller('MyController', MyController);
function MyController($http) {
// your code here...
jd.InitTime = function () {
$('.bootstrap-timepicker').timepicker();
};
};
HTML
<div data-ng-app="MyApp" data-ng-controller="MyController as mc">
<li ng-repeat="list in mc.lists">
{{list.name}}
<div class="input-append">
<input type="text" class="bootstrap-timepicker" ng-init="mc.InitTime();">
<span class="add-on"><i class="icon-time"></i></span>
</div>
</li>
</div>

Resources