Managing resources in lookup filter - javascript

My client wants to be able to filter jobplans with the selected asset. To be able to do that, I have developped a function that filters the results based on the custom resource jpassetsplink:
filterJobPlansForLookup: function(eventContext){
var workOrderSet = CommonHandler._getAdditionalResource(eventContext,"workOrder");
var jobPlanSet = CommonHandler._getAdditionalResource(eventContext,"jobPlanResource");
jobPlanSet._lookupFilter = null;
var assetSet = null;
var assetnum = null;
var itemnum = null;
var jpAssetSpLinkSet = null;
//CommonHandler._clearFilterForResource(this,jobPlanSet);
var siteid = workOrderSet.getCurrentRecord().get("siteid");
if(siteid == null){
siteid = UserManager.getInfo("defsite");
}
if(workOrderSet.getCurrentRecord() != null){
assetnum = workOrderSet.getCurrentRecord().get("asset");
assetSet = CommonHandler._getAdditionalResource(eventContext,"additionalasset");
CommonHandler._clearFilterForResource(eventContext, assetSet);
assetSet = assetSet.clearFilterAndSort().filter('siteid == $1', siteid)
if (assetnum != null){
var asset = assetSet.find('assetnum == $1', assetnum);
if (asset && asset.length>0){
itemnum = asset[0].get('itemnum');
}
}
}
var filter = [{siteid: siteid, status: "ACTIF"}];
if (assetnum != null){
jpAssetSpLinkSet = CommonHandler._getAdditionalResource(eventContext,"jpassetsplinkResource");
jpAssetSpLinkSet._lookupFilter = null;
CommonHandler._clearFilterForResource(eventContext, jpAssetSpLinkSet);
var filteredJpAssets = null;
if (itemnum == null){
filteredJpAssets = jpAssetSpLinkSet.clearFilterAndSort().filter('assetnum == $1', assetnum);
} else {
filteredJpAssets = jpAssetSpLinkSet.clearFilterAndSort().filter('itemnum == $1', itemnum);
}
Logger.trace("[WODetailExtensionHandler] Found " + filteredJpAssets.data.length + " links out of " + jpAssetSpLinkSet.count() );
if(filteredJpAssets && filteredJpAssets.data.length>0){
filter = [];
filteredJpAssets.data.forEach(function(jpAsset){
filter.push({jpnum: jpAsset.get("jpnum"), siteid: siteid, status: "ACTIF"});
});
}
}
jobPlanSet.lookupFilter = filter;
}
With the right circumstances this code works. There are multiple problems with it though:
1- When searching for an asset, the resulting filter is applied to the resource and cannot seem to be removed. If I search an asset in the asset lookup, when the execution gets to this function, the resource data is still filtered and calls to CommonHandler._clearFilterForResource, assetSet.clearFilterAndSort() or directly changing the _lookupFilter property does not work. This sometimes results in the impossibility to find the selected asset in the asset resource, thus the filtering ends up failing.
2- Not searching and directly inputing the desired asset leads to another problem. Since there is no filter on the resource, only the number of entries specified by pageSize is loaded. In my case, pageSize is set at 2000 for the asset resource. That means if the selected asset is not in the 2000 first entries, it is not found by the function, thus the filtering fails.
3- If the filter manages to work, it seems to block further filtering by jpnum or description in the jobplan lookup.
To conclude, here's my question: Is there a way to manage resources so that these problems do not occur ? Any tip is appreciated.
Thanks,

Related

Grabbing GeoJSON data in Openlayers

