Adding url and alt attributes dynamically with radio buttons in Angularjs - javascript

So I have a group of radio buttons that are built dynamically using a JSON file. The JSON file contains things like url (of an image), alt text, etc...
I have it working pretty well, and can change the value of the img src based on the value of the radio button selected. However, what if I need to access other elements of the json object as well so that I can set the alt text of the image depending on the element selected.
Here is a plunkr of the basic functionality as I have it to this point...
http://plnkr.co/edit/bI4ggTNCPSq3o1Bt6gqg?p=preview
The JSON object looks like so:
{
"header_images" : [
{
"id" : "havoc-header",
"alt" : "Some alt text",
"fname" : "admissions-general-havoc-header.jpg",
"url" : "email-assets/header-images/admissions-general-havoc-header.jpg"
},
{
"id" : "cob-header",
"alt" : "Some more alt text",
"fname" : "cob-banner.jpg",
"url" : "email-assets/header-images/cob-banner.jpg"
},
{
"id" : "css-header",
"alt" : "still some more alt text",
"fname" : "css-banner.jpg",
"url" : "email-assets/header-images/css-banner.jpg"
},
{
"id" : "huns-header",
"alt" : "one more alt-text",
"fname" : "huns-banner.jpg",
"url" : "email-assets/header-images/huns-banner.jpg"
}
]
}
The radio buttons are being built in the view like so:
<label ng-repeat="header in assets.header_images">
<input type="radio" ng-model="assets.headerimage" value="{{header.url}}" id="{{header.id}}" data-alt="{{header.alt}}" />
{{header.url}}
</label>
And my controller looks like this:
function MainCtrl($scope, $http) {
$http.get('js/assets.json').then(function(res){
$scope.assets = res.data;
});
}
So basically when a radio button is clicked, I would like to populate this which is outside of the ng-repeat:
<img src="{{assets.headerimage}}" alt="{{alt}}" />
The src attribute is easy enough. I have that working fine...As you can see when one of the dynamic radio buttons are checked, the value is added to the assets.headerimage binding. But, how would I set the appropriate alt text?
I am sure this is really easy, but I can't get my head around it for some reason. I am relatively new to angularjs, so be gentle :D

in your radio ng-model, you are assigning header.url to the model, so you cant access the alt. but instead if you assign the header to the model you will be able to access the alt.
<label ng-repeat="header in assets.header_images">
<input type="radio" ng-model="assets.headerimage" value="{{header}}" id="{{header.id}}" data-alt="{{header.alt}}" />
</label>
<img ng-src="{{assets.headerimage.url}}" alt="{{assets.headerimage.alt}}"/>

Add ng-change event in the input. Assume the alt is in the parent scope, and then you can use $parent to access alt
<input type="radio" ng-model="assets.headerimage" ng-change="$parent.alt = header.alt" ... >

I'll start with a side-note, you should use ng-src (link) for populating source of an image. This way your browser will not hit 404s after request wrong URLs like /{{assets.headerimage}}.
Now, regarding your question. Assuming your id is unique, you could re-work your JSON to a form similar to this:
{
"header_images" : {
"havoc-header": {
"id" : "havoc-header",
"alt" : "Some alt text",
"fname" : "admissions-general-havoc-header.jpg",
"url" : "email-assets/header-images/admissions-general-havoc-header.jpg"
},
"cob-header": {
"id" : "cob-header",
"alt" : "Some more alt text",
"fname" : "cob-banner.jpg",
"url" : "email-assets/header-images/cob-banner.jpg"
},
"css-header": {
"id" : "css-header",
"alt" : "still some more alt text",
"fname" : "css-banner.jpg",
"url" : "email-assets/header-images/css-banner.jpg"
},
"huns-header": {
"id" : "huns-header",
"alt" : "one more alt-text",
"fname" : "huns-banner.jpg",
"url" : "email-assets/header-images/huns-banner.jpg"
}
}
}
then change value="{{header.url}}" of each of your checkboxes to value="{{header.id}}", and finally populate your image like this:
<img ng-src="{{ assets.header_images[assets.headerimage].url }}"/>
Another way does not require modifying the JSON structure, but it still involves using header.id as your model's value. Simply add a function like this to your scope:
$scope.getChosenHeader = function() {
for(var i=0;i<$scope.header_images.length;i++) {
var h = $scope.header_images[i];
if(h.id === $scope.assets.headerimage) {
return h;
}
}
}
and then populate your image like this:
<img ng-src="{{ getChosenHeader().url }}"/>
This way you can access the whole object instead of just url property. Of course remember to only show your <img/> tag if getChosenHeader().url is not undefined

Related

changing model property value is changing the same property in other object

