I use a RESTAdapter model to fill a Ember's select view with options.
The contentBinding is mapped the a property in the controller, where I use this.set('myProperty', model.find(someQuery)).
model.find(someQuery) with 1 result works perfect, but model.find(someQuery) with many results have a weird effect. The last object from the result is showed as many times as the length of the result.
{{view Ember.Select contentBinding="myProperty" optionValuePath="content.id"
optionLabelPath="content.name"
selectionBinding="selectedResult"
prompt=" "}}
Interesting. At first glance the code you included in your question looks fine. To debug:
1) Check to be sure the query results are what you expect.
content = model.find(someQuery); //with many results
// wait for results...
console.log(content.getEach('id')); //expect array of ids
console.log(content.getEach('name')); //expect array of names
2) examine contents of myProperty - from template:
{{#each myProperty}}
<pre>{{id}}.{{name}}</pre>
{{/each}}
Expect template to output id/name for each option.
Related
I've seen the JSON array questions here and I'm still a little lost, so could use some extra help.
Here's the setup:
My Flow calls a sproc on my DB and that sproc returns this JSON:
{
"ResultSets": {
"Table1": [
{
"OrderID": 9518338,
"BasketID": 9518338,
"RefID": 65178176,
"SiteConfigID": 237
}
]
},
"OutputParameters": {}
}
Then I use a PARSE JSON action to get what looks like the same result, but now I'm told it's parsed and I can call variables.
Issue is when I try to call just, say, SiteConfigID, I get "The output you selected is inside a collection and needs to be looped over to be accessed. This action cannot be inside a foreach."
After some research, I know what's going on here. Table1 is an Array, and I need to tell PowerAutomate to just grab the first record of that array so it knows it's working with just a record instead of a full array. Fair enough. So I spin up a "Return Values to Virtual Power Agents" action just to see my output. I know I'm supposed to use a 'first' expression or a 'get [0] from array expression here, but I can't seem to make them work. Below are what I've tried and the errors I get:
Tried:
first(body('Parse-Sproc')?['Table1/SiteConfigID'])
Got: InvalidTemplate. Unable to process template language expressions in action 'Return_value(s)_to_Power_Virtual_Agents' inputs at line '0' and column '0': 'The template language function 'first' expects its parameter be an array or a string. The provided value is of type 'Null'. Please see https://aka.ms/logicexpressions#first for usage details.'.
Also Tried:
body('Parse-Sproc')?['Table1/SiteconfigID']
which just returns a null valued variable
Finally I tried
outputs('Parse-Sproc')?['Table1']?['value'][0]?['SiteConfigID']
Which STILL gives me a null-valued variable. It's the worst.
In that last expression, I also switched the variable type in the return to pva action to a string instead of a number, no dice.
Also, changed 'outputs' in that expression for 'body' .. also no dice
Here is a screenie of the setup:
To be clear: the end result i'm looking for is for the system to just return "SiteConfigID" as a string or an int so that I can pipe that into a virtual agent.
I believe this is what you need as an expression ...
body('Parse-Sproc')?['ResultSets']['Table1'][0]?['SiteConfigID']
You can see I'm just traversing down to the object and through the array to get the value.
Naturally, I don't have your exact flow but if I use your JSON and load it up into Parse JSON step to get the schema, I am able to get the result. I do get a different schema to you though so will be interesting to see if it directly translates.
I have a table with columns and below, an icon upon clicking, I can modify the table columns.
Now I want to count the columns before and after. I have a solution which works, where I call the the following before and after and then use the wrapped alias (via parseInt) to compare:
cy.get('body').then(($el) => {
// eslint-disable-next-line #typescript-eslint/no-unsafe-assignment
const countColsNr = $el.find('th[e2e-tag-header]').length;
cy.wrap(**to be named**).as(`${s}`);
});
This counts the actual columns and saves it in the variable to be named.
However, if I use a JQuery approach, it always gets the same column number, which is at the beginning of the test:
const beforetColsNr = Cypress.$('th[e2e-tag-header]').length;
log(beforetColsNr.toString());
... column handling code
... also tried with wait inbetween steps for debug
const afterColsNr = Cypress.$('th[e2e-tag-header]').length;
log(afterColsNr.toString());
Before number and after, are the same! When I look at the state of the browser (screenshot), I can see different columns amount at time of counting in after. This JQ-approach does not count properly the second time or uses the first value.
Is this something which is expected? Or is something I have to investigate?
cypress is asyncrhonous, so beforetColsNr and aftterColsNr are initialized at the same moment.
In the "cypress" mode in your first code block it works because of the usage of the .then().
Attempting to confirm that of all the schema in the head of a page exactly 3 of them should have a specific string within them. These schemas have no tags or sub classes to differentiate themselves from each other, only the text within them. I can confirm that the text exists within any of the schema:
cy.get('head > script[type="application/ld+json"]').should('contain', '"#type":"Product"')
But what I need is to confirm that that string exists 3 times, something like this:
cy.get('head > script[type="application/ld+json"]').contains('"#type":"Product"').should('have.length', 3)
And I can't seem to find a way to get this to work since .filter, .find, .contains, etc don't filter down the way I need them to. Any suggestions? At this point it seems like I either need to import a custom library or get someone to add ids to these specific schema. Thanks!
The first thing to note is that .contains() always yields a single result, even when many element match.
It's not very explicit in the docs, but this is what it says
Yields
.contains() yields the new DOM element it found.
If you run
cy.get('head > script[type="application/ld+json"]')
.contains('"#type":"Product"')
.then(console.log) // logs an object with length: 1
and open up the object logged in devtools you'll see length: 1, but if you remove the .contains('"#type":"Product"') the log will show a higher length.
You can avoid this by using the jQuery :contains() selector
cy.get('script[type="application/ld+json"]:contains("#type\": \"Product")')
.then(console.log) // logs an object with length: 3
.should('have.length', 3);
Note the inner parts of the search string have escape chars (\) for quote marks that are part of the search string.
If you want to avoid escape chars, use a bit of javascript inside a .then() to filter
cy.get('script[type="application/ld+json"]')
.then($els => $els.filter((index, el) => el.innerText.includes('"#type": "Product"')) )
.then(console.log) // logs an object with length: 3
.should('have.length', 3);
I'm using this http://www.xpathtester.com/xpath/5a30592045b6aa5089faf909261ede0b XPath tester, which returns exactly what I want. For some reason it removes my full query, but if you use it, it works.
*/h3[contains(string(), "Description")]/following-sibling::p[1]
But in real life, I get nothing from my variable.
I'm trying to get the data after <h3>Description</h3>, in this case a paragraph <p>.
HTML
$feed_item=
<div class="outer-feed"><ul>
<li><strong>Severity:</strong> <span class="label label-info">Low</span></li>
</ul>
<h3>Description</h3>
<p>The lack of validation of configuration parameters used in SQL queries caused various SQL injection vectors.</p>
...
Here's my XPath
$description_node = $xpath->query('*/h3[contains(string(), "Description")]/following-sibling::p[1]', $feed_item);
$description = "description: " . $description_node->item(0)->textContent;
and var_dump
object(DOMNodeList)#1654 (1) { ["length"]=> int(0) }
And the error
Notice
: Trying to get property 'textContent' of non-object in
What confuses me is that I can get Severity from the same HTML by using this:
$severity_node = $xpath->query('*/li[contains(string(), "Severity:")]', $feed_item);
$severity = preg_replace('/Severity:\W*/u', '', $severity_node->item(0)->textContent);
My first thought was to scale back to just the H3 and output that.
$description_node = $xpath->query('*/h3[contains(string(), "Description")]', $feed_item);
object(DOMNodeList)#1654 (1) { ["length"]=> int(0) } // doesn't contain anything
Given that the following are identical but the first works and the second doesn't, what could be the problem?
$severity_node = $xpath->query('*/li[contains(string(), "Severity:")]', $feed_item);
$description_node = $xpath->query('*/h3[contains(string(), "Description")]', $feed_item);
Why is one working and not the other. And what is the best way to troubleshoot things like this. It seems to work on the xpathtester. What could I be doing wrong that causes this problem in PHP?
Try with this XPath:
//h3[text()="Description"]/following::p[1]
A query starting */h3[...] will only work if the context item when it is invoked is the grandparent of the h3 element. You've given no information about the context item, so I suspect it is something different.
You ask the question(s): "Why is one working and not the other. And what is the best way to troubleshoot things like this. It seems to work on the xpathtester. What could I be doing wrong that causes this problem in PHP?"
Well, the first thing is to understand that XPath expressions may depend on the context item, and that the same expression evaluated with different context items is going to give different results. Once you understand that concept, it hopefully becomes a lot clearer.
I want to make 3 dependents drop down list, each drop down dependent to the previous drop down, so when I select an item from first drop down , all data fetch from database and add to second drop down as item.
I know how to do this in a normal php page using ajax, but as opencart uses MVC I don't know how can I get the selected value
Basically, you need two things:
(1) Handling list changes
Add an event handler to each list that gets its selected value when it changes (the part that you already know), detailed tutorial here in case someone needed it
Just a suggestion (for code optimization), instead of associating a separate JS function to each list and repeating the code, you can write the function once, pass it the ID of the changing list along with the ID of the depending list and use it anywhere.
Your HTML should look like
<select id="list1" onchange="populateList('list1', 'list2')">
...
</select>
<select id="list2" onchange="populateList('list2', 'list3')">
...
</select>
<select id="list3">
...
</select>
and your JS
function populateList(listID, depListID)
{
// get the value of the changed list thorugh fetching the elment with ID "listID"
var listValue = ...
// get the values to be set in the depending list through AJAX
var depListValues = ...
// populate the depending list (element with ID "depListID")
}
(2) Populating the depending list
Send the value through AJAX to the appropriate PHP function and get the values back to update the depending list (the part you are asking for), AJAX detailed tutorial here
open cart uses the front controller design patter for routing, the URL always looks like: bla bla bla.bla/index.php?route=x/y/z&other parameters, x = folder name that contains a set of class files, y = file name that contains a specific class, z = the function to be called in that class (if omitted, index() will be called)
So the answer for your question is:
(Step 1) Use the following URL in your AJAX request:
index.php?route=common/home/populateList
(Step 2) Open the file <OC_ROOT>/catalog/controller/common/home.php , you will find class ControllerCommonHome, add a new function with the name populateList and add your logic there
(Step 3) To use the database object, I answered that previously here
Note: if you are at the admin side, there is a security token that MUST be present in all links along with the route, use that URL:
index.php?route=common/home/populateList&token=<?php echo $this->session->data['token'] ?> and manipulate the file at the admin folder not the catalog
P.S: Whenever the user changes the selected value in list # i, you should update options in list # i + 1 and reset all the following lists list # i + 2, list # i + 3 ..., so in your case you should always reset the third list when the first list value is changed
P.P.S: A very good guide for OC 1.5.x => here (It can also be used as a reference for OC 2.x with some modifications)