Auto Refresh Select Menu's in Web Apps - javascript

Is it possible to refresh the Select Menu's without hitting the controller and regenerating the whole Select Menu in the Controller and sending back to the respective Div ?
I can sent the data in JSON format from the Controller but I don't want to sent the entire Div all over ... .. because it breaks the DRY principle . This is a very common scenario , I am wondering if its possible to make the Select Menu listen to some object in the JS , so that , if that Object is modified , refresh the Select Menu accordingly .... The object can be made like {value,text} etc ...Does any one have any pointers ?
How the select gets generated in the Web App ?
<form>
<content>
<div id=unique id>
<for each in some model>
<option> <value>
</div>
<content>
We have many such forms with Select Menu's ... Everytime we tend to write select to another file and call it again .
<div id=unique id>
<for each in some model>
<option> <value>
</div>
Why not re-use ?
1) Because we only need to refresh the Select not the Content
2) The data obtained in the main page is from some dictionary (pre-populated from DB) , in the refresh , we might obtain the data from the DB direct ...
So sticking to dry principle can I just populate SELECT contents by some UI listeners to some Object ? If the Object changes , refresh the select with new value . I will just write AJAX to get the JSON of the new SELECT MENU .

What about:
$.each(jsonData, function(index){
$('select').append('<option value="'+jsonData[index].value+'">'+jsonData[index].text+'</option>');
});
When JSON Data looks like:
[
{
"value":1,
"text":"Text for First Item"
},
{
"value":2,
"text":"Text for 2nd Item"
}
]
Hope I understand your question ;-)

Related

How to add dropdown menus dynamically based on previous selection?

Assume that I have a hierarchy of categories/subcategories such as:
Books
Fiction
Cooking
Seafood
Pizza
IT
HTML
JavaScript
ReactJS
NodeJS
As you have noticed, we have an unknown depth of categories where ReactJS have a depth of 3, but Fiction have a depth of 1
I am trying to create a form that includes a dropdown menu of root categories (Books, Clothes, Candies, ...etc). Whenever the user clicks on a root category (assume it is Books). Another dropdown will appear with the subcategories (Fiction, Cooking and IT). After that, when the user clicks on IT, another dropdown menu will appear below which will be HTML and JavaScript. Finally, when the user clicks on JavaScript, the final dropdown menu will appear which will contain ReactJS and NodeJS.
Please note that:
The user can choose any level and procceed. I mean he can choose Books and submit the form, or click NodeJS and submit the form.
I want to give the user the ability to rollback. I mean that maybe the user clicks on Books > IT > HTML > JavaScript > NodeJS. He can go to the second dropdown menu (Fiction, Cooking and IT), and then choose Cooking. In this case, all the drop menus below the second dropdown menu will be deleted and we will have a new drop menu which contains Seafood and Pizza.
When we submit the form, only the final dropdown menu value will be sent to the server.
I am not that strong in Frontend, I have already built and tested the Backend which receives the category/subcategory id and then returns all of the subcategories underneath it.
My questions are:
Is my approach good? (You can suggest better approaches)
If my approach is good, how to implement it in Vanilla JavaScript?
Here is a minimal code:
<div id="myDiv">
<select name="category">
<option value="">Please select a category...</option>
<!-- Some for-loop that is getting the main categories successfully -->
</select>
</div>
<script>
const request = new Request(
'Here is the URL to the controller that already gives me the subcategories successfully', {
method: 'POST',
body: JSON.stringify({parent: 1}), // 1 is the id of the parent category that we want to get its subcategories
}
);
fetch(request).then(function (response) {
return response.json();
}).then(function (json) {
for (const i in json) {
console.log(json[i]['name']); // This line prints the subcategories names successfully
}
});
</script>
It is not necessarily to give me the full code, I only need some guidelines.

Is there a way to run this script in a sequence?

For context I've made a script to assign a localstorage item based on what the user selects, this item may contain "S10" but could also contain many other values, for this example the user has selected "S10"
This has now stored into localstorage.
I have created this script that I have placed in each of the product grid includes, so this script will load multiple times for each product:
window.onload = function() {
const string = localStorage.getItem('MyfitmentList', JSON.stringify(myfitment) );
const attr = {{ product.metafields['fitment']['fitment'] | json }};
document.getElementById("fitment").innerHTML = (`${string.includes(attr) ? '✔' : 'not a fit' }`);
}
Each product grid also features this container <div id="fitment"></div>
The script above loads individually for each product grid card and has it's own seperate values for "attr". If "attr" doesn't contain "S10" (which the user previously selected) then it should show "not a fit", but if the product does contain "S10" then it should show the tick.
The problem I'm having is that this only works for the first script, on the first <div id="fitment"></div> container. I can see that each individual script has a different "attr" value which is good, but I don't know why only the first container is populated.
Each script should run individually and populate the "fitment" container within it's own product card. I'm a bit at a loss as to how to do it.
Also this is on Shopify - Someone below has recommended using a single script with a loop, but in that case how would I retrieve a unique "attr" value each time the script loops? this would still leave the question of how to populate each fitment container with the correct message for each product.
Any help would be really appreciated!

