Find linked DOM nodes by path with JsViews - javascript

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.

Related

Can I make an attribute appear only once in a vue v-for

I have an array of people with associated teams. I want to display all the people in the record, but I only want to display their team name once.
Meaning, if the v-for loop has encountered this particular team name, it should put it in a new temporary array to signify that it should be unique, then when it encounters that team name again, checks it through that temporary array and prevent it from showing again.
Sample HTML Code:
<div id="a-list">
<div v-for="person in people">{{person.Name}}, {{person.Team}}</div>
</div>
Sample Vue Code:
var crew = new Vue({
el: "#a-list",
data: {
people:
[ { "Name": "Richard","Team":"DMS"}, { "Name": "Mark","Team":"VV"}, { "Name": "Steve","Team":"VV"}, {"Name":"Koji","Team":"MZ"}, {"Name":"Jamie","Team":"VV"} ]
}
});
Expected Output:
Richard, DMS
Mark, VV
Steve,
Koji, MZ
Jaimie,
Is this possible to do directly from the v-for loop and not in the JS file?
Edited to show more data that are not sequential
Update: As Fabio has pointed out, the above scenario wouldn't make much sense unless the order of the team is arranged sequentially in the output first. So his answer is correct.
This could be a solution:
<div id="a-list">
<div v-for="(person,index) in people"> {{person.Name}}, {{ ((index == 0) || person.Team != people[index-1].Team) ? person.Team : '' }}</div>
</div>

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('');
};
})

ng-repeat with an if-statement in Angular

I have a website with a huge list of 30 articles. Until now this was like 300 lines of HTML code and with angularjs it is now like 10 lines. But some of these articles are special and so I need to be more specific what to show. Here is the HTML code:
<div id="illustratorcontent" ng-repeat="illustrator in illustrators">
<article id="illustratorcontent{{illustrator.ID}}">
<header>
<h3>{{illustrator.name}}</h3>
</header>
<div class="illustratorcontentext">
<p>This work is copyrighted and owned by {{illustrator.name}}...</p>
<p>Details of the copyright holder: </p>
<ul class="listplacing">
<li>Name: {{illustrator.name}}</li>
<li>Website: {{illustrator.name}}'s website</li>
<li>Website: {{illustrator.name}}'s {{illustrator.website2text}}</li>
</ul>
</div>
</article>
</div>
and the IllustratorController is:
function Illustrator ($scope){
$scope.illustrators = [
{
"ID" : "Ilus1",
"name" : "Ilus1",
"website" : "http://Ilus1page.com/"
},{
"ID" : "Ilus2",
"name" : "Ilus2",
"website" : "http://Ilus2page.com/"
}, ...
{
"ID" : "IlusX",
"name" : "IlusX",
"website" : "http://IlusXpage.com/"
"website2text" : "Twitter IlusX",
"website2url" : "http://twitterilusx.com/"
}
]
}
So my question is how can I show the existing information and leave the li out where no info is given.
So on this line:
<li>Website: {{illustrator.name}}'s {{illustrator.website2text}}</li>
how can I say that this line is just shown when the object parameter is given or not empty. I already looked for some ways. There is something called ng-if and ng-ui but that did not work out for me. Also there is a way to realize it with classes and hiding divs. But actually I would prefer to not generate them. And the website2url/website2text is just an example, there are more values than this.
Similar questions, but mine is a bit more special I believe:
angular ng-repeat with condition
https://stackoverflow.com/questions/28334600/paginating-angular-ng-repeat-with-nested-filtering
Hello create filter for to show the link of not like
<li ng-if="showSecondWebLink(illustrator)">....</li>
create function in your controller
$scope.showSecondWebLink = function(illustrator){
if(illustrator.website2url && illustrator.website2text ){
return true;
}
return false;
}
Just try with:
<li ng-if="illustrator.website2url && illustrator.website2text">...</li>

