I'm editing a custom theme in BigCommerce, and I'm working on the product options section of the code on a product page. This is for a product page for a product with multiple options. A default parent SKU is shown above the options. Upon choosing an option, the new, final SKU gets populated above. The handlebar code to show that SKU is {{product.sku}}.
I've found that customers get confused and don't realize there are other SKUs available if they just choose the right option, so I want to show the SKU within the label of the option.
Here is the part of the code I'm targeting (from set-rectangle.html):
<div class="form-field" data-product-attribute="set-rectangle">
<label class="form-label form-label--alternate form-label--inlineSmall">
{{this.display_name}}:
<span data-option-value></span>
{{#if required}}
<small>{{lang 'common.required'}}</small>
{{/if}}
</label>
{{#each this.values}}
<input
class="form-radio"
type="radio"
id="attribute_rectangle__{{../id}}_{{id}}"
name="attribute[{{../id}}]"
value="{{id}}"
{{#if selected}}
checked
data-default
{{/if}}
{{#if ../required}}required{{/if}}>
<label class="form-option" for="attribute_rectangle__{{../id}}_{{id}}" data-product-attribute-value="{{id}}">
<span class="form-option-variant">{{this.label}}<br>{{product.sku}}</span>
</label>
{{/each}}
</div>
I've tried inserting {{product.sku}} inside the label - as you can see above, but that doesn't work - nothing populates. I think it needs to reference the option with a "this" in there somewhere, but that's the extent of my coding savviness.
A single product attribute does not necessarily determine the variant, as there may be more attibutes, a combination of which would determine a specific variant (e.g. "Size" and "Color" Vs. just "Size").
That said, when (and only when) you have variants defined by a single product attribute (e.g. "Size"), you could actually bind a specific SKU to each of the attribute values (e.g. "30ml" => sku "AAA", "50ml" => sku "BBB").
I'm afraid the variant SKUs are not included in the data available as part of the "values", for the reasons above.
In fact, if you add the "debug" line (useful sometimes) using the "json" helper:
{{#each this.values}}
<input
class="form-radio"
type="radio"
id="attribute_rectangle__{{../id}}_{{id}}"
name="attribute[{{../id}}]"
value="{{id}}"
{{#if selected}}
checked
data-default
{{/if}}
{{#if ../required}}required{{/if}}>
<label class="form-option" for="attribute_rectangle__{{../id}}_{{id}}" data-product-attribute-value="{{id}}">
<span class="form-option-variant">{{this.label}}</span>
</label>
<!-- {{{json this}}} -->
{{/each}}
(note "json this"), you will see that the HTML produced only includes, for each variant, the "label", "id" (variant ID), "data", and "selected" properties, no variant SKU in there...
<!-- {"label":"30ml","id":104,"data":"30ml","selected":false} -->
If you can identify the product variant ID (the above is the option value ID, not a variant ID), you can retrieve the missing data using the BigCommerce Store Front API (specifically GraphQL) and JavaScript, and then use that data to inject SKUs in your HTML, see the following example from BigCommerce:
https://developer.bigcommerce.com/api-docs/storefront/graphql/graphql-storefront-api-samples#get-variant-details-as-a-product-object
That JavaScript would look something like this:
<script>
(function(w) {
const sfApiToken = '{{json settings.storefront_api.token}}';
if (sfApiToken) {
w.document.querySelectorAll('[data-vidsku]').forEach((e) => {
const vid = e.getAttribute('data-vidsku') || null;
if (vid) {
const queryS = `query VariantById {
site {
product(variantEntityId: ${vid}}) {
sku
}
}
}`;
fetch('/graphql', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${sfApiToken}`
},
body: JSON.stringify({query: queryS})
})
.then((res) => res.json())
.then((json) => {
e.textContent = json.product.sku || '';
});
}
});
}
})(window);
</script>
Related
Working on an ASP.NET Core 6 MVC app. I am created a dropdown using the select asp-for tag helper. But I am not seeing a dropdown down arrow. Also I want to set the top or particular value selected by default.
Below is code and image of a dropdown
<div class="col-sm-3">
<select name="products" class="form-control "
asp-items="#(new SelectList(ViewBag.ddaircraft,"id","name"))">
</select>
</div>
Action Method code for ViewBag:
Public IActionResult Index()
{
var countries= _countries.getCountries();
//add an country item on the top of list.
countries.Insert(0, new Aircraft { Registration="0"});
//i used the country.name for value and item
var ddforAircraft = from country in countries
select new { id = country.name, name=country.name=="0"?"Item List":country.name };
// ddforAircraft.Append(new { id = "0", name = "" });
ViewBag.ddaircraft = ddforAircraft;
return View()
}
I found the answer, after Tiny Wang pointed me to the direction which really helped me to search the answer.
In order to see the dropdown down arrow I added a css class "form-select" without removing anything and I started to see the down arrow
<div class="col-sm-3">
<select name="products" class="form-control form-select-sm form-select " asp-items="#(new SelectList(ViewBag.ddaircraft,"id","name"))">
</select>
</div>
Missing dropdown arrow resulted from the class form-control, I test in my side and I found the arrow can be seen by default until I add class="form-control " to my code:
removing this 2 options then the arrow appeared again, so it proved to relate to the class, you may need to update the style:
Then I use Jquery to change the default selected option when page is loading in my code, my selector has Id Country, then change the value(ListItem.Value):
<select asp-for="Country" asp-items="#(new SelectList(Model.Countries, nameof(ListItem.Value), nameof(ListItem.Text)))">
<option>Please select one</option>
</select>
#section Scripts{
<script>
$("#Country").val('Canada')
</script>
}
Using Laravel 5.4 and Vuejs 2.1
In code I have a select field with two input fields (quantity and stock) in tr (Table Row). Table Row with the fields can be dynamically added as much as user is needed, this is the code:
<tbody id="loads-content">
<tr v-for="(item, index) in items">
<td>
<select v-model="item.load_id" name="load_id[]" class="loads" v-on:change="getLoadStock()">
<option v-for="load in loads" :value="load.id" :id="load.id">{{load.name}}</option>
</select>
</td>
<td><input type="text" v-model="item.quantity" name="quantity[]" class="input is-hovered qty"></td>
<td><input type="text" v-model="item.stock" class="input is-hovered stock" disabled readonly="true"></td>
<td>
<button type="button" class="button is-danger remove" #click="items.splice(index, 1)"><i class="fa fa-minus-square-o" aria-hidden="true"></i></button>
</td>
</tr>
<a #click="addLine" class="button is-success"><i class="fa fa-plus-square-o" aria-hidden="true"></i></a>
</tbody>
When you choose some value from select field I need to populate the STOCK input field with stock data from the database. For that I used an API call. I made something like this:
methods: {
addLine() {
this.items.push({
load_id: '',
quantity: '',
stock: ''
})
},
getLoadStock(){
var loadsContent = $('#loads-content');
var tr = loadsContent.parent().parent();
var id = tr.find('.loads').val();
axios.get('/api/load/' + id)
.then(function(response) {
tr.find('.stock').val(response.data.stock);
})
.catch(function(error) {
console.log(error)
});
},
This code is not working as expected.
The goal is to fetch actual stock for current choosen load, to see how much quantity can you enter in the input field for quantity.
I am open for any suggestions, if anyone has a better approach and solution please help.
Thanks in advance! :)
You are mixing two different and incompatible ways of building a web app: Vue.js and jQuery.
When using Vue, you should not be manipulating the DOM directly. You should instead be binding the DOM elements to Vue model attributes. Then, if you need a DOM element to reflect a change, you change the model – not the DOM.
So for instance, you would want something like this (note adding index as an argument to getLoadStock):
<select v-model="item.load_id" name="load_id[]" class="loads" v-on:change="getLoadStock(index)">
<option v-for="load in loads" :value="load.id" :id="load.id">{{load.name}}</option>
</select>
and
getLoadStock(index){
axios.get('/api/load/' + this.items[index].load_id)
.then(function(response) {
this.items[index].stock = response.data.stock;
})
.catch(function(error) {
console.log(error)
});
},
I am very new to CanJs just started learning, I am struck with below problem not able to debug it.
I have model as below:
Localized = can.Model({
findOne : 'GET /resources/localized'
}, {
});
GET /resources/localized ---> fetches available localized languages.
And I have defined a component as below
can.Component({
tag : 'preferences',
template : initView,
init: function() {
console.log(locales);
},
scope : {
locales: new LocalizedModel.findOne({})
}
});
initView has mustache template as below:
<div class="form-group">
<label>{{dateLayout}}</label>
<select class="form-control" id="lang" name="lang" can-change="save">
{{#list locales.languageOptions}}
<option value="{{name}}">{{name}}</option>
{{/list}}
</select>
</div>
But the problem is locales are not getting populate, I could see network call for /resources/localized, any pointers here could really help to understand this.
Be careful to use the correct naming, especially if you were reading up on both EJS and Mustache building from the CanJS documentation. The helper that iterates over a list is {{#each listref}}...{{/each}} in can.Mustache and can.Stache, where in can.EJS it is <% list(listref, function(item) { %>...<% }) %>. Using {{#list ...}} will likely not produce anything.
I am using the tokeninput control found here at http://loopj.com/jquery-tokeninput/ - its quite popular I believe.
I have the following code that fills the input box nicely with author names - but I want to prepopulate the control with values found in the database when the user is in an EDIT session i.e. to find authors that have been found for that record already (looks something like this):
Here's the code:
$("#authorlist").tokenInput('/author/getauthors/', {
hintText: "Enter surname",
searchingText: "Searching...",
preventDuplicates: true,
allowCustomEntry: true,
highlightDuplicates: false,
tokenDelimiter: "*",
theme: "facebook"
// prePopulate: [{"id": 5016, "name": "Test 1" }]
});
Obviously this already gets a full list of authors (/author/getauthors/) - but it needs to prepopulate from that list too, with authors already found that record - and thats the bit I can't seem to figure out.
I can see that that you can use prePopulate in the javascript (I've commented it out) and I have the found author values in my Edit.cshtml i.e.
#foreach(var item in Model.AUTHORs.Select(model => new { model} ))
{
<div type="hidden" id="authorHidden" > #item.model.FULL_NAME</div>
}
So it's just a case of putting those values in some kind of json format and getting the tokeninput control to populate them ready for when the form is loaded and shown to the user.
Other code for displaying the tokeninput control in Edit.cshtml is:
<div class="editor-label">Authors</div>
<div class="authors">
<div class="editor-field">
<input type="text" id="authorlist" name="tiAuthors" />
</div>
</div>
Any help or pointers are much appreciated.
You could use an HTML5 data-* attribute on your input inside the view to put the list of authors that you want to be prepopulated:
<input type="text" id="authorlist" name="tiAuthors" data-authors="#Json.Encode(Model.AUTHORs.Select(a => new { id = a.AuthorId, name = a.AuthorName })))" />
and then:
$('#authorlist').tokenInput('/author/getauthors/', {
hintText: 'Enter surname',
searchingText: 'Searching...',
preventDuplicates: true,
allowCustomEntry: true,
highlightDuplicates: false,
tokenDelimiter: '*',
theme: 'facebook',
prePopulate: $('#authorlist').data('authors')
});
There's a ton of info on the interwebs about how to handle dynamic views (via ajax calls) with Knockout, but is there a best practice for dynamic viewmodels?
For instance, say I have a single page app that renders (via ajax) different types of forms (with different input fields) based on role, user choices, contexts, etc. Not only would I use templates for each form, but I'd like to do the same for the viewmodel, since each viewmodel may have many very different properties and it wouldn't be practical to have one massive viewmodel for every possible template.
I'm a bit of a rookie with ko, and it may not be meant to be used in this fashion. Any advice is much appreciated.
A popular way to do this type of thing is to have a main view model that hosts sub-view models.
Here is a really basic example of defining "model" objects that have a template and associated data.
function Model(key, template, data) {
this.key = key;
this.template = ko.observable(template);
this.data = data;
}
var viewModel = {
models: ko.observableArray([
new Model("user", "userTmpl", { first: "Bob", last: "Smith" }),
new Model("item", "itemTmpl", { name: "MyItem", description: "Here are some details" })
]),
selectedModel: ko.observable()
};
ko.applyBindings(viewModel);
Then, you could use it like:
<select data-bind="options: models, optionsText: 'key', optionsCaption: 'select a model...', value: selectedModel"></select>
<hr />
<div data-bind="with: selectedModel">
<div data-bind="template: { name: template(), data: data }"></div>
</div>
<script id="userTmpl" type="text/html">
<span data-bind="text: last"></span>, <span data-bind="text: first"></span>
</script>
<script id="itemTmpl" type="text/html">
<h3 data-bind="text: name"></h3>
<div data-bind="text: description"></div>
</script>
http://jsfiddle.net/rniemeyer/29kWf/
Obviously, you wouldn't likely bind the selection of the model in a select, but it helps show how it can work. Rather than an array your models could be an object with the property names matching the key.
The "data" in the "model" objects would be your sub-view models.
I'm Facing the same problem.
Try Knockout Namespaces