<span class="counter qty empty"
data-bind="css: { empty: !!getCartParam('summary_count') == false }, blockLoader: isLoading">
<span class="counter-number">
<!-- ko if: getCartParam('summary_count') --><!-- ko text: getCartParam('summary_count') --><!-- /ko --><!-- /ko -->
<!-- ko ifnot: getCartParam('summary_count') --><!-- /ko -->
</span>
<span class="counter-label">
<!-- ko i18n: 'items' --><!-- /ko -->
</span>
</span>
the span counter bind some data via KnockoutJS! , i need to know the before and after data bind states in the span ??
There is a beforeChange event that you can subscribe to in your view model:
myViewModel.counterObservable.subscribe(function(oldValue) {
alert("The counters's previous value is " + oldValue);
}, null, "beforeChange");
Related
I want to set up a partial to render a generic, customizable modal. {{> myModal }}.
I want to customize the modal from the calling view by sending an object.
I have tried the approach as recommended here: Passing an array of objects to a partial - handlebars.js
Here is my code from within the view which calls the partial:
{{# getJsonContext '
{
"id": "deleteModal",
"title": "Are you sure?",
"formId": "delete-form",
"body": "Press Yes to delete this record. Press No to cancel."
}
'}}
{{> myModal this }}
{{/ getJsonContext }}
Here is my helper:
getJsonContext: function(data, options) {
console.log(data); <-- The result is correct.
let jsonReturn = JSON.parse(data);
console.log(jsonReturn.title); <-- The result is correct.
return jsonReturn;
},
Here is my partial:
<div class="modal fade" id="{{ id }}">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header bg-light">
<h4 class="modal-title">{{ title }}</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Form -->
<form id="{{ formId }}" method="post">
<!-- Modal body -->
<div class="modal-body">
{{ body }}
</div>
<!-- Modal footer -->
<div class="modal-footer rounded-bottom bg-light">
</div>
</form>
</div>
</div>
</div>
I expect the partial view to render the customized modal.
The partial does not seem to render at all. When I "View Page Source" in the browser, in the place of all of the partial <html>, all it shows is [object Object].
If I place the {{> myModal this}} outside of the helper, "View Page Source" shows all the modal's <html>, but naturally, the id, title, formId and body are all blank (null).
After racking my brain with lots of trial and error and research, I finally found out the problem. I had to pass the JSON as a named parameter in the main view's call to the partial. The syntax should have been:
{{> jeff-modal <anyVariableName> = getJsonContext }}
I finished up and made my generic {{> jeff-modal }} accept a list of buttons, which can be customized.
The modal is now useful as a generic, customizable, simple modal which can be rendered as a partial from within any view to require the user to press one of x-number of buttons to make a choice.
Example
The main (calling) view has a list of journal entries (transactions) in date order. For each transaction, there is a clickable "delete" icon. The modal opens when the user presses the icon. When the modal is opened, we need to know which transaction to delete. We have its _id.
After setting up the table and columns in <html> and starting an {{# each }} iterator, the following is how the "delete" icon is set up for each transaction. Notice how the data-id's value is set to the transaction's _id property:
<!-- table stuff up here -->
{{# each transactions}}
<tr class="journal-entry-row">
<!-- some `<td>`'s -->
<span class="journalData">
<a href='#' class="open-deleteDialog"
data-id="{{this._id}}" data-backdrop="static"
data-toggle="modal" data-target="#deleteModal">
<span class="material-icons md-24 md-dark">delete</span>
</a>
</span>
<!-- more table stuff -->
{{/ each }}
The material-icons, above, can be found here: https://material.io/icons/
"delete" is a trash can.
Below is the javascript to set the onclick for all the trash cans. Notice how the transaction's _id makes the trip from the <html> into the javascript via the data-id (above). (I thought that was pretty clever, whoever thought of it.)
Also, since our custom modal was assigned an id of "delete-form", we can get it to set the modal's <form> action.
Finally, a little CSS is added to highlight the row being deleted.
$(document).on("click", ".open-deleteDialog", function () {
var myTransactionId = $(this).data('id');
// Set form action to call delete with _id parameter
$("#delete-form").attr("action", "/transaction/delete?_id=" + myTransactionId );
// Highlight the selected row.
$(this).closest(".journal-entry-row").addClass("table-warning");
});
Now, let's go back to the calling view after the end of the <table>. This is where the modal is called within the handlebars helper block (but only when the trash can is clicked):
<!-- The Delete modal -->
{{# getJsonContext '
{
"id": "deleteModal",
"title": "Are you sure?",
"formId": "delete-form",
"body": "Press \"Yes\" to delete this record. Press \"No\" to cancel.",
"buttons":
[
{ "type": "submit", "onclick": "", "class": "btn btn-primary", "text": "Yes" },
{ "type": "button", "onclick": "removeHighlightEffect()", "class": "btn btn-secondary", "dataDismiss": "modal", "text": "No" }
]
}'
}}
{{> jeff-modal options = getJsonContext }}
{{/ getJsonContext }}
Here's the helper:
getJsonContext: function(data, options) {
let jsonReturn = options.fn(JSON.parse(data));
return jsonReturn;
},
Finally, the partial {{> jeff-modal }}:
<div class="modal fade" id="{{ id }}">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header bg-light">
<h4 class="modal-title">{{ title }}</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Form -->
<form id="{{ formId }}" method="post">
<!-- Modal body -->
<div class="modal-body">
{{ body }}
</div>
<!-- Modal footer -->
<div class="modal-footer rounded-bottom bg-light">
{{# each buttons }}
<button type = "{{ type }}" onclick = "{{ onclick }}" class = "{{ class }}" data-dismiss = "{{ dataDismiss }}" >{{ text }}</button>
{{/ each }}
</div>
</form>
</div>
</div>
</div>
I am not sure whether I can call multiple instances of this modal from the same view with different titles, bodies, buttons, etc. Some tweaking might be required for that, and I'm not going to worry about it until the need arises.
I want to display telephone field on create account page along with the first name and last name. Telephone field is mapped to Address Fields so cannot display telephone field separately and hiding the other address fields.
Help me to display just the telephone field and not the address fields.
Replace the following html file by above given code :- "vendor\magento\module-checkout\view\frontend\web\template\shipping-address\address-renderer\default.html
<!--
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<div class="shipping-address-item" data-bind="css: isSelected() ? 'selected-item' : 'not-selected-item'">
<!-- ko text: address().firstname --><!-- /ko --> <!-- ko text: address().lastname --><!-- /ko --><br/>
<!-- ko text: address().telephone --><!-- /ko --><br/>
<!-- ko if: (address().isEditable()) -->
<button type="button"
class="action edit-address-link"
data-bind="click: editAddress, visible: address().isEditable()">
<span data-bind="i18n: 'Edit'"></span>
</button>
<!-- /ko -->
<button type="button" data-bind="click: selectAddress" class="action action-select-shipping-item">
<span data-bind="i18n: 'Ship Here'"></span>
</button>
</div>
I have a dynamic form that's using <core-ajax> to bind data into multiple <paper-dropdown-menu>'s. My question: What is the preferred way to change the data in each dropdown based upon the previous one's selection. Right now, there is no javascript for it, only Polymer data-binding. Here is the code:
<polymer-element name="example-element" attributes="">
<template>
<link rel="stylesheet" href="example-element.css">
<core-ajax auto
url="http://example.json"
response="{{regionData}}"
handleAs="json">
</core-ajax>
<!-- global user object -->
<pvc-globals id="globals" values="{{globals}}"></pvc-globals>
<!-- page container -->
<div class="background" vertical layout>
<!-- toolbar -->
<template is="auto-binding">
<!-- Add teams dialog -->
<paper-action-dialog heading="Add A Example" backdrop autoCloseDisabled
id="addTeamDialog">
<p>Once this form is complete, you will have a new example on your account.</p>
<br>
<!-- Region Name -->
<paper-dropdown-menu label="Choose Your Region" style="width: 100%;">
<paper-dropdown class="dropdown">
<core-menu class="menu" selected="{{selection}}">
<template repeat="{{region in regionData}}">
<paper-item name="{{region.name}}">{{region.name}}</paper-item>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<br><br>
<!-- State Name depending on what region you choose -->
<paper-dropdown-menu label="Choose Your State" style="width: 100%;">
<paper-dropdown class="dropdown">
<core-menu class="menu">
<template ref="{{region.name}}" repeat="{{region, regionIndex in regionData}}">
<paper-item>{{region.states[regionIndex]}}</paper-item>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<br><br>
<!-- Club Name -->
<paper-dropdown-menu label="Choose Your Club depending on what region you choose" style="width: 100%;">
<paper-dropdown class="dropdown">
<core-menu class="menu">
<template repeat="{{region, regionIndex in regionData}}">
<template repeat="{{clubs in region.clubs}}">
<paper-item>{{clubs.name}}</paper-item>
</template>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<br><br>
<!-- Team Name -->
<paper-dropdown-menu label="Choose Your Team" style="width: 100%;">
<paper-dropdown class="dropdown">
<core-menu class="menu">
<template repeat="{{region, regionIndex in regionData}}">
<template repeat="{{clubs in region.clubs}}">
<paper-item>{{clubs.teams[regionIndex]}}</paper-item>
</template>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<!-- <paper-input-decorator label="Your Team Name" floatingLabel
error="Team Name is required!" autovalidate>
<input is="core-input" type="text" value="{{teamName}}" required>
</paper-input-decorator> -->
<paper-button dismissive on-tap="{{openInfo}}">More Info...</paper-button>
<paper-button affirmative>Cancel</paper-button>
<paper-button affirmative>Add Team</paper-button>
</paper-action-dialog>
<!-- more info dialog (At time, adding `backdrop` attr to this caused error on close) -->
<paper-dialog heading="More Info For Adding Teams" transition="core-transition-top"
id="infoDialog">
<p>If you're region or team is missing, please email us at
info#mintonette.io so we can contact the
neccessary region/authorities to request your addition to join our community!</p>
</paper-dialog>
<!-- toast -->
<paper-toast id="toast1" text="{{message}}" onclick="discardDraft(el)"></paper-toast>
</div>
</template>
<script src="example-element.js"></script>
</polymer-element>
Here is my example of dependent(cascading) drop-down's: JSBin
It is commented, but shortly this is what it does:
it assumes that there is saved state(in the DB) and at load it
initiate the drop-downs according to that state.
it catches selection changes(for storing them after).
Found what I feel to be the most 'Polymer-way' possible to this question (until a Polymer team member answers it). Once you have access to your JSON data via <core-ajax> element, then bind and loop through it in your <paper-dropdown-menu> like so:
<core-ajax auto
url="http://jsonexample.com/example.json"
response="{{yourData}}"
handleAs="json">
</core-ajax>
<!-- Region Name -->
<paper-dropdown-menu label="Your Label" style="width: 100%;">
<paper-dropdown class="dropdown">
<core-menu class="menu" selected="{{selection}}" on-core-select="{{DDSelected}}">
<template repeat="{{something in yourData}}">
<paper-item name="{{something.name}}">{{something.name}}</paper-item>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
Notice that on the <core-menu> element there is the on-core-selected attribute that binds to a function in our script, which looks like this:
regionSelected: function(e) {
var convert = [];
// save selected item
this.item = this.DDSelection;
// Loop through ajax data to find obj selected that matches selection
for (var i = 0; i <= this.yourData.length - 1; i++) {
if (this.yourData[i].name === this.item) {
this.yourObj = this.yourData[i];
// convert obj to array b/c Polymer doesn't loop -> obj
convert.push(this.yourObj);
this.convert = convert;
}
}
}
What this is basically doing is taking the selection that the user chose and looping through your ajax data to find a matching property name. Once it has, then convert that object into an array (if necessary). This is crucial because as of now, Polymer only loops through arrays - not objects. Then, store it as a variable called convert
Once you do this, in your next dropdown, loop through convert and you are golden:
...
<paper-dropdown-menu label="Choose Your Second" style="width: 100%;"
disabled?="{{!selection}}">
<paper-dropdown class="dropdown">
<core-menu class="menu">
<template repeat="{{key, index in convert}}">
<template repeat="{{prop in key.props}}">
<paper-item>{{prop}}</paper-item>
</template>
</template>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
A good UX move is to also disable the dropdown until the first has been selected. Notice the disabled?="{{selection}}" attribute on the <paper-dropdown-menu> element which is doing that...
That's it!
Does anyone know why the performance on this page is slow when it comes to the dropdown list on the - ALL - option? I must be doing something wrong with knockout.js for this to happen. For the smaller list of games it opens up quickly.
Tournament Schedule
Javascript
(function (app, $, undefined) {
app.viewModel = app.viewModel || {};
function Schedule() {
var self = this;
self.loaded = ko.observable(false);
self.divisionId = ko.observable();
self.games = ko.observableArray(null);
self.search = function(url) {
app.call({
type: 'POST',
data: { divisionId: self.divisionId() },
url: url,
success: function (result) {
self.games([]);
self.games.push.apply(self.games, result);
self.loaded(true);
}
});
};
self.init = function (options) {
app.applyBindings();
};
};
app.viewModel.schedule = new Schedule();
} (window.app = window.app || {}, jQuery));
Template
<div class="games hidden" data-bind="if: schedule.games(), css: { 'hidden': !schedule.games() }">
<div data-bind="if: schedule.games().length > 0">
<div data-bind="foreach: schedule.games">
<h2><span data-bind="html: Name"></span></h2>
<hr />
<div class="games row" data-bind="foreach: Games">
<div class="span4">
<div class="game game-box new-game-box">
<div class="datetime-header clearfix new-game-box">
<span class="time"><span data-bind="html: DateFormatted"></span> - <span data-bind="html: TimeFormatted"></span></span>,
<span class="gym" data-bind="text: Venue"></span>
</div>
<div class="team-game clearfix new-game-box" data-bind="css: { winner: AwayTeamIsWinner }">
<span class="team">
<a target="_blank" href="#" data-bind="html: AwayTeamName, attr: { href: AwayTeamLink }"></a>
</span> <span class="score" data-bind="html: AwayTeamScoreDisplay"></span>
</div>
<div class="team-game clearfix new-game-box" data-bind="css: { winner: HomeTeamIsWinner }">
<span class="team">
</span> <span class="score" data-bind="html: HomeTeamScoreDisplay"></span>
</div>
<div class="buttons clearfix">
<span class="division" data-bind="html: 'Division ' + DivisionName"></span>,
<span data-bind="text: GameTypeName"></span>
<div class="btn-group">
<a rel="nofollow, noindex" title="Add to calendar" href="#" class="btn btn-mini" data-bind="attr: { href: CalendarLink }"><i class="icon-calendar"></i></a>
<a target="_blank" title="Gym Details" href="#" class="btn btn-mini" data-bind="attr: { href: GymLink }"><i class="icon-map-marker"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="hidden" data-bind="if: (schedule.games() && schedule.games().length == 0), css: { 'hidden': !schedule.games() }">
No games found for this event.
Scores will be available here the day before the event however the schedule might already be posted under documents.
</div>
<script type="text/javascript">
app.viewModel.schedule.init({});
</script>
I downloaded your HTML and CSS and did some testing. I was able to fix the problem by removing the following CSS:
.ui-widget :active {
outline: none
}
To test this on the current page, execute document.styleSheets[0].deleteRule(23) in the console.
Some more testing showed that the drop-down is only slow in Chrome (30). Firefox (23) and IE (10) don't have the problem.
You may suffer from performance problems when manipulating large or rich (containing complex objects) observable arrays. Any time you perform any operation on such array, all the subscribers get notified.
Imagine you are inserting 100 items into an observable array. More often than not, you don’t need each subscriber to recalculate it’s dependencies 100 items, and UI to be reacting 100 times. Instead, once should just fine.
To do this, you can always modify the underlying array instead of the observableArray directly, since observableArray concept is just a function wrapper around the traditional JS array. After you are done with the array manipulation, you can then notify all the subscribers that the array has changed its state with .valueHasMutaded()
. See the simple example:
success: function (result) {
ko.utils.arrayPushAll(self.games, result);
self.games.valueHasMutated();
....
cheers
There are too many dom element at the page, it will be hard to select element for jquery.
If you need to handle big data bound after ajax, you'd better add a new thread to do it. in ajax success function:
setTimeout(function(){
// your code
}, 100);
for No.1, why not add a pager? Long long scroll bar is very terrible.
I have a list of items and I want to highlight the "selected" one (this is linked to another interface, so using pure CSS will not work). I'm guessing I could do this:
<!-- ko if: isSelected -->
<span class="selected">
<!-- ko endif -->
<span class="myItem">content goes here</span>
<!-- ko if: isSelected -->
</span>
<!-- ko endif -->
and maybe even this:
<span class="myItem<!-- ko if: isSelected --> selected<!-- ko endif -->">
content goes here
</span>
But I suspect there is a better way. I have been unable to find it.
According to the documentation here: http://knockoutjs.com/documentation/css-binding.html
<span class="myItem" data-bind="html: name, css: { selected: isSelected()"><span>
Works great!