Use AJAX to load content to a jQuery UI dialog

On my site, I have a bunch of profile previews that are being generated by running JSON data through a mustache.js template.
Here's the template:
<script id="profile-preview-template" type="text/template">
<div class="col-sm-3">
<a style="display:block">
<div class="profile-preview">
<img class="img-responsive img-circle center-block" width="200px" src="{{img_url}}" alt="Photo of {{first_name}} {{last_name}}" />
<h1>{{first_name}} {{last_name}}</h1>
<h2 class="text-muted">{{major}}</h2>
<h3 class="text-muted">Cohort {{cohort}}</h3>
</div>
</a>
</div>
</script>
The profile preview shows select info from the JSON info like first_name, last_name, etc.
Here's the format of the JSON data:
{
"profiles": [
{
"first_name": "Robert",
"last_name": "Hosking",
"img_url": "img/about/hackbert.jpg",
"major": "Computer Science",
"cohort": 12,
"bio": "some info",
"linkedin": "some url",
"home_town": "some place",
"status": "Student"
},
{
"first_name": "Jimmy",
"last_name": "Harval",
"img_url": "img/about/hackbert.jpg",
"major": "Chemistry",
"cohort": 13,
"bio": "some info",
"linkedin": "some url",
"home_town": "some place",
"status": "Student"
}
]
}
However, when one of the previews is clicked, I'd like to make a jQuery UI dialog modal popup containing all of the info in the JSON data (not just the data displayed in the preview).
My question is how do I get a reference as to which profile preview was clicked so I know where in the JSON file to look to get the rest of the information.
I made some slight changes to your HTML and template script.
<div class="profile-full"></div>
<div class="preview"></div>
<script id="profile-preview-template" type="text/template">
<div class="col-sm-3">
<a style="display:block">
<div class="profile-preview">
{{#profiles}}
<img class="img-responsive img-circle center-block" width="200px" src="{{img_url}}" alt="Photo of {{first_name}} {{last_name}}" />
<h1 id="whichProfile">{{first_name}} {{last_name}}</h1>
<h2 class="text-muted">{{major}}</h2>
<h3 class="text-muted">Cohort {{cohort}}</h3>
{{/profiles}}
</div>
</a>
</div>
</script>
As you can see, 2 divs for demonstration purposes.
.profile-full Where the contents of the correct object is output using JSON stringify. What gets output can of course be changed, this is just to show that the correct object is selected.
.preview Where the preview profiles are generated.
I also added the opening and closing {{#profiles}} {{/profiles}}
profile is derived from what is set in the beginning of your json.
"profiles": [
We assume that you have correctly linked the jQuery and mustache.js libraries. Name your json file from your post as data.json
You must also link the .js file below. I have named it main.js.
$(function() {
$.getJSON('data.json', function(data) {
var template = $('#profile-preview-template').html();
var html = Mustache.to_html(template, data);
$('.preview').html(html);
allProfiles = data.profiles;
lookup = [];
for(i=0;i<data.profiles.length;i++) {
lookup.push(data.profiles[i].last_name);
};
});//getJSON
$(document).on('click', '#whichProfile', function() {
var whichName = $(this).text().substr($(this).text().indexOf(" ") + 1);
var i = jQuery.inArray(whichName, lookup);
$('.profile-full').html(JSON.stringify(allProfiles[i]));
});
});//document ready
So what's happening here?
$(function() {
$.getJSON('data.json', function(data) {
var template = $('#profile-preview-template').html();
var html = Mustache.to_html(template, data);
$('.preview').html(html);
After our document.ready shorthand, we GET the contents of the data.json, and hold it in data. Then we put the contents of html into the div with a class of preview, as we have formatted it according to the template script.
allProfiles = data.profiles;
lookup = [];
for(i=0;i<data.profiles.length;i++) {
lookup.push(data.profiles[i].last_name);
};
});//getJSON
Next we'll create an array called lookup which contains only the last_name data from our json. This will make it possible and fast to get the reference as to which profile preview was clicked.
If you only have a few names, using the last name is fine, but use a unique identifier if the amount of profiles grows (So overlapping last names doesn't occur). Consider adding a unique id, or email to your json.data.
Then close your getJSON.
$(document).on('click', '#whichProfile', function() {
var whichName = $(this).text().substr($(this).text().indexOf(" ") + 1);
var i = jQuery.inArray(whichName, lookup);
$('.profile-full').html(JSON.stringify(allProfiles[i]));
});
});//document ready
Here on clicking a name will get the .text of what's on our page. We will convert it so it's only the last name (strip everything before the space).
We then search for this last name within the lookup array and return its index. Now that we have this index we can use it to get any value within the json relative to the index. In this example we just put the entire stringified contents of the relevant object into our profile-full div
Finally we close our document.ready.
Try clicking on a name (Can be the first or last name). We could of course put anything from that relevant object anywhere else. For example:
remove the line
$('.profile-full').html(JSON.stringify(allProfiles[i]));
and replace it with
alert(allProfiles[i].first_name + ' ' + allProfiles[i].last_name +
' is studying ' + allProfiles[i].major + ' as their Major');
Now clicking on a name will get a pop up, stating their full name and which Major they are studying.
In your template, give each <a> element an id attribute equal to the index of the corresponding profile in the JSON array.
Then, you can give the <a> element an onclick method such as foo(this), because this will pass the element to the function. In your function, you can retrieve the id with $(elem).attr("id"), where elem is the function parameter. Now you have the index of the profile, so it should be easy to retrieve the profile's information.

dynamic id ng-repeat

I am trying to set a dynamic id to my div within my ng-repeat. Let me show an example.
<div id="$index" ng-repeat="repeat in results.myJsonResults">
<div id="$index" ng-click="saveID($index)" ng-repeat="subRepeat in results.myJsonResults.subresults">
</div>
My problem is, when I click on my child div, I want to get my parent id name, but looks like angular doesn't set the ID properly to the div. Is it possible to set a dynamic ID in this concept?
PS: I tried, in the past, create a counter method on my controller and set an index, but it turns out that angular only recognizes the last value of this ID instead.
To answer your question, try this:
<div id="{{$index}}" ...>
While the above should work, this might be not what you want really (!). Please note that this is rather rare with AngularJS to manipulate elements by referring those by id.
You should focus on your model, declarative describe UI and let AngularJS do the rest without doing low-level DOM manipulations "by hand".
A use case I can think of is associating <label> elements with their respective <input> elements, as seen in http://jsfiddle.net/YqUAp/
Applied there is a pkozlowski.opensource's method
<label for="{{ 'textField-' + $index }}">Option {{ $index }}</label>
<input type="text" id="{{ 'textField-' + $index }}" ng-model="field"/>
Though I'm unsure if this is the most effective method. (Even if only for readability)
<div id="{{$index}}" ...>
Works well, but you can also put a dynamic id with the field repeated if - for instance - subRepeat would have an id field.
That would be:
<div id="subRepeat{{subRepeat.id}}" ...>
That will put ids like subRepeat1, subRepeat2, ... on your repeated div
you need to hold the index in your objects
results = { myJsonResults : [
{ name: "abc", id: 1, subResults: [
{ subName: "123", id: 1 },
{ subName: "456", id: 2 }] }
{ name: "xyz", id: 2, subResults: [
{ subName: "789", id: 1 },
{ subName: "098", id: 2 }] }
] };
Also your repeat refers to results, which disconnects them, instead use thisResult:
<div id="thisResult.id" ng-repeat="thisResult in results.myJsonResults">
{{ thisResult.name }}
<div id="subResult.id" ng-click="saveID(subResult.id)" ng-repeat="subResult in thisResult.subResults"> {{ subResult.subName }} </div>
</div>

Categories