can.Model with can.view to populate DOM dynamically or once records are loaded - promise

There is the following can.Model:
var CaseModel = can.Model.extend({
findAll: function(args){
var def = $.Deferred();
args.sobject.retrieve(args.criteria, function(err, records) {//records should be populated into view
if(err) def.reject(err);
else def.resolve(records);
});
return def;
}}, {});
How should I implement the can.Control with init method to populate can.view with deffered=CaseModel.findAll({sobject: new SObjectModel.Case()}) like here:
this.element.html(can.view('recipes', Deffered));
and how these records are looped in mustache template:
{{#each ???? }}
Thanks

Here is an example of how to do it with deferreds:
var CaseModel = can.Model.extend({
findAll: function(args){
var def = $.Deferred();
setTimeout(function () {
console.log('time');
def.resolve([{name: 'sam'}]);
}, 1000);
return def.promise();
}}, {});
var Control = can.Control({
init: function (ele, options) {
var self = this;
var pro = CaseModel.findAll({});
can.view('view', {records: pro})
.then(function(frag) {
self.element.html(frag);
});
}
});
var control = new Control('.app');
<script type="text/mustache" id="view">
VIEW
<ul>
{{#each records}}
<li>
{{name}}
</li>
{{/each}}
</ul>
</script>
And here is a demo
http://jsbin.com/kedih/1/edit
You should be able to adapt this to include your args.sobject.retrieve instead of the setTimeout in the example.

Related

Pushing array to select in knockout gives just one option

I am using MVC and a Razor View I'm trying to bound data received from a controller to a select using a knockout model, If I try to push directly the dynamic array I get only one option like this one
Only one option select:
I'm sure that I'm missing something stupid, I have already tried to return a new SelectList and using optionsText and optionsValue but didn't do the work.
I'm sure the knockout model is correct because if I write
viewModel.dliveryDates.push("option1","option2");
it works as expected
Here's my controller code that reads some data from database and send it back to the view
[HttpPost]
public JsonResult GetDeliveryDates(string code)
{
OrderHeaderPageModel instance = ObjectFactory.Create<OrderHeaderPageModel>();
instance.DeliveryDateRanges = PopulateDeliveryDateRanges(code);
return Json(instance.DeliveryDateRanges.ToArray());
}
Here's is my View code
#Html.DropDownList("deliveryranges", new SelectList(string.Empty, "Code", "Description"), "- Seleziona -", new { #data_bind = "options:dliveryDates" })
And finally my knockout model
function OrderHeaderViewModel() {
var self = this;
self.save = function () {
return true;
}
self.dliveryDates = ko.observableArray([]);
}
var viewModel = new OrderHeaderViewModel();
ko.applyBindings(viewModel, el);
$("#ordertypes").change(function () {
var postUrl = "/checkout/getdeliverydates";
$("#deliveryranges").empty();
$.post(postUrl,
{
code: $("#ordertypes").val(),
__RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
}, function (data) {
var arry = [];
var array = $.map(data, function (value, index) {
return [value];
});
$.each(data, function (i, data) {
arry.push(data.Code);
});
viewModel.dliveryDates.push(arry);
}
);
})
It looks like the code is doing some extra work mapping data that is not used in the ajax callback. Hope the following code helps.
function OrderHeaderViewModel() {
var self = this;
self.getData = function() {
//function to simulate filling the array from the server.
var data = ["Item 1", "Item 2", "Item 3", "Item 4"];
self.dliveryDates(data);
var mappedData = data.map(function(item, index) {
return {
id: index,
description: item
};
});
viewModel.mappedDliveryDates(mappedData);
}
self.save = function() {
return true;
}
//added to put the selected values in
self.selectedValue = ko.observable();
self.selectedMappedValue = ko.observable();
self.mappedDliveryDates = ko.observableArray([]);
self.dliveryDates = ko.observableArray([]);
}
var viewModel = new OrderHeaderViewModel();
ko.applyBindings(viewModel);
$("#ordertypes").change(function() {
var postUrl = "/checkout/getdeliverydates";
$("#deliveryranges").empty();
$.post(postUrl, {
code: $("#ordertypes").val(),
__RequestVerificationToken: Sana.Utils.getAntiForgeryToken()
}, function(data) {
// if the data needs to be transformed and is already an array then you can use
var mappedData = data.map(function(item, index) {
return {
id: index,
description: item
};
});
// If the data is already in the format that you need then just put it into the observable array;
viewModel.mappedDliveryDates(mappedData);
viewModel.dliveryDates(data);
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Server Data Values -
<select data-bind="options: dliveryDates, value:selectedValue, optionCaption: 'Choose...'"></select>
<br/> Mapped Values -
<select data-bind="options: mappedDliveryDates, optionsText:'description', value: selectedMappedValue, optionCaption: 'Choose...'"></select>
<br/>
<button data-bind="click: getData">Load Data</button>
<br/>
<br/>
<pre data-bind="text: ko.toJSON($root)"></pre>

Product filter in Ajax will work in Wordpress

I have this code in Ajax but I want this to work in Wordpress. I just want to make a product filter in Ajax that will work in WordPress. I am just new to this. Any help and idea please?
<script>
$(document).ready(function(){
filter_data();
function filter_data()
{
$('.filter_data').html('<div id="loading" style="" ></div>');
var action = 'fetch_data';
var minimum_price = $('#hidden_minimum_price').val();
var maximum_price = $('#hidden_maximum_price').val();
var brand = get_filter('brand');
var ram = get_filter('ram');
var storage = get_filter('storage');
$.ajax({
url:"fetch_data.php",
method:"POST",
data:{action:action, minimum_price:minimum_price, maximum_price:maximum_price, brand:brand, ram:ram, storage:storage},
success:function(data){
$('.filter_data').html(data);
}
});
}
add_action( 'wp_ajax_action', 'filter_data' );
add_action( 'wp_ajax_action', 'filter_data' );
function get_filter(class_name)
{
var filter = [];
$('.'+class_name+':checked').each(function(){
filter.push($(this).val());
});
return filter;
}
$('.common_selector').click(function(){
filter_data();
});
$('#price_range').slider({
range:true,
min:1000,
max:65000,
values:[1000, 65000],
step:500,
stop:function(event, ui)
{
$('#price_show').html(ui.values[0] + ' - ' + ui.values[1]);
$('#hidden_minimum_price').val(ui.values[0]);
$('#hidden_maximum_price').val(ui.values[1]);
filter_data();
}
});
});
</script>
How can I make an ajax call in wordpress without use of plugin?
Ajax code
<script type="text/javascript">
function filter_data(){
$('.filter_data').html('<div id="loading" style="" ></div>');
var action = 'fetch_data';
var minimum_price = $('#hidden_minimum_price').val();
var maximum_price = $('#hidden_maximum_price').val();
var brand = get_filter('brand');
var ram = get_filter('ram');
var storage = get_filter('storage');
var data = {
action:action, minimum_price:minimum_price,
maximum_price:maximum_price, brand:brand, ram:ram,
storage:storage
};
$.post('/wp-admin/admin-ajax.php',data,function(response){
$('.filter_data').html(data);
});
}
</script>
In you function.php file
add_action('wp_ajax_fetch_data','filter_data'); // for loggedin user
add_action('wp_ajax_nopriv_fetch_data', 'filter_data'); // for non loggedin user
function filter_data()
{
$minimum_price = $_POST['minimum_price'];
$maximum_price = $_POST['maximum_price'];
$brand= $_POST['brand'];
$ram= $_POST['ram'];
$storage= $_POST['storage'];
// here your product filter code
}

Call from one widget to another widget using odoo js

Js Xml Path : module_name-static-src-xml:
<div t-name="widget_1">
<button type="button" id = "button_1" >Click</button>
<div id = "test"> </div>
</div>
<div t-name="widget_2">
<p>Second Widget</p>
</div>
Js:
odoo.define("module_name.name", function(require) {
"use strict";
var Widget = require("web.Widget");
var Widget_Extend = Widget.extend({
template: "widget_1",
start: function() {
var self = this;
$(document).ready(function(){
setTimeout(function(){
$(document).on("click", "#button_1", function() {
var widget_call = '';
widget_call = '<div id ="test"></div>'
widget_call + = '<t t-call="widget_2"/>'
$('#test').html(widget_call);
});
});
});
}
});
core.action_registry.add("module_name.name", Widget_Extend);
});
Note:
I have tried to call "widget_2" using js but i could not get what i expect. I am not sure this the way to call the widget but i have tried a lot. If any one have some other way to call the 2nd widget from 1st widget using js kindly let me know.
Anticipating all kind of information about this problem.
Thanks.
you can inherited a widget to another widget As an example, it may look like this:
// in file a.js
odoo.define('module.A', function (require) {
"use strict";
var A = ...;
return A;
});
// in file b.js
odoo.define('module.B', function (require) {
"use strict";
var A = require('module.A');
var B = ...; // something that involves A
return B;
});

Knockout JS - update viewModel with AJAX Call

there are some similar questions but didn't help me to solve my issue. I can't update my results on page / view after updating my viewModel with AJAX. I am getting valid AJAX response that updates the view if I reload the page, but not when I click btnAdvancedSearch
I have simple HTML:
<div>
<input type="button" id="btnAdvancedSearch" data-bind="click: refresh" />
</div>
<div id="resultslist1" data-bind="template: { name: 'rest-template', foreach: restaurants }">
</div>
And I bind in on document load:
$(document).ready(function () {
ko.applyBindings(new RestaurantsListViewModel());
});
My viewModel is like this, and in it I call refresh that is bound with button
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests;
self.refresh = function () {
updateRestaurantsList(); //Method executes AJAX and saves result to session.
var mappedRests2 = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants= mappedRests2;
}
}
What am I missing here?
Thanks
I have tried waiting for AJAX to finish like this:
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests;
self.refresh = function () {
var latitude = sessionStorage.getItem('latitude');
var longitude = sessionStorage.getItem('longitude');
var query = '{"Accepts_Reservations":"' + $('#chkReservation').is(":checked") + '","Accepts_Cards":' + $('#chkAcceptsCards').is(":checked") + '"}';
var searchResults = getRestaurantsAdvancedSearchAJAX(query, latitude, longitude, 40);
searchResults.success(function (data) {
var information = data.d;
var mappedRests2 = $.map($.parseJSON(information), function (item) { return new Restaurant(item) });
self.restaurants = mappedRests2;
});
};
};
Edit 1
Once you have declared your observable like so:
self.restaurants = ko.observableArray([]);
When you want to update restaurants you cannot do this:
self.restaurants = mappedRests2;
Instead, you need to do this:
self.restaurants(mappedRests2);
updateRestaurantsList(); //Method executes AJAX and saves result to session.
The comment after the above line indicates that this method is making an asynchronous call. Therefore, it is likely that the line after it is executing before sessionStorage has been populated. Maybe consider having updateRestaurantsList return a promise. Then you could update your code to something like this:
updateRestaurantsList().then(function() {
var mappedRests2 = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants= mappedRests2;
});
This way the call to populate your mappedRests2 variable won't happen until after your updateRestaurantsList method has completed.
Edit 1
Be sure to never assign values to an observable using an equal sign.
// Overall viewmodel for this screen, along with initial state
function RestaurantsListViewModel() {
var self = this;
self.restaurants = ko.observableArray([]);
var mappedRests = $.map($.parseJSON(sessionStorage.getItem('searchResults')), function (item) { return new Restaurant(item) });
self.restaurants(mappedRests);
self.refresh = function () {
var latitude = sessionStorage.getItem('latitude');
var longitude = sessionStorage.getItem('longitude');
var query = '{"Accepts_Reservations":"' + $('#chkReservation').is(":checked") + '","Accepts_Cards":' + $('#chkAcceptsCards').is(":checked") + '"}';
var searchResults = getRestaurantsAdvancedSearchAJAX(query, latitude, longitude, 40);
searchResults.success(function (data) {
var information = data.d;
var mappedRests2 = $.map($.parseJSON(information), function (item) { return new Restaurant(item) });
self.restaurants(mappedRests2);
});
};
};

event click Handlebars

Using Handlebars and OpenDatabase i have small problem. When you click on a link and nothing happens
my JS.
db.transaction(function (tx) {
tx.executeSql('SELECT * FROM exhibition', [], function (tx, results) {
var source = document.querySelector("#exhibition-template").innerHTML;
var data = [];
template = Handlebars.compile(source);
for(var i=0;i < results.rows.length; i++) {
data.push(results.rows.item(i));
}
var context = (data);
if (language == 'pl')
{
var html = template({o:context,language_pl:true});
}else{
var html = template({o:context,language_en:true});
}
document.querySelector("#template").innerHTML = html;
}, null);
$(".exl").on("click", function(){
alert('event click!');
});
});
and my html
{{#each o}}
<a href='' class='exl'>{{name_pl}}</a>
{{/each}}
You're probably binding events before the generated DOM is appended to the page. So, you can either wait for Handlebars template to be rendered (In your case this mean binding event in the callback from tx.executeSql). Or you can use event delegation with jQuery:
$(document).on("click", ".exl", function(){
alert('event click!');
});

Resources