AngularJS keep category expanded at where user left off - javascript

I am new to the angularJS. And recently I have been working on a angularJS project. I use the ng-template and ng-repeat to make a multiple product category tree. And I add a button to control whether the sub-category expanded or not.Here is the problem,How I can keep the category expaneded at where user left off when the user click into other pages and click back to the category page?

You could save the current tree state into the HTML5 Localstorage.
Add a unique id to each category and save them into the localstorage.
function saveCurrentState() {
const openedCategories = /* find the opened categories */;
// we injected $window in our controller.
$window.localStorage.setItem('openedCategories', openedCategories.join(','));
}
Then, when you load the page
/* to be run on the page load.*/
function retrieveOpenedCategories() {
// all the current categories, open or not.
const categories = /* get all the categories */;
// we retrive our category from the localStorage. With some code to handle if it's empty / null.
const openedCategories = ($window.localStorage.getItem('openedCategories') || "").split(',');
// check if we have categories that were opened. length of 0 will evaluate to false-y.
if(openedCategories.length) {
// we use map because we want to change every value from the original array.
categories = categories.map((category) => {
if(openedCategories.includes(category.id)) {
category.open = true;
}
return category;
});
}
}
/* we show our categories, somehow.*/

Related

Angular: Tabkey Press not Populating fields

I'm new to Angular, I have a component which contains some basic fields and dropdown menu.When I select anything from dropdown using tab key the other related field are not getting populated,But it works with mouse click.
receipt-creation.component.ts
populateItems(event: any, obj) {
this.receiptForm.valueChanges.subscribe(() => {
const dirty = this.isFormDirty(this.receiptForm);
if (dirty) {
this.unsavedChanges = true;
}
});
this.receiptLineItems = obj.itemList; // obj.itemList contain the values related to selected item
this.changeDetectorRef.detectChanges();
this.updateSubtotal(); // this function caluclate the total based on the item selected from the dropdown
}
receipt-creation.component.html
<add-new-row
[addNewText]="INVOICE_CONSTS.ADD_LINE_ITEM"
[canAutoAddNewRow]="false"
[checkPristine]="true"
[type]="'receipt'"
[data]="lineItemsMetaData"
[form]="receiptCreationForm"
[triggerExtraAddNewKey]="true"
[itemList]="receiptLineItems"
(changeItemEvent)="populateItems($event, receiptCreationForm)"
(keydown.enter)="preventFormSubmission($event)"
></add-new-row>
With event.preventDefault() the fields gets populated, but I want the default behaviour of tab key to be working.

Sometimes the list connects other times it doesn't

Sometimes the lists connect and you can transfer between them. Other times it doesn't connect. At all times you can sort within each list, but sometimes not between them. I can't figure it out.
$('#questions .survey-page ul').sortable({
items: 'li:not(.placeholder)',
sort: function() {
$(this).removeClass('ui-state-edit'); // While sorting we do not want edit buttons to show.
},
update: function() {
refreshAllDetails(); // Update survey with the new details.
},
connectWith: '#questions .survey-page ul'
});
#question is a tag that multiple .survey-page children are put into. Each .survey-page has a ul with multiple li entries. It is this ul that I am trying to link between .survey-pages.
EDIT: As per request:
/**
* Saves the order of questions, then saves the details of all questions to server.
*/
function refreshAllDetails() {
saveOrder();
saveAllToDatabase();
}
/**
* Saves the details of all questions to server.
*/
function saveAllToDatabase() {
// Go through each page.
$("#questions").find(".survey-page").each(function() {
var surveypage = this;
// Save metadata for current page.
// Go through each question on page.
$(this).find(".questiontypestuffp").each(function() {
// Get the answers for a particular question, including meta-data for question.
var result = callWidget($(this), "getEditedAnswers");
// Get the order of the question listed on page.
result.questionorder = $(this).attr('ordervalue');
result.pageno = $(surveypage).attr("ordervalue");
// Save the question's order to its associated widget.
callWidget($(this), "setData", result);
// Update the question in database.
$.ajax({dataType: "json", url: "index.php?option=com_survey&loadorsave=update&view=surveydata&layout=edit&id=" + $("#itemid").val() + "&tmpl=component&format=json&questionvalues=" + encodeURI(JSON.stringify(result)), success: function(callback) {
}});
// Turn off edit mode.
setEditModeOff();
});
});
}
/**
* Refreshes order values with regard to their position on page. This rewrites the order values as they appear.
*/
function saveOrder() {
var pageorder = 0;
// GO through each page.
$("#questions").find(".survey-page").each(function() {
var questionorder = 0;
// Rewrite page order.
var currentPage = ++pageorder;
$(this).attr('ordervalue', currentPage);
// Rewrite each question's order on page.
$(this).find(".questiontypestuffp").each(function() {
$(this).attr('ordervalue', ++questionorder);
});
});
}
I've solved my own problem here. The class I was removing was actually required to recognize drag and drop operations, hence 'ui-state-edit'. By dynamically updating this class to/from the DOM element, this was affecting whether the list item was able to be transferred between lists. As a brief - drag and drop was rejecting the list item since it didn't have a valid class name.

