Define multiple html amd modules in one file - html

Is there any way to do a requirejs define in a text document? Or an html document?
I have a document filled with header and cell templates for a grid
<body>
<th data-fieldname="PatientPerson">Name <span data-bind="attr: { class: sortField() == 'PatientPerson' ? 'inline-block' : 'hide' }"></span></th>
<td><span class="glyphicon glyphicon-expand"></span><a data-bind="click: function () { $parent.loadReportSummary(PatientPerson.ID()) }"><span data-bind="text: PatientPerson.FullName"></span></a></td>
<th data-fieldname="StudyType">Study Type <span data-bind="attr: { class: sortField() == 'StudyType' ? 'inline-block' : 'hide' }"></span></th>
<td data-bind="text: StudyType"></td>
<th data-fieldname="ServiceDate">Service Date<span data-bind="attr: { class: sortField() == 'ServiceDate' ? 'inline-block' : 'hide' }"></span></th>
<td data-bind="text: ServiceDate"></td>
<th>Export Summary</th>
<td><a data-bind="click: function (data) { $parent.exportReportSummary(data, PatientPerson.ID, SummaryID, !StudyExported()) }">View</a></td>
<th>Print All Reports</th>
<td><a data-bind="click: function (data) { $parent.printAllReports('/PrintReports/Reports?summaryID=' + SummaryID) }">Print</a></td>
etc.......
</body>
In another module I have an array which determines which of these columns are used in a knockout computed observable. I was hoping that I could make each of these a module instead of parsing them using jquery, but I wanted them to all be in one file. I'm using the text plugin for requirejs, but there seems to be no way to declare each of these as a module inside of one file, and it seems wasteful to have to split each of these into separate files.
maybe something like
<!--export name:"PatientPerson" -->
<th data-fieldname="PatientPerson">Name <span data-bind="attr: { class: sortField() == 'PatientPerson' ? 'inline-block' : 'hide' }"></span></th>
<td><span class="glyphicon glyphicon-expand"></span><a data-bind="click: function () { $parent.loadReportSummary(PatientPerson.ID()) }"><span data-bind="text: PatientPerson.FullName"></span></a></td>
<!-- /export-->
Then referencing the module like
require('filename').PatientPerson;

