Problem with Sys.UI.DataView and javascript - javascript

I'm using Microsoft Ajax to dynamically populate a list of contacts, given a json packet. My code is as follows:
function fillContactsFromData(contacts) {
// this is just for debug to let me know that the data is correct
if (contacts.length > 0) {
alert('ID: ' + contacts[0].ID + ', Name: ' + contacts[0].Name);
}
$create(Sys.UI.DataView, { data: contacts }, null, null, $get('contacts'));
}
The associated html is as follows:
<div id="contacts" class="sys-template">
<a onclick="removeContact('{{ ID }}');"><img src="remove.png" /></a>
<a class="contact" rel="/Contacts/Index/{{ ID }}">{{ Name }}</a><br />
</div>
The first <a> tag is used to fire a script to remove the contact, while the second uses the jQuery cluetip to bring up a box on hover (details skipped here).
The problem I am having is that the HTML is not being rendered correctly. What is being generated is:
<div id="contacts">
<a><img src="remove.png" /></a>
<a class="contact" rel="/Contacts/Index/{{ ID }}">Darren Oster</a><br />
</div>
The alert box indicates data with valid ID (a Guid) and Name ("Darren Oster"). The Name is being rendered correctly, but the ID field is not rendered in the 'rel' attribute, and the 'onclick' statement is removed entirely.
Is this a limitation of MS Ajax or am I doing something incorrectly?
Thanks in advance.

If an attribute is to contain any {{ }} expressions it must be the entire value.
Yes:
foo="{{ 'abc' + ID }}"
No:
foo="abc{{ ID }}"
As for onclick not being generated, it probably is, but whatever inspection you are using doesn't show you the value, since it is set with a direct set of element.onclick, not addAttribute().

InfinitiesLoop put me on the right track, so here's the final answer (in case anyone else follows this path)...
First, I was using an earlier preview of MS Ajax 4.0. I've now updated to preview 5, and things work slightly differently. Here's the final HTML (the javascript is unchanged):
<div id="contacts" class="sys-template">
<a sys:onclick="{{ 'removeContact(\'' + ID + '\');' }}"><img src="remove.png" /></a>
<a class="contact" sys:rel="{{ '/Contacts/Index/' + ID }}">{{ Name }}</a><br />
</div>
Also note that preview 5 requires the 'sys:' prefix on any attributes that have data-bound values, so
<option value="{{ Value }}">{{ Text }}</option>
inside a <select> becomes
<option sys:value="{{ Value }}">{{ Text }}</option>

Related

How to get values of input which has the same class as another?