im new to vue , i want user to be able to add some specific social media links to the page and edit some properties like it's text
i have 2 objects in my data , models and defaults
defaults contains selectable option for social media links and their initial values
basically i copy the default value into models and let the user customize the model via inputs
data () {
return {
models : [] ,
defaults : {
twitter : { id : null , placeholder : 'my twitter' , icon : 'twitter' , text : null , 'link' : null } ,
instagram : { id : null , placeholder : 'my instagram' , icon : 'instagram' , text : null , 'link' : null } ,
tiktok : { id : null , placeholder : 'my tiktok' , icon : 'tiktok' , text : null , 'link' : null } ,
} ,
}
} ,
so there a select menu for user to select which social he wants to add to the page
Select :
<ul >
<li v-for="(social, index ) in defaults" :key="index">
<a #click="appendSocial(index)">
{{ index }}
</a>
</li>
</ul>
here is my #click="appendSocial(index)" function
appendSocial(type){
let typedefault = this.defaults[type];
this.models.push(typedefault)
},
and finally i show my models to user and provide an input for editing it's text via v-model
<div v-for="(model, index) in models" v-bind:key="model.id">
<a class="button-preview">
{{ model.text === null ? model.placeholder : model.text }}
</a>
<label>Text</label>
<input type="text" v-model="model.text" :key="index" :placeholder="model.placeholder">
</div>
so here is the problem , for some reason changing the models properties will change the same property in defaults ... and changing defaults properties will change the same property models !!
like if i add a twitter menu to the page and change it's text (model.text) to abc via v-model ... it will also change defaults.twitter.text to abc
to better demonstrate the problem i've added some console log to my appendSocialfunction
appendSocial(type){
let typedefault = this.defaults[type];
console.log(`-------- default object for ${type} -----------`);
console.log(typedefault);
this.addElement(typedefault);
},
here is the result
1 - i've selected a twitter link and added to my models , you can see defaults.twitter.text is null
2 - i've change my model.text (i suppose it would be models[0].text) to abc
3 - i've added another twitter link to my page ... this time defaults.twitter.text is also abc
also changing defaults properties will effect all the models the has been getting their value from that default object
like if i change the defaults.instagram.text to xyz all my models which have got their initial values from defaults.instagram will also change their text to xyz
it's like they are referencing each other , but i haven't passed the value between 2 objects by reference
im not sure what's happening here and how can i prevent this ?
This is because
let typedefault = this.defaults[type];
this.models.push(typedefault)
Is storing the reference to the object into your this.models array. And so if you mutate the element, you're by default changing the base object. A quick and dirty way of doing a deep clone is the following.
let typedefault = this.defaults[type];
let clonedObj = JSON.parse(JSON.stringify(typedefault));
this.models.push(clonedObj)
Note: Lodash library does have a proper deep clone functionality.
https://www.geeksforgeeks.org/lodash-_-clonedeep-method/

Find linked DOM nodes by path with JsViews

Is there an easy way to find DOM nodes in the property path of the linked JS object?
For exmpale I have some object
<script type="text/javascript">
model = {
companyName : "JS Corporation",
address : "",
staff : [
{
name : "Jack Brown",
position : "SW developer"
},
{
name : "John Dow",
position : "Big Boss"
},
{
name: "Billy Hill",
position : ""
}
]
}
</script>
and template
<!-- TEMPLATE -->
<script type="text/x-jsrender" id='companyTamplate'>
<p>company name <input data-link='companyName'></p>
<p>address <input data-link='address'></p>
<fieldset>
<legend>Staff</legend>
{^{for staff}}
<div class='person'>
<p> name <input data-link='name'> </p>
<p> position <input data-link='position'> </p>
</div>
{{/for}}
</fieldset>
</script>
and link them
$.templates('#companyTamplate').link('#companyView',model);
Then I send my object as JSON to a server and get a validation error like this
{
errors : [
{
"path" : "address",
"error" : "empty string"
},
{
"path" : "staff[2].position",
"error" : "empty string"
}
]
}
I want to highlight the input field for address and postion.
For highlighting the address field i can just navigate to
input[data-link=address]
But when I have an error in some array member it's not so evident.
Workaround is to add in input field an attribute with a full path
<input data-link='position' data-link-path='staff[{{:#index}}].position' >
and then navigate to
input[data-link-path="staff[2].position"]
But it's easy to forget add the additional attribute.
Is there a more simple way to solve this problem?
There is not necessarily a one to one mapping from data node to views or DOM nodes. You could have one to none, or one to many (for example if you had two {^{for staff}} tags in your template).
But if you are able to use knowledge of the template structure, then you can navigate the view hierarchy and use APIs such $.view() view.contents() view.childTags() to get from the data back to a chosen element on which you want to act programmatically.
For example in your sample, the following gets the jQuery object for the "Billy Hill" 'position' input:
$.view().get(true, "array").views[2].contents(true, 'input[data-link="position"]')
If you had two {^{for staff}} blocks and you want to navigate to elements in the second block, you can use childTags() APIs to get to the second one:
$.view().childTags("for")[1].tagCtx.contentView.views[2].contents(true, 'input[data-link="position"]')
which again gets you to the "Billy Hill" 'position input'.
See View Hierarchy, $.view() and the various view object and tag object APIs.

How do I send javascript parameters to DOM elements in from HTML table cells?