How to bind items to a sap.m.table in JS with correct use of template

I am trying to bind items to a sap.m.table in JS. XML binding does not allow dynamic filtering so this is not an option.
The scenario is quite basic: First screen is a list view. Second screen is a detail view for the selected entry of the list view. The detail view contains a table with additional information and in each line the user can select one of two radio buttons and hit an action button to perfom the selected action. Information which radio button is enabled and pre-selected is also returned from the backend and read from the model. The model has two-way-binding to reflect and react to user changes in the context but nothing is ever written back to the backend.
I would like to run the following code in the _onObjectMatched() after an entry was clicken on the first screen.
oTable.bindItems({
path: 'Model>/pathToEntitySet',
filters: aFilter,
factory: function(sId, Context) { return that.byId('template').clone(sId);
templateShareable: false,
parameters: {
'expand': 'anotherPath'
}
});
The template is defined in XML as of the table:
<core:FragmentDefinition
xmlns:core="sap.ui.core"
xmlns="sap.m"
xmlns:l="sap.ui.commons.layout"
xmlns:table="sap.ui.table"
xmlns:semantic="sap.m.semantic">
<Table
id="table"
alternateRowColors="true">
<columns>
...
</columns>
<dependents>
<ColumnListItem id="template">
<cells>
...
<FlexBox>
<RadioButtonGroup columns="2" selectedIndex="{Model>SelectedAction}">
<RadioButton
enabled="{= ${Model>isPos1}}"
text="{i18n>doThis}"/>
<RadioButton
enabled="{= ${Model>isPos2}}"/>
</RadioButtonGroup>
<ComboBox
items="{
path: 'Model>anotherPath',
templateShareable: false
}"
selectedKey="{Model>SelectedKey}"
enabled="{= ${Model>isPos2}}">
<core:ListItem key="{Model>Tey}" text="{Model>Text}"/>
</ComboBox>
<Button id="actionButton"
press="onActionButtonPress"
enabled="{= ${Model>isPos1} || ${Model>isPos2}}"
icon="sap-icon://begin"/>
<Label text="{Model>SelectedAction}"/> <!--Just for debugging purpose-->
</FlexBox>
</cells>
</ColumnListItem>
</dependents>
</Table>
</core:FragmentDefinition>
The first time I select an entry on screen one everything is loaded correctly.
Then I go back and click on the same line again and suddenly all values for Model>SelectedAction are displayed as -1. I can see in the Browser network analysis that the response is correct from the backend but somehow everything turns into -1 in the frontend.
I finally managed to fix this by adding these two lines before oTable.bindItems({}). I would have expected that unbinding manually is not required before binding changes. Also I don't understand why I have to reset the model (two-way-binding) even if I did not do any change like selecting another radio button.
oTable.unbindItems();
this.getView().getModel("Model").resetChanges();
The detail page still behaves funny. I cannot select a radio button. Instead when I click on a radio button they disappear for a millisecond and just appear again. I could fix this by replacing the factory with a template in the binding function:
oTable.bindItems({
path: 'Model>/pathToEntitySet',
filters: aFilter,
template: that.byId("template").clone(),
templateShareable: false,
parameters: {
'expand': 'anotherPath'
}
});
This code seems to be working but I am not having a good feeling about it. Feels not very stable. E.g. why cant UI5 take care of cloning my template? If I am not cloning it manually, a missing template error will be thrown the second time I open the detail view. And why do I have to unbind the items manually and reset the model?
Can you give some feedback if I am doing something fundamentally wrong or if this is actually the way how it is supposed to work?

Pass information to bootstrap modal from Angular.js controller

