I need to show progress bar when i entered the fields.
Based on fields fillup automatically need to show the proress bar using angularjs in ionic.
I made the following codepen:
http://codepen.io/anon/pen/ZBBGgj
You need to track the progress based on the validation of the input.
function validateInput(input, arrayLength) {
console.log(input);
if (!input.validated && input.model.length > 3) {
vm.validationProgress = vm.validationProgress + (100 / arrayLength);
input.validated = true;
console.log(vm.validationProgress);
} else if (input.validated && input.model.length <= 0) {
input.validated = false;
vm.validationProgress = vm.validationProgress - (100 / arrayLength);
}
}
This code can be improved upon, and you should probably find a better solution to it, but it should point you in the right direction.
If an input is validated = true, I color the progressbox for that input green.
<div ng-repeat="input in vm.inputfields" ng-class="{ 'validated' : input.validated }" class="validation-item">
Related
I'm wondering if the behavior I'm seeing is a bug and figured I'd ask here before submitting to github.
I need to be able to create, save, and edit saved email templates in the Quill editor. I also need to be able to use the output from Quill in an email, formatted how it was entered in the WYSIWYG editor, so I'm saving the HTML output with inline styles.
I have an issue with the indentation not rendering as it was entered when I load in a saved email template. I'm implementing the IndentAttributor like the following comment:
https://github.com/quilljs/quill/issues/1274#issuecomment-303619625
I dug into what Quill is doing and found in matchStyles() a tab character is getting inserted if textIndent is found.
Offending code in quill.js:
function matchStyles(node, delta) {
var formats = {};
var style = node.style || {};
if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
formats.italic = true;
}
if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {
formats.bold = true;
}
if (Object.keys(formats).length > 0) {
delta = applyFormat(delta, formats);
}
if (parseFloat(style.textIndent || 0) > 0) {
// Could be 0.5in
delta = new _quillDelta2.default().insert('\t').concat(delta);
}
return delta;
}
I tested a change to the quill.js node_module to check for text-indent style. However, I'm not trying to hack the editor for a specific need and I'm not familiar enough with the editor to know whether or not this will have a negative impact on anything else.
function matchStyles(node, delta) {
console.log('in matchStyles()');
var formats = {};
var style = node.style || {};
if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
formats.italic = true;
}
if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || parseInt(computeStyle(node).fontWeight) >= 700)) {
formats.bold = true;
}
if (Object.keys(formats).length > 0) {
delta = applyFormat(delta, formats);
}
if ((parseFloat(style.textIndent || 0) > 0) && !style.cssText.includes('text-indent')) { //fix to check for inline style
// Could be 0.5in
delta = new _quillDelta2.default().insert('\t').concat(delta); //problem here...
}
return delta;
}
Example project showing the issue:
https://stackblitz.com/edit/github-acpnmm
Highlight or inspect the initial text (coming from app.reducer.ts initial state) and you will see the tab character that was inserted.
Should Quill be recognizing the IndentAttributor and not insert the tab character?
I see you are using Quill in Angular project. I would suggest you to check this package: https://www.npmjs.com/package/ngx-quill. With this package you can choose the type of data (Text, HTML or Delta) that the Quill will process, and you can bind the Quill content to some variable. That way you can just store that value of that variable in the database. When you need to load the content again, fetch that value from the database, and again bind the Quill content to that value.
I have an AngularJS application, and on one of the pages, there is a button that opens a dialog allowing the user to configure a particular widget. That widget displays a table, and I have recently added the functionality to allow the user to add a single button to a given table cell. This functionality all currently works correctly.
I am now trying to extend the functionality, to allow a user to add multiple buttons to a single table cell.
The way that the user currently adds a button to a cell, is by typing the address of the page that they want to add the button for into a text field on the dialog- the page that they type must be preceded with a : character. However, that text field is also used to display variable values, so for example, to display a variable value, they would type the name of the variable, i.e. myVariable, but to add a button which would link to the Home page, they would type :pages/home.
The value that they type into the text field is converted to the appropriate object for display in the table by a function called toItemObj(item).
This function is defined with:
function toItemObj() {
...
switch(widgetObj.name) {
case 'table':
...
widgetObj.table = angular.copy($scope.widget.table);
...
var multiBtns = ";";
var btnArray = [];
var btnTmplArray = [];
var pageTitle;
...
angular.forEach(widgetObj.table.rows, function(row) {
if(row.length > 0) {
reducedRows.push(row.map(
function(vrbl, idx) {
...
if(widgetObj.table.header[idx].startsWith(":")) {
if(vrbl.vrbl.includes(multiBtns)) {
btnArray = vrbl.vrbl.split(multiBtns);
if(btnArray[0].startsWith(":")) {
brnArray[0] = btnArray[0].split(":")[1];
}
var btnArrayBtn = 0;
angular.forEach(btnArray, function() {
var btnTmpl = $compile(
'<a href="javascript:;" ng-click="goToPage(target)"' +
'class="btn btn-xs btn-brand">Go to page</a>')
toVrblItemObj(btnArray[btnArrayBtn]);
compiledBtns.push(btnTmpl);
console.log("compiledBtns: ", compiledBtns);
})
} else {
console.log("Vrbl does not include multiBtns: ", vrbl.vrbl);
}
} else {
console.log("Value of widgetObj.table.headers[idx]: ", widgetObj.table.headers[idx]);
}
console.log("Returning vrbl.vrbl: ", vrbl.vrbl);
return vrbl.vrbl;
}
));
}
});
widgetObj.table.rows = reducedRows;
...
break;
In the if(vrbl.vrbl.includes(multiBtns)) { block, I am checking whether the value given to the button by the user contains a ; character, and if it does, I am splitting that string on the ; character, and compiling a button for each of the split elements, then pushing that compiled button to a list called compiledBtns.
The problem I'm having is with returning multiple buttons when they've been compiled...
When I load the page with this code as it is above, and try adding a button to a table cell, if I just add a single button to the cell (i.e. type something like :pages/page1 into the field in the dialog, then that button is displayed correctly. But, if I try to add more than one button to a table cell, (i.e. type something like :pages/page1;pages/page2 into the field in the dialog), then the compiled buttons are not displayed, and I just get the text that I actually typed:
I thought that this might be because my return statement is only returning the single vrbl.vrbl element (i.e. a single button), so I tried changing it to:
if(widgetObj.table.headers[idx].startsWith(":")) {
console.log("column heading starts with : - returning compiledBtns- ", compiledBtns);
return compiledBtns;
} else {
return tag.tag;
}
i.e. if the column is a 'buttons' column (the heading starts with a : character, then it should return the list of compiled buttons, but if not, then it should return a variable.
Doing this caused the table to be displayed as:
i.e. it stops even the cells with only one button from showing the button...
The console is showing me that the buttons are being compiled:
compiledBtns: (2) [ƒ, ƒ]0: ƒ publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn)1: ƒ publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn)length: 2__proto__: Array(0)
ctrl.js:467 Returning tag.tag: :pages/auth;pages/userpage1
but for some reason, they're just not being shown...
Can anyone explain what I'm doing wrong here? How can I get my function to return and display multiple buttons in the same table cell?
Edit
I've just noticed that when only entering one button in a table cell, the button is only displayed/ rendered as a button when you select it from the 'autocomplete' list that's displayed when you start typing- i.e. if you type out the page address in full, and click 'Preview' on the dialog, the button is not displayed in the table- it just shows the textual address that you typed.
When trying to add multiple buttons to the table cell, obviously the autocomplete doesn't recognise any pages with the address of two combined page address (separated with a ;), so you can't select more than one page from the drop down at a time.
How would I enable the selection of multiple items from the autocomplete drop down? The autocomplete functionality is defined in the HTML:
<div class="repeat-item-animation" data-ng-repeat="row in widget.table.rows">
<div class="row">
<div class="col-sm-12 input-addon-btn widget-picker-table-row-input">
<tags-input min-length="1"
um-max-tags-strict
key-property="tag"
data-ng-model="row"
display-property="tag"
template="tagItem.html"
replace-spaces-with-dashes="false"
on-tag-adding="onAddingTagItem($tag)"
on-tag-added="warning.rows = undefined"
um-tags-input-warning="{{warning.rows}}"
max-tags="{{widget.table.headers.length}}"
placeholder="Start typing a tag name or some text">
<auto-complete min-length="1"
load-on-focus="true"
load-on-empty="true"
display-property="tag"
select-first-match="false"
template="autocomplete.html"
source="autocompleteTagsFilter($query)">
</auto-complete>
</tags-input>
<a href data-ng-click="widget.table.rows.splice($index, 1)"
class="btn-icon btn-icon-sm btn-config btn-danger">
<span class="glyphicon glyphicon-minus-sign"></span>
</a>
</div>
</div>
I've Google'd a bit, but can't find any properties for the above <auto-complete> tag that would appear to enable selection of more than one element... Anyone have any suggestions?
Edit
So it seems that the auto-complete feature is actually a part of the implementation of ngTagsInput, which it seems the application is using to provide this functionality. From reading its documentation, it seems that the source parameter is the one I want to be using to return an array of strings (i.e. the links for multiple pages that I want to add buttons for). In the 'Parameters' table under the 'Usage' section of the autoComplete directive in ngTagsInput, the description of source is:
Expression to evaluate upon changing the input content. The input value is available as $query. The result of the expression must be a promise that eventually resolves to an array of strings.
In my application, the source attribute is currently set to the function autocompleteTagsFilter($query), which is defined with:
$scope.autocompleteTagsFilter = function(query) {
// Use last valid query keyword instead if input keyword is undefined
// When the first character of the tag that the user enters is a ':',
// the drop down should display a list of available user pages, not tags.
// Put the 'if(!query && lastTagQryKw)' inside the 'else', so that this
// is run whenever the entered tag name doesn't start with ':'
if (query.startsWith(":")) {
console.log("query starts with ':' ", query);
/*Check whether string has a ';' & split it if it does */
if(query.includes(";")) {
var strings = query.split(";");
console.log("strings[0]: ", strings[0]);
console.log("strings[1]: ", strings[1]);
var strings0 = strings[0].split(":")[1];
// console.log("strings0: ", strings0);
var strings1 = strings[1].split(":")[1];
// console.log("strings1: ", strings1);
/*Now need to pass each string to 'toTagItemObj(), to make it a tag item object', so that it can be displayed as a button */
angular.forEach(strings, function(string, btnString) {
// console.log("angular.forEach - value of string: ", string);
// console.log("angular.forEach - value of btnString: ", btnString);
toTagItemObj(string);
})
}
// Split the 'query' search string on ':', to use only the string
var buttonsQuery = query.substring(1);
if (!buttonsQuery && lastTagQryKw) {
buttonsQuery = lastTagQryKw;
}
/*check whether the buttonsQuery variable contains a ';' - if it does, split it */
if(buttonsQuery.includes(';')) {
console.log("buttonsQuery includes ; - ", buttonsQuery);
var btnsToDisplay = buttonsQuery.split(";");
console.log("btnsToDisplay: ", btnsToDisplay);
}
// Declare variables to be used in 'for' loop
var userPages = pagesPresets;
var page;
var result = [];
// 'For' loop should iterate through the list of user pages,
// and remove path, so that only the page name is shown
for (page in userPages) {
page = userPages[page];
// If the page key starts with 'buttonsQuery', and its length
// is greater than 6 (i.e. just 'pages/' shouldn't be displayed)
// add the page to the list of pages to be displayed.
if (page.key.startsWith(buttonsQuery) && page.key.length > 6) {
result.push(page.key);
}
};
if (result.length > 0) {
lastTagQryKw = query;
}
// Return the list of pages that match what the user has typed
return result;
// Otherwise, if the user types something that does not start with ':',
//then it should be a tag- search for tags that match this term
} else {
if (!query && lastTagQryKw) {
query = lastTagQryKw;
}
var result = Object.keys(fxTag.getTags()).filter(function(name) {
return name.indexOf(query.toLowerCase()) !== -1;
});
if (result.length > 0) {
lastTagQryKw = query;
}
return result;
}
};
As it currently stands, this autocompleteTagsFilter() function is returning an array of strings in its return statement. The array of strings is what's shown in the list of auto-complete options available, but the button is only actually created when selecting one of them.
According to the documentation, I need this to return a promise which resolves to an array of strings, but I'm not sure how I'd do this... Can anyone suggest a way I might make this function return a promise that resolves to an array of strings?
Edit
The template code for the markup/ cell rendering as it currently stands is:
angular.module('app.widget').run(function($templateCache) {
// Add basic suggestion template of autocomplete
$templateCache.put(
'autocomplete.html',
'<div class="pull-left autocomplete-text autocomplete-full-text">' +
'<span ng-bind-html="$highlight($getDisplayText())"></span></div>'
);
$templateCache.put('tagItem.html',
'<div data-ng-click="data.nounit = data.isTag && !data.nounit" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isTag"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-unit" ' +
'data-ng-hide="!data.units || data.nounit">({{data.units}})</span>' +
'<a class="remove-button" ng-click="$removeTag()">' +
String.fromCharCode(215) + '</a></div>'
);
}).controller(...){
I tried changing this to use an ng-if, so that it would show a different template for when buttons should be displayed for multiple pages, but this gives me an error in the console that says:
Error: [ng:areq] Argument 'WidgetPickerCtrl' is not a function, got undefined
I got this error after changing the template to:
angular.module('ultimetric.widget').run(function($templateCache) {
// Add basic suggestion template of autocomplete
$templateCache.put(
'autocomplete.html',
'<div class="pull-left autocomplete-text autocomplete-full-text">' +
'<span ng-bind-html="$highlight($getDisplayText())"></span></div>'
);
$templateCache.put('tagItem.html',
'<ng-if="data.isPages">' +
'<div data-ng-click="data.toPageBtn = data.isPages && !data.toPageBtn" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isBtn"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-text">' +
'data-ng-hide="!data.units || data.toPageBtn">({{data.unnits}})</span>' +
String.fromCharCode(215) + '</a></div>'
'<div data-ng-click="data.nounit = data.isTag && !data.nounit" ' +
'class="glyphicon-clickable" um-tag-item-disabled>' +
'<span class="tag-item-icon glyphicon glyphicon-tag" ' +
'data-ng-show="data.isTag"></span><span class="tag-item-text">' +
'{{$getDisplayText()}}</span><span class="tag-item-unit" ' +
'data-ng-hide="!data.units || data.nounit">({{data.units}})</span>' +
'<a class="remove-button" ng-click="$removeTag()">' +
String.fromCharCode(215) + '</a></div>'
);
}).controller(...){
Any ideas how I can get the autocomplete to show multiple buttons at the same time?
Say I have a component with dialog drop to parsys on a content page /content/phonegap/ss/en_gb/login/home/test1/jcr:content/par/productimage
now inside the dialog i have something like
I wish to get $PATH append to URL and send selected device e.g the text 'Device ID:HTC_10_GOLD', to servlet in this dialog listener extjs:
<deviceAndColour
jcr:primaryType="cq:Widget"
allowBlank="{Boolean}false"
fieldLabel="Device and Colour"
name="./deviceAndColour"
options="/bin/reference/data/device.devices.json$PATH"
type="select"
xtype="selection">
<listeners
jcr:primaryType="nt:unstructured"
selectionchanged="function(pathfield) {
var selected = this.findParentByType('form').find('name', './deviceAndColour')[0].getText();
console.log( this.findParentByType('form').find('name', './deviceAndColour')[0].getText());
$.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json$PATH?selectedDevice=' + selected + '&colour=red', function(jsonData){
selectBox.setOptions(jsonData);
});
}" />
</deviceAndColour>
So bascially, the console.log( this.findParentByType('form').find('name', './deviceAndColour')[0].getText()); is not working as I expected, neither the $PATH inside the dialog listener js, it does not retrieve the path at all.
Apart from above attempt, I know var selected = this.findParentByType('form').find('name', './deviceAndColour')[0].getValue(); this will get the value asscociated with the select correctly, but I do not need value, I just wish to getText(), and get the current $PATH in extjs.
another questions, you may noticed $.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json$PATH?selectedDevice=' + selected + '&colour=red'
how do I skip the & in this listner, as if i use & directly, project won't even build. there must be someway to skip & and let extjs treat as part of the string to send request
Anyone expereinced this before? please suggest with code example.
Thanks
Getting the text of the option selected:
function(field,value){
for(var i = 0; i < field.options.length; i++) {
if(field.options[i].value === value) {
console.log('Selected: ' + field.options[i].text);
break;
}
}
}
Getting the path to the resource being edited:
function(field,value){
console.log("Resource:" + field.findParentByType("dialog").path);
}
Documentation: https://docs.adobe.com/docs/en/cq/5-6/widgets-api/index.html?class=CQ.form.Selection
UPDATE
Please try the following code adapted to your scenario (I also refactored the code to make use of params when providing query parameters. There is no reason why this shouldn't work.
function(field, value) {
var selected = '';
var path = field.findParentByType("dialog").path;
// get text of option selected
for(var i = 0; i < field.options.length; i++) {
if(field.options[i].value === value) {
selected = field.options[i].text;
break;
}
}
var params = {
selectedDevice: selected,
colour: 'red'
}
$.getJSON('/bin/reference/data/device/availablecolour.availablecolour.json'+path, params, function(jsonData){
// FIXME: how are you getting the "selectBox" reference?
selectBox.setOptions(jsonData);
});
}
there is one filter functionality in my demo I will explain my problem I have one table in which i use infinite scroll is implemented In other words when user moves to bottom it load more data.There is search input field in top .Using this I am able to filter item in table .but I don't know why it is not working
When you search "ubs" and "ing" first time .it works perfectly .But when you load more data other words when user scroll to bottom and load more data the again it try to filter "ubs" and "ing" it not give any result why ?
<label class="item item-input">
<img src="https://dl.dropboxusercontent.com/s/n2s5u9eifp3y2rz/search_icon.png?dl=0">
<input type="text" placeholder="Search" ng-model="query">
</label>
secondly Actually I am implementing infinite scroll so only 100 element display .can we search element from 2000 (which I am getting from service )and display data the search result ?
Update :
Here's a Plunker with everything working together. I have separated all of the pieces into individual JS files, as it was getting unruly:
Plunker
Search
The built in filter will only return results from the current view data that the ng-repeat is displaying. Because you're not loading all of the data into the view at once, you'll have to create your own search functionality.
In the demo, click the search icon to show the search box, then type your search value and press the ENTER key or click the search button to return the results.
Since you want to check whether the user pressed ENTER you have to pass both the event and the querystring to the function, so you can check for the enter keycode. The function should also run when someone clicks or taps the search button. I set ng-model="query" on the input, so query is the reference in the view. Therefore, you'll add ng-click="searchInvoices($event, query)" to your search button, and ng-keyup="searchInvoices($event, query)" to the input. And, finally, to make it easy to clear the input field, add a button that displays when the input is not empty with ng-show="query" and attach a click event with ng-click="query=null; resetGrid()".
Add the searchInvoices function to your controller. It will only run the search if either the query is empty (because you need to reset the view if the person uses the backspace key to empty the input) OR if the user pressed ENTER OR if the event was a click event in case the user clicks the search button. The inner if statement, prevents the search from running if the query is empty and just resets the view. If the query is not empty, against the total dataset and builds an array of matching results, which is used to update the view.
The last line sets the scroll position to the top of the scrollview container. This makes sure that the user sees the results without having to click somewhere in the scrollview container. Make sure you inject the $ionicScrollDelegate into your controller for this to work and set delegate-handle="invoicegrid" on your ion-scroll directive.
$scope.searchInvoices = function(evt, queryval) {
if (queryval.length === 0 || evt.keyCode === 13 || evt.type === 'click') {
if (queryval.length === 0) {
$scope.invoice_records = $scope.total_invoice_records;
} else {
var recordset = $scope.total_invoice_records;
results = [];
var recordsetLength = recordset.length;
var searchVal = queryval.toLowerCase();
var i, j;
for (i = 0; i < recordsetLength; i++) {
var record = recordset[i].columns;
for (j = 0; j < record.length; j++) {
var invoice = record[j].value.toLowerCase();
if (invoice.indexOf(searchVal) >= 0) {
results.push(recordset[i]);
}
}
}
$scope.invoice_records = results;
$ionicScrollDelegate.$getByHandle('invoicegrid').scrollTop();
}
}
};
Lastly, you need to modify the loadMore() function that is used by the infinite scroll directive, so that it doesn't try to load additional data when scrolling through the search results. To do this, you can just pass the query into loadMore on the directive like: on-infinite="loadMore(query)", then in your function, you can just run the broadcast event when the query exists. Also, removing the ngIf will ensure that the list remains dynamic.
$scope.loadMore = function(query) {
if (query || counter >= $scope.total_invoice_records.length) {
$scope.$broadcast('scroll.infiniteScrollComplete');
} else {
$scope.counter = $scope.counter + showitems;
$scope.$broadcast('scroll.infiniteScrollComplete');
}
};
You used filter in wrong way inside ng-repeat like ng-repeat="column in invoice_records | filter:query" instead of ng-repeat="column in invoice_records | query"
<div class="row" ng-repeat="column in invoice_records |filter:query">
<div class="col col-center brd collapse-sm" ng-repeat="field in column.columns" ng-show="data[$index].checked && data[$index].fieldNameOrPath===field.fieldNameOrPath">{{field.value}}</div>
<div class="col col-10 text-center brd collapse-sm"></div>
</div>
Demo Plunkr
In OS X, if I do a single click on a file or folder to highlight it, and then do another single click on the filename, the filename becomes an editable input box that I can change the filename in that box. Now I found xeditable http://vitalets.github.io/angular-xeditable/#overview very useful. I'm wondering in html, or Angularjs, is there such two single click event? Thanks.
Yes its a ng-dblclick used the same as ng-click
here's and example:
<button ng-dblclick="count = count + 1" ng-init="count=0">
Increment (on double click)
</button>
count: {{count}}
Solved the problem by the following code:
app.controller('QueryItemCtrl', function ($scope) {
$scope.rename = function () {
$scope.lastClickTime = $scope.lastClickTime || 0;
var now = new Date().getTime();
var delta = now - $scope.lastClickTime;
$scope.lastClickTime = now;
if (delta > 500 && delta < 1000) {
$scope.textBtnForm.$show();
}
};
})
In the html, data-ng-click="rename()".