I created a plugin where you can add a !define to the end of the require on some file, and it will make the spec suggested from the OP work. It works with the official text plugin for requirejs.
define(['text', 'module'], function (text, module) {
var exportRegExp = /<!--\s*?export[^>]*>([\s\S]*?)<!--\s*?\/export\s*?-->/g,
masterConfig = (module.config && module.config()) || {},
buildMap={};
text.export = function (content) {
var exports = null;
if (content) {
var matches = content.match(exportRegExp) || [],
match, _i, _len;
exports = matches.length ? {} : null;
for (_i = 0, _len = matches.length; _i < _len; _i++) {
match = matches[_i];
var exportName = match.match(/(<!--\s*?export\s*?name\:")(.*?)\"\s*?-->/).slice(-1)[0];
exports[exportName] = match.replace(/<!--\s*?export[^>]*>\n?/, '').replace(/<!--\s*?\/export\s*?-->/, '');
}
}
return exports;
};
var baseParseName = text.parseName;
text.parseName = function(name) {
var index, parseExport = false;
index = name.indexOf('!export');
if (index !== -1) {
parseExport = true;
name.split('!export').join('');
}
var out = baseParseName(name);
out["strip"] = { "export": parseExport, "strip": out["strip"] };
return out;
};
text.finishLoad = function (name, strip, content, onLoad) {
var exports = strip.export ? text.export(content,name) : content;
if (exports == null) content = strip.strip ? text.strip(content) : content;
else content = exports;
if (masterConfig.isBuild) {
buildMap[name] = content;
}
onLoad(content);
};
text.write = function (pluginName, moduleName, write, config) {
if (buildMap.hasOwnProperty(moduleName)) {
var content = text.jsEscape(buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"define(function () { return '" +
content +
"';});\n");
}
};
});
The following will work
require([text!somefile!define],function(){})
This also works but it ignores the !strip command if any exports exist in the file.
require([text!somefile!strip!define],function(){})
then you can call
require(['someModuleNameInDefineComment'],function(){})
I have not handled strip with a beginning define in the header.
Here is a Gist

Related

Make own calibration dialog with comma delimiter decimal

Is there an event similar as "FINISHED_CALIBRATION" when the style for the "calibration panel" is set to display: block? I want to create a custom calibration dialog with comma separator. I want to open own calibration dialog instead of your dialog. I check two point for calibration with this code:
const calibrationEndpoints: HTMLCollection = document.getElementsByClassName('calibration-endpoint');
const countPoint = Array.from(calibrationEndpoints).filter(e => e.classList.contains('editable')).length;
if (countPoint === 2) {
this.openCalibrationDialog();
}
Please review this sample
this.showPanel = function() {
var self = this;
const _window = this.getWindow();
if (_calibrationPanel) {
_window.setTimeout(function () { _calibrationPanel.requestedSizeTextbox.focus();}, 0);
_calibrationPanel.setVisible(true);
_calibrationPanel.updatePanelPosition(_measurement.indicator.labelPosition, _measurement.indicator.p1, _measurement.indicator.p2, _measurement.indicator.calibrationLabel.clientHeight);
self.addWindowEventListener("keyup", function onKeyUp(e){
var key = e.key || String.fromCharCode(e.keyCode);
if (key == "Escape" && self.isActive()) {
self.hidePanel();
self.clearSize();
self.showAddCalibrationLabel();
self.removeWindowEventListener("keyup", onKeyUp);
}
});
}
else {
_viewer.dispatchEvent({ type: MeasureCommon.Events.OPEN_CALIBRATION_PANEL_EVENT, data: {size: _selectedSize, units: _selectedUnits } });
}
};

Nesting options under an if binding causes wrong behavior

I want to have a <select> without using the options binding, and nest the <option> element under an if binding.
The following is what I did (here's also a fiddle), which displays a behavior I wasn't expecting: The if seems to fire for each option selection, whereas what I expected is that it would fire only when adding the options elements to DOM.
Thus, when an option is chosen, it doesn't displayed. Only when choosing the same option again, it renders as it should.
What did I do wrong?
var DogHouseViewModel = function() {
var self = this;
self.allowedNames = ["A", "B", "C"];
self.puppies = ko.observableArray([]);
self.createPuppy = function () {
self.puppies.push(new DogViewModel());
}
self.isNameAlreadyTaken = function (puppyName) {
var puppies = self.puppies();
for (var i = 0; i < puppies.length; i++) {
if (puppies[i].dogName() == puppyName) {
return true;
}
}
return false;
}
self.printPuppiesName = function () {
self.puppies().forEach(function (puppy) {
alert(puppy.dogName())
})
}
}
var DogViewModel = function (dogName) {
var self = this;
self.dogName = ko.observable();
}
vm = new DogHouseViewModel()
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.1.0/knockout-min.js"></script>
<div>
<button data-bind="text: 'create a puppy', click: createPuppy"></button>
<button data-bind="text: 'print puppies names', click: printPuppiesName"></button>
<div data-bind="foreach: puppies">
<select data-bind="value: dogName">
<!-- ko foreach: $parent.allowedNames -->
<!-- ko if: !($root.isNameAlreadyTaken($data)) -->
<option data-bind="value: $data, text: $data"></option>
<!-- /ko -->
<!-- /ko -->
</select>
</div>
</div>
I believe this is what you are trying to do. The problem is that your allowedNames are the values of the options do you can't just remove them from the array. But you can clone the parent array and as it changes compute the array by returning a list of not used values.
I also added a check to make sure we don't accidentally add a puppy object to the puppies array when no names are available.
var DogHouseViewModel = function() {
var self = this;
self.allowedNames = ["A", "B", "C"];
self.puppies = ko.observableArray([]);
self.createPuppy = function() {
var newPuppy = new DogViewModel(self);
if(newPuppy.allowedNames().length > 0) { // Check to see if there are any names left.
self.puppies.push(newPuppy);
}
}
self.removePuppy = function(obj) {
self.puppies.remove(obj);
}
self.printPuppiesName = function() {
self.puppies().forEach(function(puppy) {
alert(puppy.dogName())
})
}
}
var DogViewModel = function(parent) {
var self = this;
self.dogName = ko.observable();
self.allowedNames = ko.computed(function() {
var allowedNamesClone = parent.allowedNames.slice(0);
var usedNames = parent.puppies().filter(function(pup) { // get all pups who have a name
return pup.dogName() !== '' && pup.dogName() !== self.dogName();
})
usedNames.forEach(function(pup) {
var index = allowedNamesClone.indexOf(pup.dogName());
if (index > -1) {
allowedNamesClone.splice(index, 1); // remove name from cloned array
}
})
return allowedNamesClone; // return new array
})
}
vm = new DogHouseViewModel()
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<button data-bind="text: 'create a puppy', click: createPuppy"></button>
<button data-bind="text: 'print puppies names', click: printPuppiesName"></button>
<div data-bind="foreach: puppies">
<!-- ko if: allowedNames().length > 0 -->
<select data-bind="options: allowedNames, value: dogName"></select>
<button data-bind="click: $root.removePuppy">x</button>
<!-- /ko -->
</div>
</div>

What does this tag do?

I have been learning how to read other people's code and when ever I see something like this <meganav-item item="item" ng-repeat="item in website.nav.primary"></meganav-item> I get stuck.
I have a basic understand of angular, but the problem is the <meganav> tag. I do not know what this is..I have done a Google search, but nothing useful is showing.
Update
I have managed to locate the file of the <meganav> element. After following the instructions from the links that you guys have provided, it led me to a file named "MegaNavItem.js". Here is the code:
window.tcoStore.directive('meganavItem', ['$timeout','transport', function($timeout,transport) {
var lockTimeout = false;
var meganavLocks = transport.getModel('meganavLocks', {lock : false});
var clear = function (){
if(meganavLocks.timeout){
$timeout.cancel(meganavLocks.timeout);
}
}
var action = function(callback, time) {
if(meganavLocks.lock){
return;
}
clear();
meganavLocks.timeout = $timeout(callback, time);
}
var dropLock = function(callback, time) {
meganavLocks.lock = false;
}
return {
restrict : 'E',
replace: true,
templateUrl : '/page/header/meganav/item.html',
scope : {
item : '=',
clickOnly : '#',
delayIn : '#',
delayOut : '#'
},
link : function($scope, elem, attrs){
if(!$scope.clickOnly){
$scope.delayInValue = parseInt($scope.delayIn || 300,10);
$scope.delayOutValue = parseInt($scope.delayOut || 500,10);
elem.on('mouseenter', $scope.showDelayed);
if($scope.delayOutValue > 0){
elem.on('mouseleave', $scope.hideDelayed);
}
}
},
controller: ['$scope', '$timeout', 'transport', '$location' ,
function($scope, $timeout, transport,$location) {
// When $location changes ...
$scope.$on('$locationChangeSuccess', function() {
$scope.hide(true);
$scope.isActive = !_.isUndefined($scope.item.link) && ($scope.item.link.replace(/\/+$/,'') == $location.path().replace(/\/+$/,''));
});
$scope.loadSubmenu =0;
// tranposrt model accessable by other items
var meganavVisibleModel = transport.getModel('meganavActive');
var meganavVisibleModelId = $scope.item.$$hashKey;
meganavVisibleModel[meganavVisibleModelId] = false;
// hide and show funs
$scope.hide = function(forceFullClose){
clear();
meganavVisibleModel[meganavVisibleModelId] = false;
if(forceFullClose) {
meganavLocks.lock = true;
$timeout.cancel(lockTimeout);
lockTimeout = $timeout(dropLock, 1000);
}
};
$scope.hideDelayed = function (delay) {
action($scope.hide, _.isNumber(delay) ? delay : $scope.delayOutValue);
};
$scope.show = function(){
if(meganavLocks.lock){
return;
}
clear();
$scope.loadSubmenu = 1;
for(var i in meganavVisibleModel){
meganavVisibleModel[i] = (meganavVisibleModelId == i);
}
};
$scope.showDelayed = function (delay) {
action($scope.show, _.isNumber(delay) ? delay : $scope.delayInValue);
};
$scope.$watch(function(){
$scope.visible = meganavVisibleModel[meganavVisibleModelId];
});
// first touch click, second touch go to link
$scope.touch = function($event, path){
if(!$scope.visible) {
//$event.preventDefault();
$scope.show();
}else if(tco.empty(path)) {
$scope.hide();
} else {
if(path.match(/^https?:/)){
window.location.href = path;
}else{
$location.path(path);
}
}
}
}]
}
}]);
And this file led me to another file named item.html. The code :
<li class="header--menu__item my-repeat-animation" ng-class="{ 'is-active': isActive, open : visible && item.groups.length}" off-click="hide()" >
<a ng-if=":: item.groups.length"
ng-class="{active: item.active}"
class="header--menu__item--link has-children"
ng-click="show()"
title="{{::item.name}}">
{{::item.name}}
</a>
<a ng-if=":: !item.groups.length"
class="header--menu__item--link"
href="{{::item.link}}"
title="{{::item.name}}">
{{::item.name}}
</a>
<div class="header-menu-dropdown ng-hide" ng-show="visible" ng-if=":: item.groups.length">
<ul class="header-menu-dropdown__meganavGroup">
<li ng-repeat="meganavGroup in item.groups" class="header--menu-group">
<span class="meganav--group--name">{{::meganavGroup.name}}</span>
<ul class="meganav--group--items">
<li ng-repeat="groupItem in meganavGroup.items">
{{::groupItem.name}}
<span class="icon"></span>
</li>
</ul>
</li>
<li class="header-menu-offers" ng-repeat="offer in item.offers">
<a href="{{::offer.offer_link}}" class="placeholder">
<img tco-image="offer.offer_image" crop="3" alt="{{::offer.offer_name}}" />
</a>
<span class="offer-name">{{::offer.offer_name}}</span>
</li>
</ul>
<div class="header-menu-message" ng-bind-html="item.message"></div>
</div>
</li>
My issue is now that I cannot make out what where to find {{::item.name}}, which is the thing that I want to change. What technique can I use to find {{::item.name}}?
Sorry for all the noob questions! Your help is much appreciated!
In Angular it is possible to build your own HTML element. You won't find any information about this element because it doesn't exist. The developer has created that on its own and handles the content inside a module. Have a look at http://www.aleaiactaest.ch/2012/07/29/build-your-own-html-element-with-angular/ for more information.
Hope this helps, Cheers.
As i've noticed it's Angular App, so probably there are defined an directive which is called meganavItem. See Angular Directive for more information, you have to find definition of that directive and discover what is html layout and logic lives under <meganav-item>. However if there are no directive with defined name.
Also it may be separate registered element, see "Custom Elements
"article of how it's done and it would be more easy for you to find out how it works ( if it registered in that way...)

Add search filter inside the select dropdown in AngularJS

I want to add a search filter inside a select dropdown in angularJS.
I have used ng-options to list down the options and used filter to filter out the data in the search box , but the problem is that the search box is not coming inside(or under) select dropdown. (When I click the select dropdown, it shows a search filter and below it has all the options)
Below is the code for your reference :
<div class="rowMargin">
<label class="control-label" for="entitySel">Entity:</label>
<div class="controls">
<select id="entityId" class="input-medium" type="text" name="entityId" ng-model="payment.entityId" ng-options="entityOpt for entityOpt in paymentEntityOptions">
<option value="">Select</option>
</select>
<span ng-show=" submitted && addPayment.entityId.$error.required">
<label class="error">Please provide entity Id </label>
</span>
<div ng-show="payment.entityId == \'Individual\'">
<span>
<select ng-model="payment.entity.individual" ng-options = "individual for individual in individualEntities | filter : filterEntity">
<option value="">Select Individual Entity</option>
<option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</option>
</select>
</span>
</div>
<div ng-show="payment.entityId == \'Group\'">
<span>
<select ng-model="payment.entity.group" ng-options = "group for group in groupEntities | filter : filterEntity">
<option value="">Select Group Entity</option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</select>
</span>
</div>
</div>
I have used the bootstrap button with class 'dropdown-toggle' and on click of the button I have appended an input search box as following :
<div class="dropdown pull-right makePaymentDropdownMainDiv" auto-close="outsideClick">
<button class="btn btn-default dropdown-toggle makePaymentDropdownBtn" type="button" id="individualDrop" data-toggle="dropdown">{{payment.entity}}<span class="caret pull-right"></span></button>
<span ng-show="submitted"><label class="error">Select an Individual</label></span>
<ul class="dropdown-menu makePaymentDropdownUlStyle" role="menu" aria-labelledby="individualDrop">
<input disable-auto-close type="search" ng-model="serchFilter" class="makePaymentDropdownSearchBox" placeholder="Search"></input>
<li role="presentation" ng-repeat="indi in individuals | filter: serchFilter"><a role="menuitem" ng-click="selectEntity(indi)">{{indi}}</a></li>
</ul>
</div>
Showing the 'li' using ng-repeat.
Remember to add auto-close="outsideClick" to your dropdown so that it doesn't close on filtering attempt.
Sorry, I'm rather late to the party, but to me it sounds like you need acute-select, an open source extension (MIT license) to Angular that does exactly this, without further dependencies.
They also have a demo page, which shows what it can do nicely.
you can use easy and best way to search filter inside the select dropdown in AngularJS
Working Demo : http://plnkr.co/edit/o767Mg6fQoyc7jKq77If?p=preview
(function (angular, undefined) {
'use strict';
// TODO: Move to polyfill?
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
/**
* A replacement utility for internationalization very similar to sprintf.
*
* #param replace {mixed} The tokens to replace depends on type
* string: all instances of $0 will be replaced
* array: each instance of $0, $1, $2 etc. will be placed with each array item in corresponding order
* object: all attributes will be iterated through, with :key being replaced with its corresponding value
* #return string
*
* #example: 'Hello :name, how are you :day'.format({ name:'John', day:'Today' })
* #example: 'Records $0 to $1 out of $2 total'.format(['10', '20', '3000'])
* #example: '$0 agrees to all mentions $0 makes in the event that $0 hits a tree while $0 is driving drunk'.format('Bob')
*/
function format(value, replace) {
if (!value) {
return value;
}
var target = value.toString();
if (replace === undefined) {
return target;
}
if (!angular.isArray(replace) && !angular.isObject(replace)) {
return target.split('$0').join(replace);
}
var token = angular.isArray(replace) && '$' || ':';
angular.forEach(replace, function (value, key) {
target = target.split(token + key).join(value);
});
return target;
}
var module = angular.module('AxelSoft', []);
module.value('customSelectDefaults', {
displayText: 'Select...',
emptyListText: 'There are no items to display',
emptySearchResultText: 'No results match "$0"',
addText: 'Add',
searchDelay: 300
});
module.directive('customSelect', ['$parse', '$compile', '$timeout', '$q', 'customSelectDefaults', function ($parse, $compile, $timeout, $q, baseOptions) {
var CS_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, controller) {
var customSelect = attrs.customSelect;
if (!customSelect) {
throw new Error('Expected custom-select attribute value.');
}
var match = customSelect.match(CS_OPTIONS_REGEXP);
if (!match) {
throw new Error("Expected expression in form of " +
"'_select_ (as _label_)? for _value_ in _collection_[ track by _id_]'" +
" but got '" + customSelect + "'.");
}
elem.addClass('dropdown custom-select');
// Ng-Options break down
var displayFn = $parse(match[2] || match[1]),
valueName = match[3],
valueFn = $parse(match[2] ? match[1] : valueName),
values = match[4],
valuesFn = $parse(values),
track = match[5],
trackByExpr = track ? " track by " + track : "",
dependsOn = attrs.csDependsOn;
var options = getOptions(),
timeoutHandle,
lastSearch = '',
focusedIndex = -1,
matchMap = {};
var itemTemplate = elem.html().trim() || '{{' + (match[2] || match[1]) + '}}',
dropdownTemplate =
'<a class="dropdown-toggle" data-toggle="dropdown" href ng-class="{ disabled: disabled }">' +
'<span>{{displayText}}</span>' +
'<b></b>' +
'</a>' +
'<div class="dropdown-menu">' +
'<div stop-propagation="click" class="custom-select-search">' +
'<input class="' + attrs.selectClass + '" type="text" autocomplete="off" ng-model="searchTerm" />' +
'</div>' +
'<ul role="menu">' +
'<li role="presentation" ng-repeat="' + valueName + ' in matches' + trackByExpr + '">' +
'<a role="menuitem" tabindex="-1" href ng-click="select(' + valueName + ')">' +
itemTemplate +
'</a>' +
'</li>' +
'<li ng-hide="matches.length" class="empty-result" stop-propagation="click">' +
'<em class="muted">' +
'<span ng-hide="searchTerm">{{emptyListText}}</span>' +
'<span class="word-break" ng-show="searchTerm">{{ format(emptySearchResultText, searchTerm) }}</span>' +
'</em>' +
'</li>' +
'</ul>' +
'<div class="custom-select-action">' +
(typeof options.onAdd === "function" ?
'<button type="button" class="btn btn-primary btn-block add-button" ng-click="add()">{{addText}}</button>' : '') +
'</div>' +
'</div>';
// Clear element contents
elem.empty();
// Create dropdown element
var dropdownElement = angular.element(dropdownTemplate),
anchorElement = dropdownElement.eq(0).dropdown(),
inputElement = dropdownElement.eq(1).find(':text'),
ulElement = dropdownElement.eq(1).find('ul');
// Create child scope for input and dropdown
var childScope = scope.$new(true);
configChildScope();
// Click event handler to set initial values and focus when the dropdown is shown
anchorElement.on('click', function (event) {
if (childScope.disabled) {
return;
}
childScope.$apply(function () {
lastSearch = '';
childScope.searchTerm = '';
});
focusedIndex = -1;
inputElement.focus();
// If filter is not async, perform search in case model changed
if (!options.async) {
getMatches('');
}
});
if (dependsOn) {
scope.$watch(dependsOn, function (newVal, oldVal) {
if (newVal !== oldVal) {
childScope.matches = [];
childScope.select(undefined);
}
});
}
// Event handler for key press (when the user types a character while focus is on the anchor element)
anchorElement.on('keypress', function (event) {
if (!(event.altKey || event.ctrlKey)) {
anchorElement.click();
}
});
// Event handler for Esc, Enter, Tab and Down keys on input search
inputElement.on('keydown', function (event) {
if (!/(13|27|40|^9$)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
switch (event.keyCode) {
case 27: // Esc
anchorElement.dropdown('toggle');
break;
case 13: // Enter
selectFromInput();
break;
case 40: // Down
focusFirst();
break;
case 9:// Tab
anchorElement.dropdown('toggle');
break;
}
});
// Event handler for Up and Down keys on dropdown menu
ulElement.on('keydown', function (event) {
if (!/(38|40)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
var items = ulElement.find('li > a');
if (!items.length) return;
if (event.keyCode == 38) focusedIndex--; // up
if (event.keyCode == 40 && focusedIndex < items.length - 1) focusedIndex++; // down
//if (!~focusedIndex) focusedIndex = 0;
if (focusedIndex >= 0) {
items.eq(focusedIndex)
.focus();
} else {
focusedIndex = -1;
inputElement.focus();
}
});
resetMatches();
// Compile template against child scope
$compile(dropdownElement)(childScope);
elem.append(dropdownElement);
// When model changes outside of the control, update the display text
controller.$render = function () {
setDisplayText();
};
// Watch for changes in the default display text
childScope.$watch(getDisplayText, setDisplayText);
childScope.$watch(function () { return elem.attr('disabled'); }, function (value) {
childScope.disabled = value;
});
childScope.$watch('searchTerm', function (newValue) {
if (timeoutHandle) {
$timeout.cancel(timeoutHandle);
}
var term = (newValue || '').trim();
timeoutHandle = $timeout(function () {
getMatches(term);
},
// If empty string, do not delay
(term && options.searchDelay) || 0);
});
// Support for autofocus
if ('autofocus' in attrs) {
anchorElement.focus();
}
var needsDisplayText;
function setDisplayText() {
var locals = { };
locals[valueName] = controller.$modelValue;
var text = displayFn(scope, locals);
if (text === undefined) {
var map = matchMap[hashKey(controller.$modelValue)];
if (map) {
text = map.label;
}
}
needsDisplayText = !text;
childScope.displayText = text || options.displayText;
}
function getOptions() {
return angular.extend({}, baseOptions, scope.$eval(attrs.customSelectOptions));
}
function getDisplayText() {
options = getOptions();
return options.displayText;
}
function focusFirst() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
focusedIndex = 0;
opts.eq(0).focus();
}
}
// Selects the first element on the list when the user presses Enter inside the search input
function selectFromInput() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
var ngRepeatItem = opts.eq(0).scope();
var item = ngRepeatItem[valueName];
childScope.$apply(function () {
childScope.select(item);
});
anchorElement.dropdown('toggle');
}
}
function getMatches(searchTerm) {
var locals = { $searchTerm: searchTerm }
$q.when(valuesFn(scope, locals)).then(function (matches) {
if (!matches) return;
if (searchTerm === inputElement.val().trim()/* && hasFocus*/) {
matchMap = {};
childScope.matches.length = 0;
for (var i = 0; i < matches.length; i++) {
locals[valueName] = matches[i];
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(matches[i]);
}
//childScope.matches = matches;
}
if (needsDisplayText) setDisplayText();
}, function() {
resetMatches();
});
}
function resetMatches() {
childScope.matches = [];
focusedIndex = -1;
};
function configChildScope() {
childScope.addText = options.addText;
childScope.emptySearchResultText = options.emptySearchResultText;
childScope.emptyListText = options.emptyListText;
childScope.select = function (item) {
var locals = {};
locals[valueName] = item;
var value = valueFn(childScope, locals);
//setDisplayText(displayFn(scope, locals));
childScope.displayText = displayFn(childScope, locals) || options.displayText;
controller.$setViewValue(value);
anchorElement.focus();
typeof options.onSelect === "function" && options.onSelect(item);
};
childScope.add = function () {
$q.when(options.onAdd(), function (item) {
if (!item) return;
var locals = {};
locals[valueName] = item;
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(item);
childScope.select(item);
});
};
childScope.format = format;
setDisplayText();
}
var current = 0;
function hashKey(obj) {
if (obj === undefined) return 'undefined';
var objType = typeof obj,
key;
if (objType == 'object' && obj !== null) {
if (typeof (key = obj.$$hashKey) == 'function') {
// must invoke on object to keep the right this
key = obj.$$hashKey();
} else if (key === undefined) {
key = obj.$$hashKey = 'cs-' + (current++);
}
} else {
key = obj;
}
return objType + ':' + key;
}
}
};
}]);
module.directive('stopPropagation', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var events = attrs['stopPropagation'];
elem.bind(events, function (event) {
event.stopPropagation();
});
}
};
});
})(angular);
<body ng-app="Demo">
<div class="container" ng-controller="DemoController">
<label>Level 1</label>
<div custom-select="g for g in nestedItemsLevel1 | filter: $searchTerm" custom-select-options="level1Options" ng-model="level1"></div>
<label>Level 2</label>
<div custom-select="g for g in nestedItemsLevel2 | filter: $searchTerm" ng-model="level2" cs-depends-on="level1"></div>
</div>
<!-- basic scripts -->
<!--[if !IE]> -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<![endif]-->
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="js/customSelect.js"></script>
<script>
(function () {
var app = angular.module('Demo', ['AxelSoft']);
app.controller('DemoController', ['$scope', '$timeout', '$q', function ($scope, $timeout, $q) {
$scope.searchAsync = function (term) {
// No search term: return initial items
if (!term) {
return ['Item 1', 'Item 2', 'Item 3'];
}
var deferred = $q.defer();
$timeout(function () {
var result = [];
for (var i = 1; i <= 3; i++)
{
result.push(term + ' ' + i);
}
deferred.resolve(result);
}, 300);
return deferred.promise;
};
$scope.nestedItemsLevel1 = ['Item 1', 'Item 2', 'Item 3'];
$scope.level1 = $scope.nestedItemsLevel1[0];
$scope.level1Options = {
onSelect: function (item) {
var items = [];
for (var i = 1; i <= 5; i++) {
items.push(item + ': ' + 'Nested ' + i);
}
$scope.nestedItemsLevel2 = items;
}
};
$scope.nestedItemsLevel2 = [];
$scope.level1Options.onSelect($scope.nestedItemsLevel1[0]);
}]);
})();
</script>
</body>
https://docs.angularjs.org/api/ng/directive/select
There can be only one hard coded in a ngOption.