Simplified problem
I have a store. For a product to be included in the store there needs to be a shelf for it. So, to add a new product to the store the workflow is:
Add a shelf
Add product to that shelf
(The workflow can not be changed)
Realization
The shelf is realized by a row in a table, which in turn is controlled by an Angular.js controller (each shelf is an object in an array). To add an product the user selects "create product" in a drop-down menu that is present on each row. This will show an bootstrap modal where I have from a controller added a tab for each product that is possible to add (since each product needs configuration :) ) , then when the user presses a "create" button in the modal a JavaScript method is called interfacing a REST interface to add the product (the UI is updated by a Socket.io event send from the server when the product has been added successfully.
Problem
The JavaScript method (CreateProduct) needs to now what row (R) was affected as well as what tab (T) was selected so that the "onclick" method for the button is CreateProduct(R, T);
My current solution is pretty ugly imho, I have two global variables for R and T, then I use jQuery to capture show event and tab event from the modal, the link in the dropdown has a field "data-row-id" that is identifying the row
HTML (Jade) snippet from dropdown menu:
a(data-toggle="modal", href="#createProduct", data-row-id="{{row.RowID}}") Create Product
JavaScript:
var R = null;
$('#productModal').on('show.bs.modal', function(e) {
R = $(e.relatedTarget).data('row-id');
});
var T = null;
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
T = e.target.text;
});
I hope there is a better solution to this, I probably am just thinking a bit upsidedown due to inexperience with Angular.js , perhaps there is a way to pass these through the model? Perhaps add these to the modal controller, but then, how to pass the row? My dream would be something like this on the button code
button(type="button", class="btn btn-default", data-dismiss="modal", ng-click="storeCtrl.CreateProduct({{modalCtrl.shelf, modalCtrl.Product)") Create Product
I found a better way (at least I don't need to use jQuery) using ModalService
I created a CreateProductModalController having a variable selectedProduct, this is set on a ng-click event in the tab
ul(class="nav nav-pills", id="tabContent")
li(ng-repeat="prod in products", ng-class="{active: $index == 0}", ng-click="activateTab(prod.name)")
a(href="#{{prod.name}}", data-toggle="tab") {{prod.name}}
The ModalService is called with the rowID that was clicked.
The only problem I have now is that all must be in $scope, I want it to be more encapsulated

How do I implement cascading dropdown using golang's templates

Scenario:
I have a cascade scenario, where values in second dropdown depends upon first. I have three templates "layout", "input" and "inner".
Attempt:
I'm making ajax call on change of first dropdown in "input" template and stuck with handling the response returned. Currently I found a way to fix it by replacing html of second dropdown. But, I think this is not the better way to handle. I want something in line of rendering templates where I do not need to amend html.
please help in acheiving the task in better way or point to some wiki. Only Standard Library
Thanks,
Layout.html:
http://play.golang.org/p/LikKy6rf3-
Input.html:
http://play.golang.org/p/wM7EpPdXuM
Inner.html:
http://play.golang.org/p/xFpInKZfFT
Main.go:
http://play.golang.org/p/cxtJU-Lhi1
On the higher level you have 2 options:
Either send all the values for the dropdown lists (e.g. as a tree) and change the values on the 2nd and 3rd level when the dropdown of a higher level changes (suitable for small lists, unsuitable for large dataset)
Or the one you chose: when selection changes, you make an AJAX call (triggered from onchange) and you populate the list from the results.
Elaborating #2: populating list from the AJAX call result
You also have 2 options to do this:
Either the AJAX call returns HTML call which you can simply use to replace the inner HTML of the HTML <select> tag.
Or the AJAX call may only return the data (e.g. encoded using JSON), and a Javascript code can build the content of the list.
AJAX returning HTML
The AJAX call may return the complete HTML code needed to be replaced as the inner HTML of the <select>. To achieve this at server side, you can create/separate the HTML template responsible only to generate the HTML code of this, for example:
{{define "innerList"}}
{{range .}}
<option value="{{.Key}}">{{.Text}}</option>
{{end}}
{{end}}
You can execute only this template like this:
// tmpl is the collection of your templates
values := ... // Load/construct the values
tmpl.ExecuteTemplate(w, "innerList", values)
Here values is a slice of the following structure:
type Pair struct {
Key string
Text string
}
Building <select> content with Javascript
The AJAX call may return a JSON datastructure, an array/list of value;text pairs from which you add the <option> child tags yourself.
To add an <option> to a <select> tag:
var x = document.getElementById("mySelect");
var option = document.createElement("option");
option.value = "1234";
option.text = "Kiwi";
x.add(option);
So basically what you need to do is remove current children of the <select>, iterate over the list received as the response and add a new <option> tag constructed from each.

Categories

Resources