What I'm trying to do:
Figure out how to reference/grab geoJSON data from a server.
In this case I'm just using an example on the openLayers doc.
Ideally I'd just be able to print out a features ID/type, but I cannot get it to work.
What's happening:
var selectElement = document.getElementById('type');
var source = vector.getSource();
var feature = source.getFeatures()[0];
var changeInteraction = function() {
if (select !== null) {
map.removeInteraction(select);
}
var value = selectElement.value;
if (value == 'singleclick') {
select = selectSingleClick;
} else if (value == 'click') {
select = selectClick;
} else if (value == 'pointermove') {
select = selectPointerMove;
} else if (value == 'altclick') {
select = selectAltClick;
} else {
select = null;
}
if (select !== null) {
map.addInteraction(select);
select.on('select', function(e) {
document.getElementById('status').innerHTML = feature.getGeometry().getType();
});
console.log(feature);
}
};
I was hoping my innerHTML would display "Polygon" in this case, but no such luck. I've tried various combinations, and been looking over the documentation, can't see what I'm doing wrong.
The server I'm trying to grab the info from is,
https://openlayers.org/en/v4.6.4/examples/data/geojson/countries.geojson
Any help would be appreciated.
(I can attach full code if helpful)
I was able to replicate your program and find the solution for retrieving the Country's name for a selected feature, as mentioned in your comments.
First, remove the following lines. You don't want the first feature of the source file but the first selected feature instead.
var source = vector.getSource();
var feature = source.getFeatures()[0];
Second, define the feature inside the callback function(e) for the select Event. Also, since getFeatures() will return a Collection of features the getArray() method is necessary.
The get(key) method will return a value for a determined key, "name" in this case.
if (select !== null) {
map.addInteraction(select);
select.on('select', function(e) {
var feature = e.target.getFeatures().getArray()[0];
document.getElementById('status').innerHTML = ' ' +
feature.get("name") + ' ' + feature.getId();
});
}

Meteor subscribe callback running when subscription contains previous subscribe result