watch changes on JSON object properties

I'm trying to implement a directive for typing money values.
var myApp = angular.module('myApp', []);
var ctrl = function($scope) {
$scope.amount = '0.00';
$scope.values = {
amount: 0.00
};
};
myApp.directive('currency', function($filter) {
return {
restrict: "A",
require: "ngModel",
scope: {
separator: "=",
fractionSize: "=",
ngModel: "="
},
link: function(scope, element, attrs) {
if (typeof attrs.separator === 'undefined' ||
attrs.separator === 'point') {
scope.separator = ".";
} else {
scope.separator = ",";
};
if (typeof attrs.fractionSize === 'undefined') {
scope.fractionSize = "2";
};
scope[attrs.ngModel] = "0" + scope.separator;
for(var i = 0; i < scope.fractionSize; i++) {
scope[attrs.ngModel] += "0";
};
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
};
var pattern = /^\s*(\-|\+)?(\d*[\.,])$/;
if (pattern.test(newValue)) {
scope[attrs.ngModel] += "00";
return;
};
}, true);
}
};
});
HTML template:
<div ng-app="myApp">
<div ng-controller="ctrl">
{{amount}}<br>
<input type="text" style="text-align: right;" ng-model="amount" currency separator="point" fraction-size="2"></input>
</div>
</div>
I want to bind the value in my input element to values.amount item in controller but the watch instruction of my directive doesn't work.
How do I leverage two-way-data-binding to watch JSON objects?
To understand problem more precise I've created a jsfiddle.
The task is the following: Add extra zeros to the input element if user put a point. I mean if the value in input element say "42" and user put there a point, so the value now is "42." two extra zeros have to be aded like this "42.00".
My problems:
If I use ng-model="amount" the logic in input element works, but amount value of outer controller doesn't update.
If I use ng-model="values.amount" for binding, neither amount of outer controller nor input element logic works.
I really have to use ng-model="values.amount" instruction, but it doesn't work and I don't know why.
Any ideas?