I have a page which renders divs as per the amount of results found in the search feature I have, and then In those divs I have hidden inputs with some information about the found user. One of the hidden inputs is the ID, What I want to happen is when the user clicks on the name label in any one of the divs, I want to save that hidden ID input field value to a variable using jQuery, so that I can use it as to determine what page to go to next by adding it in as a param in a url. I am using jQuery, Twig and PHP.
$(document).ready(function (){
$('.search-result-name').click(function (){
var lawyerId = $('.search-result-id').val();
window.location.href = "/profile?lawyerId="+lawyerId;
})
})
<div class="search-result-info">
<input type="hidden" value="{{ item.id }}" class="search-result-id">
<input type="hidden" value="{{ item.verifiedLicense }}" class="search-result-id" id="verified-license">
<div class="search-result-name">{{ item.firstName }} {{ item.lastName }}</div>
</div>
You can simplify the approach by outputting the value you need to retrieve in the metadata of the div element. Then you can retrieve that value from the element reference in the event that's raised, something like this:
jQuery($ => {
$('.search-result-name').on('click', e => {
const $element = $(e.target);
var lawyerId = $element.data('lawyerid');
console.log(`/profile?lawyerId=${lawyerId}`);
// window.location.assign(`/profile?lawyerId=${lawyerId}`); // commented for this demo only, uncomment in production
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="search-result-info">
<div class="search-result-name" data-lawyerid="{{ item.id }}">{{ item.firstName }} {{ item.lastName }}</div>
</div>
That being said, having a div be clickable and having it redirect to another page is not good practice. I would suggest you convert the HTML to use an <a /> element, and output the data directly in to the href attribute. That way you don't need any JS at all:
<div class="search-result-info">
{{ item.firstName }} {{ item.lastName }}
</div>

Jquery click function not working for all the ids

I have created a page which contains all the products from the database, and this is dynamic page. Columns are created in loop. Whenever user clicks the product i am fetching the product id which is unique, however this is working only for one product, for next product even if click the function is not triggered. below is the code for reference.
{{ album.product_name }}
Category : {{ album.product_category }}
Price : {{ album.product_price }}
$(document).ready(function() {
$('#k').click(function(){
var a = $('#custId').val();
alert(a)
console.log(a)
});
This is working perfectly fine for the first product only i.e the first product from the loop. for rest the click function is not working. Please help!
Below is the HTML code:
<div class="card-body">
<a href="#" id="k">
<img src="qph.fs.quoracdn.net/…" class="card-img-top" alt="..." height="90px" width="85px" id="k">
</a>
<h5 class="card-title">{{ album.product_name }}</h5>
<p class="card-text">Category : {{ album.product_category }}</p>
<p class="card-text">Price : {{ album.product_price }}</p>
<input type="hidden" id="custId" value={{ album.id }}>
</div>
You cannot have multiple elements with the same ID. Consider adding a common class name to all the target elements, say myClass, then you can bind the desired event using:
$('.myClass').click(function(){
//your code here...
});
Explanation: The browser assumes you only have one element with an id of k (because is should be like so by convention), therefore $("#k") will only target the first element with that ID.

How to include liquid inside javascript?

I've been trying all day long to include this liquid code inside javascript with no luck so far..
I'm simply trying to update a div with the cart data to show the image and name, this is what I've got.
$('.openCart').click(function(e)
{
$.ajax({
type: 'POST',
url: '/cart/add.js',
data: data,
dataType: 'json',
success: function()
{
{% capture content %}{% include 'addItemToCartDetails' %}{% endcapture %}
var content = {{ content | json }};
$("._second").html(content);
}
});
});
Overall the following code doesn't work(simply because of the for loop, and I have no clue how to get around this..): If I remove the for loop then the code retrieves the divs and everything, besides the item data since the loop isn't working.
This is the addItemToCartDetails.liquid,
{% for item in cart.items %}
<div class="_second_1">
<a href="{{ item.url | widivin: collections.all }}" class="cart-image">
<img width="320" src="{{ item | img_url: '700x700' }}" alt="{{ item.title | escape }}">
</a>
</div>
<div class="_second_2"><h3>Product Name</h3>{{ item.product.title }}</div>
<div class="_second_3"><h3>Remove Product</h3><span class="clearCart">Remove</span></div>
<div class="_second_4"><h3>Quantity</h3><input class="quantity" type="input" name="updates[]" id="updates_{{ item.id }}" value="{{ item.quantity }}" min="0" data-id="{{ item.id }}" title="If you'd like another subscription please checkout separately" alt="If you'd like another subscription please checkout separately" disabled></div>
<div class="_second_5">
<h3>Total Price</h3>
<div class="totalDetails">Total: {{ item.line_price | plus: 0 | money }} / month</div>
</div>
<div class="_third">
<input type="submit" name="checkout" value="PROCEED TO CHECKOUT">
</div>
{% endfor %}
You are trying to use Liquid when you should be using Javascript
All of the Liquid processing happens on the backend to construct an HTML file that gets passed to the browser. Once the HTML page has been passed to the user's browser, Javascript can be used to manipulate the document and make this appear/disappear/change.
Practically, this means that your {% for item in cart.items %} in addItemToCartDetails.liquid will be rendered once, before page load, and never again afterwards. No matter how many items are added to the cart, the results of that snippet will only ever be current as of page load.
You should be using the Javascript variables instead
Shopify passes the line item object representing the most recently-added product to your success callback function. Your code should look something like this:
$('.openCart').click(function(e)
{
$.ajax({
type: 'POST',
url: '/cart/add.js',
data: data,
dataType: 'json',
success: function(line_item)
{
var content = '<h3>You added a ' + line_item.title + '!</h3>' +
'<p> We appreciate that you want to give us ' + Shopify.formatMoney(line_item.line_price, {{ shop.money_format }}) + '!</p>' +
'<p>That is very nice of you! Cheers!</p>';
// Note: Not every theme has a Shopify.formatMoney function. If you are inside an asset file, you won't have access to {{ shop.money_format }} - you'll have to save that to a global javascript variable in your theme.liquid and then reference that javascript variable in its place instead.
$("._second").html(content);
}
});
});
If you're curious about what all you can access in the response object, add a console.log(line_item) to the beginning of your success function to print the object to your browser's console. (In most browsers you can right-click any element on the page and select 'Inspect Element' to bring up your developer tools. There should be a tab called 'Console' where the logs get printed to, and once your information is there you should be able to click on the object to expand its contents.)
In your first snippet, pass cart.items as the variable items to the template:
{% include 'addItemToCartDetails', items: cart.items %}
In the file addItemToCartDetails.liquid template, modify the for loop statement accordingly:
{% for item in items %}
In your case, you can put this {% capture content %}{% include 'addItemToCartDetails' %}{% endcapture %} in your liquid code somewhere in your HTML probably hidden and append it to the element where you want it to appear like so.
success: function()
{
var content = $("#addItemToCartDetails-wrapper").html();
$("._second").html(content);
}
Something like that. Hope that helps!

Angular - Use $index to load product images

I have the following form in my AngularJS app:
<li ng-repeat="device in devices track by $index">
<div class="db-handset-image">
<span class="phone-silhouette"></span>
{{ relative image here }}
</div>
<div class="db-device">
<ul class="opts">
<li>
<select name="manufacturer[ [[$index]] ]" ng-model="selectedManufacturer" ng-change="getManufacturerModels(selectedManufacturer)">
<option value="">Manufacturer</option>
<option ng-repeat="manufacturer in manufacturers" value="[[manufacturer.id]]">[[manufacturer.name]]</option>
</select>
</li>
<li>
<select name="device[ [[$index]] ]" ng-model="selectedModel" ng-change="loadModelImage(selectedModel, $index)">
<option value="">Model</option>
<option ng-repeat="model in manufacturerModels" value="[[model.id]]">[[model.model + ' ' + model.variants[$index].memory + ' ' + model.variants[$index].colour]]</option>
</select>
</li>
</ul>
</div>
</li>
What happens in this form is that a user will select a manufacturer from the first dropdown and a model from the model dropdown. The model dropdown will populate with the relative models after a manufacturer has been selected using Angular's $filter.
When the user has selected a model, loadModelImage is fired and what needs to happen here is that after a model selection, that model image is then loaded into the {{relative image here}} placeholder. This is currently being done like so:
$scope.loadModelImage = function (modelId, $index) {
$http.get(ajaxurl + '?action=get_handset&hid=' + modelId)
.success(function (data) {
$scope.selectedHandsets++;
$scope.modelImages.splice(0, 0, data.handset.images);
})
}
This issue with this is that if I replace the relative image here placeholder text with an <img> loading in the model images, each model that's been selected appears in every row.
My other issue is that if you remain on the same 'row' (see below screenshot) and change the handset image, another array of images is pushed to $scope.modelImages when it in fact the images for that 'row' should effectively be overwritten with the new selection.
To give you a clear understanding of how the form looks, here's a screenshot:
When you click 'add new handset' the row containing the dropdowns is visually duplicated and you can add select another handset.
I hope my problem's explained clearly enough, any Q's ask.

Getting value from input control using jQuery

I am using the teleriks treeview control (asp.net mvc extensions), where I may have up to three children nodes, like so (drumroll...... awesome diagram below):
it has its own formatting, looking a bit like this:
<%=
Html.Telerik().TreeView()
.Name("TreeView")
.BindTo(Model, mappings =>
{
mappings.For<Node1>(binding => binding
.ItemDataBound((item, Node1) =>
{
item.Text = Node1.Property1;
item.Value = Node1.ID.ToString();
})
.Children(Node1 => Node1.AssocProperty));
mappings.For<Node2>(binding => binding
.ItemDataBound((item, Node2) =>
{
item.Text = Node2.Property1;
item.Value = Node2.ID.ToString();
})
.Children(Node2 => Node2.AssocProperty));
mappings.For<Node3>(binding => binding
.ItemDataBound((item, Node3) =>
{
item.Text = Node3.Property1;
item.Value = Node3.ID.ToString();
}));
})
%>
which causes it to render like this. I find it unsual that when I set the value it is rendered in a hidden input ? But anyway:...
<li class="t-item">
<div class="t-mid">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1</span>
<input class="t-input" name="itemValue" type="hidden" value="6" /></div>
<ul class="t-group" style="display:none">
<li class="t-item t-last">
<div class="t-top t-bot">
<span class="t-icon t-plus"></span>
<span class="t-in">Node 1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="207" />
</div>
<ul class="t-group" style="display:none">
<li class="t-item">
<div class="t-top">
<span class="t-in">Node 1.1.1</span>
<input class="t-input" name="itemValue" type="hidden" value="1452" />
</div>
</li>
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
</ul>
</li>
</ul>
What I am doing is updating a div after the user clicks on a certain node. But when the user clicks on a node, I want to send the ID not the Node text property. Which means I have to get it out of the value in these type lines <input class="t-input" name="itemValue" type="hidden" value="1453" />, but it can be nested differently each time, so the existing code I ahve doesn't ALWAYS work:
<script type="text/javascript">
function TreeView_onSelect(e) {
//`this` is the DOM element of the treeview
var treeview = $(this).data('tTreeView');
var nodeElement = e.item;
var id = e.item.children[0].children[2].value;
...
</script>
So based on that, what is a better way to get the appropriate id each time with javascript/jquery?
edit:
Sorry to clarify a few things
1) Yes, I am handling clicks to the lis of the tree & want to find the value of the nested hidden input field. As you can see, from the telerik code, setting item.Value = Node2.ID.ToString(); caused it to render in a hidden input field.
I am responding to clicks anywhere in the tree, therefore I cannot use my existing code, which relied on a set relationship (it would work for first nodes (Node 1) not for anything nested below)
What I want is, whenever there is something like this, representing a node, which is then clicked:
<li class="t-item t-last">
<div class="t-bot">
<span class="t-in">Node 1.1.2</span>
<input class="t-input" name="itemValue" type="hidden" value="1453" />
</div>
</li>
I want the ID value out of the input, in this case 1453.
Hope this now makes a lot more sense.
if possible would love to extend this to also store in a variable how nested the element that is clicked is, i.e. if Node 1.1.2 is clicked return 2, Node 1.1 return 1 and node 1 returns 0
It's a little unclear what you're asking, but based on your snippet of JavaScript, I'm guessing that you're handling clicks to the lis of the tree & want to find the value of the nested hidden field? If so, you want something like this:
function TreeView_onSelect(e) {
var id = $(e.item).find(".t-input:first").val();
}
Edit: In answer to your follow-up question, you should be able to get the tree depth with the following:
var depth = $(e.item).parents(".t-item").length;
In jQuery you can return any form element value using .val();
$(this).val(); // would return value of the 'this' element.
I'm not sure why you are using the same hidden input field name "itemValue", but if you can give a little more clarity about what you are asking I'm sure it's not too difficult.
$('.t-input').live('change',function(){
var ID_in_question=$(this).val();
});

Categories

Resources