Using part of Text to control redirect using Protractor

In our project, there are different urls assigned to different categories of product. If the product category is Cat1, click on edit button should take the user to the Cat1 page, and Cat2 should take the user to Cat2 page. However these categories are in a dynamic table so we can not use a fix reference for the edit buttons, and I am trying to make it dynamic. Below is my code snippet:
it('should take the user to appropriate page', function () {
expect(globalVariables.Edit_Button_1.isDisplayed());
// get rows
var row_1 = globalVariables.tableData_Dashboard.all(by.tagName("tr")).get(1);
// get cell values
var cells = row_1.all(by.tagName("td"));
var Cetegory = cells.get(3).getText().then(function (GL) {
// console.log(GL)
return GL;
});
globalVariables.Edit_Button_1.click();
browser.wait(EC.invisibilityOf(globalVariables.Edit_Button_1), 25000, 'Edit button is not disappearing yet');
if (Cetegory.endsWith('Cat1')){
expect(browser.getCurrentUrl()).toEndWith("Cat1");
}
else {
expect(browser.getCurrentUrl()).toEndWith("Cat2")
}
The tests fails with the log " Failed: Cetegories.endsWith is not a function ..
How can this be fixed?
Cetegory is a promise, not a string. Thus it does has function endsWith. You need to consume the promise eventual value in then() as following.
Cetegory.then(function(_Cetegory){
if (_Cetegory.endsWith('Cat1')){
expect(browser.getCurrentUrl()).toEndWith("Cat1");
}
else {
expect(browser.getCurrentUrl()).toEndWith("Cat2")
}
})

Keep checkbox checked after page refresh AngularJS

I'm quite new to AngularJS and had to takeover somebody else's project at work which has little to no documentation.
I have 2 kinds of check-boxes in my application, one is a "Select All" checkbox and another is a device selection checkbox. As the name suggests, the select all will select all the devices listed below it and if I uncheck the "select all" checkbox, I can check the devices individually to see them.
Here is the code of the Select all checkbox -
<input type="checkbox" data-ng-model='devCtrl.uiChoices.selectAll' value='true' data-ng-change="devCtrl.selectAll()"/><h4>Select / Deselect All</h4>
Controller:
_this.uiChoices.selectAll = true;
I can understand from above that by default, select all is checked and I can see all the devices below it checked too.
Moving onto the device check-box -
<input type="checkbox" data-ng-model='device.draw' data-ng-change="device = devCtrl.adjustVisibility(device)" />
Controller -
_this.adjustVisibility = function(draw) {
draw.marker.setVisible(draw.plot);
return draw;
}
Basically, whenvever the device is selected, it will appear on a google map. If it is unchecked, it won't appear on the map.
My question is, after I uncheck the "Select all" checkbox and then select only 2 devices in the list below and then do a page refresh, I want the select all to be disabled and show only those 2 devices to be checked and displayed on the map.
The list of devices is being pulled from a MySQL database and is updated dynamically.
Any help is appreciated.
As I said, you can do it by 3 different ways.
1 - Using $scope variable
In AngularJS you have a main Controller usually set at index.HTML body that you can access from all other controllers. You could use it to store your data on the $scope variable. See the example:
index.html:
<body ng-controller="DefaultController">
DefaultController.js:
angular.module('YourModule').controller('DefaultController', ['$scope', function ($scope) {
//Storing Your data
$scope.isChecked = true;
}]);
YourCheckBoxController.js
angular.module('YourModule').controller('YourCheckBoxController', ['$scope', function ($scope) {
//Here you gonna access the $scope variable, that does not change on page reload
$scope.accessingTheVariable= function () {
if ($scope.isChecked) {
//Select All
}
else {
//Do not Select All
}
};
$scope.onCheckBoxToggle {
$scope.isChecked = _this.uiChoices.selectAll;
//More code
};
}]);
2- Using localStorage
//The property exists
if (localStorage.hasOwnProperty("isChecked")) {
if(localStorage.isChecked) {
//Select All
}
else {
//Do not Select All
}
}
//To set localStorage.isChecked
localStorage.setItem("isChecked", _this.uiChoices.selectAll);
3 - Angular Service (Factory)
On this scenario you should create a service that could be accessed from every Controller in your project (usefull if you gonna use the data on more than 1 Controller). Look:
YourService.js
angular.module('YouModule').factory('YouService', function () {
var data =
{
IsChecked = true
};
data.buildIsChecked = function (isChecked) {
this.IsChecked = isChecked;
};
return data;
});
YourIsCheckedController.js:
angular.module('YourModule').controller('YourCheckBoxController',
['$scope', 'YouService', function ($scope, YouService) {
//Setting the service
//...
YouService.buildIsChecked(_this.uiChoices.selectAll);
//Accessing the service Information (it could be accessed from any Controller, just remember to set Service Name at begin of the module declaration)
var isChecked = MenuService.IsChecked;
}]);
You need a way of saving those checked devices.
Try localStorage. Basically, when you select a device, add it to an array, like checkedDevices and add this array to localStorage like so:
localStorage.setItem("devices", JSON.stringify(checkedDevices));
then, at the beginning of your controller, get this array from the localStorage:
var devices = JSON.parse(localStorage.getItem("devices"));
then, check if it has items, if it does, set selectAll to false:
if (devices.length > 0){
this.selectAll = false;
}else{
this.selectAll = true;
}
then, for every device, check if it is in devices array, if it is, select it.

Listjs Make button for last page after filter

list.js Question:
How do you create a div so when you click it, it shows the last page of the pagination, after I have done a filter.
I have a list
var List = new List('list', {
valueNames: ['name'],
page: 5,
plugins: [ListPagination({})]
});
And say this list has 20 pages.
After I apply a filter:
List.filter(function(item) {
if (item.values().category.toLowerCase().indexOf('wordtofilter') > -1) {
return true;
} else {
return false;
}
});
It now has 5 pages. I want to have a button when I click it will take me to the last page.
Currently I can get to the last page of an unfiltered list using this:
$('.go-to-last-page').on('click', function(){
List.show(List.size(), 5);
});
But If i filter my list, and click it, it will attempt to take me to page 20, instead of 5. How do I make it so it takes me to the last page of the filtered list? (page 5)
I had this same problem a few years after your question, so I'm sharing my solution in case it might help someone else:
First I create the first and last page buttons:
<nav>
<button id="btn-first">FIRST</button>
<ul class="pagination"></ul>
<button id="btn-last">LAST</button>
</nav>
Secondly, in js I assign the data attributes that the paging buttons generated by list.js have by default.
const LIST_PAGINATION = 10;
var btn_first = document.getElementById('btn-first');
var btn_last = document.getElementById('btn-last');
btn_first.addEventListener("click",function(e){
btn_first.dataset.i = 1;
btn_first.dataset.page = LIST_PAGINATION;
},false);
btn_last.addEventListener("click",function(e){
let total = list.matchingItems.length; // list.js object in my case I called it "list"
let page = Math.ceil(total / LIST_PAGINATION);
btn_last.dataset.i = page;
btn_last.dataset.page = LIST_PAGINATION;
},false);

Categories