I am fairly new to meteor, and I am running into a strange issue with subscribe callbacks. I have a database containing courses and reviews. I'm using a publish/subscribe model on the reviews to return reviews that are only relevant to a selected class, and I want this to change each time a new class is clicked on. I want to print all the reviews and compile some metrics about the reviews (average quality, difficulty rating). Using the following code, with a subscribe that updates the reviews sent to the client, the printed reviews (which are grabbed from a helper) return correctly, but the metrics (which are grabbed on an onReady callback to the helper) are inaccurate. When the onReady function is run, the current result of the local reviews collection contains the union of the clicked class and the previously clicked class, even though the reviews themselves print correctly.
I've also tried using autoTracker, but I got the same results. Is there a way to clear previous subscribe results before updating them?
publish:
Meteor.publish('reviews', function validReviews(courseId, visiblity) {
console.log(courseId);
console.log(visiblity);
var ret = null
//show valid reviews for this course
if (courseId != undefined && courseId != "" && visiblity == 1) {
console.log("checked reviews for a class");
ret = Reviews.find({class : courseId, visible : 1}, {limit: 700});
} else if (courseId != undefined && courseId != "" && visiblity == 0) { //invalidated reviews for a class
console.log("unchecked reviews for a class");
ret = Reviews.find({class : courseId, visible : 0},
{limit: 700});
} else if (visiblity == 0) { //all invalidated reviews
console.log("all unchecked reviews");
ret = Reviews.find({visible : 0}, {limit: 700});
} else { //no reviews
console.log("no reviews");
//will always be empty because visible is 0 or 1. allows meteor to still send the ready
//flag when a new publication is sent
ret = Reviews.find({visible : 10});
}
//console.log(ret.fetch())
return ret
});
subscribe:
this.helpers({
reviews() {
return Reviews.find({});
}
});
and subscribe call, in constructor with the helpers:
constructor($scope) {
$scope.viewModel(this);
//when a new class is selected, update the reviews that are returned by the database and update the gauges
this.subscribe('reviews', () => [(this.getReactively('selectedClass'))._id, 1], {
//callback function, should only run once the reveiws collection updates, BUT ISNT
//seems to be combining the previously clicked class's reviews into the collection
onReady: function() {
console.log("class is: ", this.selectedClass);
if (this.isClassSelected == true) { //will later need to check that the side window is open
//create initial variables
var countGrade = 0;
var countDiff = 0;
var countQual = 0;
var count = 0;
//table to translate grades from numerical value
var gradeTranslation = ["C-", "C", "C+", "B-", "B", "B-", "A-", "A", "A+"];
//get all current reviews, which will now have only this class's reviews because of the subscribe.
var allReviews = Reviews.find({});
console.log(allReviews.fetch());
console.log("len is " + allReviews.fetch().length)
if (allReviews.fetch().length != 0) {
console.log("exist")
allReviews.forEach(function(review) {
count++;
countGrade = countGrade + Number(review["grade"]);
countDiff = countDiff + review["difficulty"];
countQual = countQual + review["quality"];
});
this.qual = (countQual/count).toFixed(1);
this.diff = (countDiff/count).toFixed(1);
this.grade = gradeTranslation[Math.floor(countGrade/count) - 1];
} else {
console.log("first else");
this.qual = 0;
this.diff = 0;
this.grade = "-";
}
} else {
console.log("second else");
this.qual = 0;
this.diff = 0;
this.grade = "-";
}
}
})
When using pub-sub the minimongo database on the client will contain the union of subscriptions unless they are explicitly cleared. For that reason you want to repeat the query that's in the publication on the client side so that you filter and sort the same way. Minimongo is very fast on the client and you typically have much less data there so don't worry about performance.
In your constructor you have:
var allReviews = Reviews.find({});
instead use:
var allReviews = Reviews.find(
{
class : (this.getReactively('selectedClass'))._id,
visible : 1
},
{limit: 700}
);
Another side tip: javascript is quite clever about truthy and falsy values.
if (courseId != undefined && courseId != "" && visibility == 1)
can be simplified to:
if (courseId && visibility)
assuming you're using visibility == 1 to denote true and visibility == 0 to denote false

PS 1.6 Module trigger event on Ajax Add to cart

I've been studying how PrestaShop modules work lately and it would be great if someone could point me in the right direction.
I want to trigger an event (javascript method to be exact, let's call it "MyShop") that would get all the product details in it.
I have some older code but either it was working with older version of PS either it's only for normal add to cart and not ajax.
The issue is that it doesn't even get triggered at all after pressing "add to cart" button, it's actually a piece of code from Google Analytics module.
Here is a piece of modified code from my module's main .php file that is supposed to get triggered on the add to cart, it's using "hookActionCartSave()" but I am not sure if that is still available in 1.6 or got replaced by something else or whether it just simply doesn't work with ajax and I should look elsewhere?
/**
* hook save cart event to implement addtocart and remove from cart functionality
*/
public function hookActionCartSave()
{
if (!isset($this->context->cart))
return;
$cart = array(
'controller' => Tools::getValue('controller'),
'addAction' => Tools::getValue('add') ? 'add' : '',
'removeAction' => Tools::getValue('delete') ? 'delete' : '',
'extraAction' => Tools::getValue('op'),
'qty' => (int)Tools::getValue('qty', 1)
);
$cart_products = $this->context->cart->getProducts();
if (isset($cart_products) && count($cart_products))
foreach ($cart_products as $cart_product)
if ($cart_product['id_product'] == Tools::getValue('id_product'))
$add_product = $cart_product;
if ($cart['removeAction'] == 'delete')
{
$add_product_object = new Product((int)Tools::getValue('id_product'), true, (int)Configuration::get('PS_LANG_DEFAULT'));
if (Validate::isLoadedObject($add_product_object))
{
$add_product['name'] = $add_product_object->name;
$add_product['manufacturer_name'] = $add_product_object->manufacturer_name;
$add_product['category'] = $add_product_object->category;
$add_product['reference'] = $add_product_object->reference;
$add_product['link_rewrite'] = $add_product_object->link_rewrite;
$add_product['link'] = $add_product_object->link_rewrite;
$add_product['price'] = $add_product_object->price;
$add_product['ean13'] = $add_product_object->ean13;
$add_product['id_product'] = Tools::getValue('id_product');
$add_product['id_category_default'] = $add_product_object->id_category_default;
$add_product['out_of_stock'] = $add_product_object->out_of_stock;
$add_product = Product::getProductProperties((int)Configuration::get('PS_LANG_DEFAULT'), $add_product);
}
}
if (isset($add_product) && !in_array((int)Tools::getValue('id_product'), self::$products))
{
self::$products[] = (int)Tools::getValue('id_product');
$ga_products = $this->wrapProduct($add_product, $cart, 0, true);
if (array_key_exists('id_product_attribute', $ga_products) && $ga_products['id_product_attribute'] != '' && $ga_products['id_product_attribute'] != 0)
$id_product = $ga_products['id_product_attribute'];
else
$id_product = Tools::getValue('id_product');
if (isset($this->context->cookie->MyShop_cart))
$MyShop_cart = unserialize($this->context->cookie->MyShop_cart);
else
$MyShop_cart = array();
if ($cart['removeAction'] == 'delete')
$ga_products['quantity'] = -1;
elseif ($cart['extraAction'] == 'down')
{
if (array_key_exists($id_product, $MyShop_cart))
$ga_products['quantity'] = $MyShop_cart[$id_product]['quantity'] - $cart['qty'];
else
$ga_products['quantity'] = $cart['qty'] * -1;
}
elseif (Tools::getValue('step') <= 0) // Sometimes cartsave is called in checkout
{
if (array_key_exists($id_product, $MyShop_cart))
$ga_products['quantity'] = $MyShop_cart[$id_product]['quantity'] + $cart['qty'];
}
$MyShop_cart[$id_product] = $ga_products;
$this->context->cookie->MyShop_cart = serialize($MyShop_cart);
$this->didCartChange = 1;
}
}
I know that regarding what I am looking for a lot is handled in blockcart/ajax-cart.js and controllers/front/CartController.php but I am not precisely sure where or what to look for and this way I was hoping someone will lead me to a proper direction or save me some time on searching.
I also assume that regarding the javascript potion I have to modify the template itself, this is a piece of code from my module's header.tpl:
var MyShop_blockcart_log = function(json) {
var products = json.products;
var product;
var ps = [];
var p;
for(var i=0; i<products.length; i++) {
product = products[i];
p = {};
p.id = product.id;
p.link = product.link;
p.imageUrl = product.id_image.replace("medium_default", "large_default");
p.quantity = product.quantity;
p.name = product.name;
p.price = "{$currency->sign} " + (parseInt(product.price_float) / product.quantity).toFixed(2);
ps.push(p);
}
MyShop("log", "product", "cart", ps);
}
That code does not cause any errors, it just doesn't get triggered after adding products to the cart and it should.
Do I have to bind it to the add to cart button via my header.tpl file for example? And if yes then how could I do it?
Thanks in advance,
Cheers.
The hookActionCartSave only executes on cart->add() and cart->update(). But when CartController updates/adds a product, if the cart already exists for that customer, it won't executes neither one of them.
I would recommend using the actionBeforeCartUpdateQty:
Hook::exec('actionBeforeCartUpdateQty', array(
'cart' => $this,
'product' => $product,
'id_product_attribute' => $id_product_attribute,
'id_customization' => $id_customization,
'quantity' => $quantity,
'operator' => $operator,
'id_address_delivery' => $id_address_delivery,
'shop' => $shop,
'auto_add_cart_rule' => $auto_add_cart_rule,
));
It might suit your needs. Or look for another hook. In the last case scenario, you could override the CartController->postProcess and check if it's trying to add/remove products. I used this override to let the user add the maximum amount available to cart. Example, if stock is 5, and user tries to add 6, it will add 5 and not send an error. It also checks for product used in packs. If same product is in 2 pack, user can't order max stock of both packs.

remember search query to use in function

I'm using Angularjs with the dirPagination plugin to connect with an Web API. This seems to work fine. I added a search function, to do a server side search:
$scope.searchChanged = function () {
if ($scope.searchFor.length == 0) {
$scope.calculatedValue = 'e';
} else {
vm.getData(vm.pageno, vm.getSearch($scope.searchFor));
}
}
vm.getSearch = function (query) {
if (query == undefined) {
query = 'tech';
} else {
query = query;
}
return query;
}
See Plnkr for the full code
If I start searching (e.g. sales) the API returns results and the paging is correct, the get request is:
/api/students/?category=sales&begin=1&pageSize=10
But if you want to go to another page number, the get request to the server is:
/api/students/?category=tech&begin=2&pageSize=10
How can the view remember the query 'sales', so that the paging and results are correct?
You are making a common mistake here: You don't need to pass in variable from the view if you are already using a scope variable.
Changing to this would be much less error prone
// change this to var getSearch or function getSearch if you don't need it on the view anymore
vm.getSearch = function () {
var query = vm.searchFor;
// you should only use vm, change ng-model to data.searchFor
if (query == undefined) {
query = 'tech';
}
return query;
}
vm.getData = function () {
vm.users = [];
$http.get("/api/students/?category=" + vm.getSearch() + "&begin=" + vm.pageno + "&pageSize=" + vm.itemsPerPage).success(function (response) {
vm.users = response.data;
vm.total_count = response.total_count;
});
};
Your request id good, you need to optimize the sql query so you can get the right results. it should look something like this:
#begin INT = 0,
#pageSize INT = 10
SELECT *
FROM [TableName]
ORDER BY id
OFFSET (#pageSize * #begin )
ROWS FETCH NEXT #pageSize ROWS ONLY;

The collection has not been initialized - Sharepoint Javascript

I'm getting the following error when attempting to get an enumerator for a collection of lists: "Uncaught Error: The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."
It happens on the line var listEnumerator = lists.getEnumerator(); it seems to me that there is an issue in my attempt to load lists into the client object with context.load(lists);
Here's the portion of my code that's causing the problem. I've marked the place just before the error is thrown.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Begin checking for list_________________________
function checkForList(listToFind, typeOfListToCreateIfTheListIsMissing)
{
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostcontext = new SP.AppContextSite(context, hostUrl);
var hostweb = hostcontext.get_web();
var lists = hostweb.get_lists();
context.load(lists);
context.executeQueryAsync(checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
}
//Failed to get lists for some reason
function onQueryFailed(sender, args) {
alert('We failed to retrieve lists. \n' + args.get_message() + '\n' + args.get_stackTrace());
}
//____________________________Does list exist?____________________________
function checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfList)
{
var listExists = false;
//!!!!!!!!!!!!!!! ERROR HERE !!!!!!!!!!!!!!!!
var listEnumerator = lists.getEnumerator();
var title;
while (listEnumerator.moveNext())
{
title = listEnumerator.get_current().get_title();
if (title == listToFind)
{
listExists = true;
}
}
if (!listExists)
{
alert("It appears that a required list does not already exist. \nClick ok, and we'll automatically create one for you.");
//Create a new list
createList(listToFind, hostweb, typeOfList);
}
else if (listExists)
{
//Do nothing.
}
}
//____________________________If it doesn't, create one on the local site____________________________
function createList(nameOfNewList, hostweb, typeOfList) {
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(nameOfNewList);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
context.load(newList);
context.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert('List created successfully!');
}
function onListCreationFail(sender, args) {
alert('Failed to create the list. ' + args.get_message());
}
I've looked at this question sharepoint javascript collection not initialized error which seems to be fairly similar to mine, but I'm having trouble implementing the solution provided there, making me think my error may be have a different cause.
I've also tried querying for the lists inside of the function that is throwing the error, but that doesn't seem to solve anything.
For a little background, these functions are attempting to read all lists from the app's host site, check to see if a specified list exists, and create a list if no matching list exists. If there's a better way of doing that than what I'm attempting, I'd be open to that too.
Any pointers?
Some things I've tried that don't seem to work:
Changing the Asynchronous query
context.executeQueryAsync(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
to a Synchronous one.
context.executeQuery(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
I've figured out an alternate, and shorter way to method of achieving the same goal I was trying to achieve before.
Instead of checking to see if a list does not already exist, I just try to create a list, and the Query fails to create a list if one is already there. (That's good because I don't want to overwrite the list if it is already there.)
I'm not totally sure if there are any undesired side effects of what I'm doing here, but in my tests it produced the desired behavior.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Create a list if one does not already exist_________________________
function createList(listToCreate, typeOfList)
{
// Create an announcement SharePoint list with the name that the user specifies.
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(listToCreate);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
currentContext.load(newList);
currentContext.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert("We've created a list since one doesn't exist yet." );
}
function onListCreationFail(sender, args) {
alert("We didn't create the list. Here's why: " + args.get_message());
}

Categories

Resources