I have the following project, where I am working on a chess opening
repertoire, in table form.
http://web.ics.purdue.edu/~mjschwei/chesstable/3/
For each move, I would like a link that puts the board in the position that arises at that point in the variation, and comments
that I'd have for each move. Chessboard.js is easy to put into any position, it just needs a fen string
How do I send the following information to the chessboard
fen position, i.e. information like this "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR"
and the comment information, text information associated with the move to the comment div, like this
"This is the beginning of the Ruy Lopez, one of White's sharpest openings"
In this example
http://web.ics.purdue.edu/~mjschwei/chesstreed3/20/
when I click on a node, I am able to pass comment information and fen (board position) information to the "comment" div and javascript chessboard respectively, because that info, for each node, and each move, is store in the JSON file, like this:
"name" : "Ruy Lopez",
"children" : [
{
"name" : "e4",
"move" : "1",
"text" : "The Open Game",
"fen" : "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR",
"side" : "white",
"nodecolor" : "GhostWhite",
"textcolor" : "DarkSlateGrey",
"variation" : "Open Game",
"type" : "normal",
"children" : [
{
"name" : "e5",
"move" : "1",
"text" : "E4 is met with the symmetrical e5, indicating the liklihood of a sharp game",
"fen" : "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR",
"side" : "black",
"nodecolor" : "DarkSlateGrey",
"textcolor" : "white",
"variation" : "Open Game",
"type" : "normal",
"children" : [
{
------
and when I click on a node, this occurs
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
d3.select("#info").text(d.text);
d3.select("#score1").text(d.name);
board.position(d.fen)
}
where board gets the fen information, and the info div gets the text information. D3.js makes it easy for me, since the framework w/Json makes storing, and sending info, easy.
But, building this out in an HTML Table, I'm a little lost without d3.js and the Json file. I have only this for the link
<td class="black" id="3a6-">a6</td>
And I don't know where to put the fen, text, comment, or other information, nor how to send it. Can anyone help?
It's my first question, I've worked out a lot of d3.js on my own, and I don't want to aggravate anyone or get in trouble, but I didn't know how else to ask.
where to put the fen, text, comment?
<td class="black" id="3a6-">a6</td>
you can put it on HTML 5 data attribute
<td class="black" id="3a6-" data-fen="" data-text= "" data-comment="" >a6</td>
and call like this
var aaa = $("#test").data('fan')
console.log(aaa)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div id = "test" data-fan="fan is the best"></div>
</body>
</html>

split colon in view page using angularjs

I want replace colon in view page using angularjs.
For example i have some unique data like that
$scope.PlayerList =[{
"ID" : "111:Player",
"Name" : "Rasberry, Jackson (16U Blue)",
"Type" : null
}, {
"ID" : "112:Player",
"Name" : "Keller, Jacob",
"Type" : null
}]//This is my list
Here is my html
<div ng-repeat="item in PlayerList track by $index">
<input type="ceckebox" id="{{item.ID}}">
<!--Real out put is like that <input type="checkbox" id="111:Player"> -->
But i want to like <input type="checkbox" id="111Player">
</div>
I want to omit colon(:) in view page using angularjs without change $scope.layerList properties
If you have any idea please help me.........................
You can create a filter to achieve the results. Update code to following
<input type="ceckebox" id="{{item.ID | customText}}">
.filter('customText', function() {
return function ( input ) {
return input.split(":").join('');
};
})

CQ5 nested multi-field with select option

I will need to add the values that editor writes them in textfield from dialog panel into a <select> option. I have created the multi-field option for the dialog component, but not sure how I should get values in the <select> it self.
I'm using the JS widget for multi-field, you can find it here. I know that the multi-field JS should return a JSON array of options, how do I achieve this in my case?
Here's my XML markup:
<developer
jcr:primaryType="cq:Widget"
title="Data"
xtype="panel">
<items jcr:primaryType="cq:WidgetCollection">
<developer
jcr:primaryType="cq:Widget"
fieldDescription="Click the '+' to add a new data"
fieldLabel="Dev Data"
name="./devdata"
xtype="multifield">
<fieldConfig
jcr:primaryType="cq:Widget"
xtype="devprofile"/>
</developer>
</items>
</developer>
JavaScript that is adding the textfield for multi-field
this.developerName = new CQ.Ext.form.TextField({
fieldLabel : "Developer's Name",
allowBlank: true,
width : 400,
listeners : {
change : {
scope : this,
fn : this.updateHidden
},
dialogclose : {
scope : this,
fn : this.updateHidden
}
}
});
this.add(this.developerName);
And the markup:
<c:if test="${not(empty(developerName))}">
<select id="use-names" class="js-example-basic-multiple">
<option>Example</option>
<option>${developerName}</option>
</select>
</c:if>
Please let me know if you need more detailed shared.
ACS commons has pretty good working example of custom multifield. Take a look at https://adobe-consulting-services.github.io/acs-aem-commons/features/widgets.html
Just replace textarea with your dropdown and you will have what you need..

Categories