Extract data from existing HTML with Angular.js - javascript

angular.js is great for complex client side JavaScript based web applications, but I also thinking about to use it for smaller simple JavaScript tasks.
For example I have a list with some items:
<ul>
<li data-id="1">Foo</li>
<li data-id="2">Bar</li>
</ul>
Now I want to add some buttons to the HTML which should filter and/or sort the list after some user input, which should be an easy task.
Is there any way to extract the data from existing HTML elements to use them with angular.js? The data need to be in the HTML, so search engine could also get a hold of whats
Edit for clarity:
The end result would be that the data from the ul list will be pushed into a model of the controller that handling the list. ([{id:1, text:"Foo"}, {id:2, text:"Bar"}])
If I push more objects into the model, the list should display them.
If I apply a filter to the model it should filter the li elements.
Best case scenario would be something similar to this:
<div ng-model="data">
<ul ng-repeat="object in data | filter:filterCriteria">
<li data-id="1">Foo</li>
<li data-id="2">Bar</li>
<li data-id="{{object.id}}">{{object.text}}</li>
</ul>
</div>

Okay. Apparently there is no way to this in a default Angular.js application.

I ran into the same problem in a project I'm working on. My solution was to add the data from the html to a variable in my angular controller and then hide the initial html. This method keeps the original list in the html for SEO, old browsers and users without javascript. It replaces this list with an angular generated one for other users.
A working example using your code is pasted below or you can see it at this link.
I'm aware this is an old question so my answer might not be of help to the initial poster. My solution is aimed at anyone else who runs into the same problem we did.
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app">
<head>
<script src="http://code.jquery.com/jquery-1.9.0.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script>
function Ctrl($scope) {
$scope.objects = [];
$scope.init = function(){
$("ul.data").hide();
$("ul.data li").each(function(){
var $this = $(this);
var newObject = {
name: $this.html(),
id: $this.attr("data-id")
};
$scope.objects.push(newObject);
});
};
}
</script>
</head>
<body>
<div ng-app>
<div ng-controller="Ctrl" ng-init="init()">
<ul>
<li ng-repeat="object in objects" data-id="{{object.id}}">{{object.name}}</li>
</ul>
<ul class="data">
<li data-id="1">Foo</li>
<li data-id="2">Bar</li>
</ul>
</div>
</div>
</body>
</html>

If I am understanding the question correctly, you could just use the angular.element().scope for the html element in question.
I use this method for certain interaction that can't be handled directly out of the box with angular.

Related

Add class on hover on Angular JS

I'm trying to add a class when hovering the li element in the code below with Angular
<li ng-mouseenter="cola-selected=true" class="pull-left" ng-class="{'selected' : cola-selected}">
<a href="interna.html">
<img src="assets/images/cola.png">
</a>
</li>
This is all the functionality the page will have, so I though maybe it is not necessarily to add a new js file for the js.
When the mouse enter to the li it should have the new class selected. The code above it is not working, and I cannot figure out why.
Here is an example of my code on a fiddle: https://jsfiddle.net/mjrmeffc/
Why do you need an extra file if you can write the logic in your angular application?
I assume you make use of ng-app and have a so called javascript file where your logic is, and you should include it in here.
Here is an example of the proper way of adding/removing a class.
<div ng-app>
<div
class="italic"
ng-class="{red: hover}"
ng-mouseenter="hover = true"
ng-mouseleave="hover = false">
Test 1 2 3.
</div>
</div>
NB I found this in another stackoverflow question, please make proper use of google search first, I don't have enough reputation to flag your question, or to make a comment.
* EDIT *
It seems like you're not yet familiar with AngularJS. You need to include your 'app.js' or 'script.js' whatever you want to call it. In there you define your application using
var app = angular.module('yourappname', [/* add your dependencies here*/]);
//Other logic like controllers or services
And your HTML should be
<div ng-app="yourappname">
<div ng-controller="yourController">
PLEASE ORIENTATE YOURSELF FIRST BEFORE YOU START ASKING QUESTIONS
Try using ng-mouseover attr
<li ng-mouseover="hoverIn()" class="pull-left" ng-class="{{applyClass}}">
write a function hoverIn() in your script and assign value when hover over
$scope.hoverIn = function() {
this.applyClass = "red";
}
Working Demo

ng-repeat only displaying the first iterated data

I have this very weird problem. I have googled answers about this problem, but they don't seem to be a solution for it. Im ng-repeating a Object which would yield two divs (in this case), where each div has some items within li-s. But, it isnt working.
HTML Code
<head>
<title>Orari</title>
</head>
<body ng-controller="OrariController as cont">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="orari.js"></script>
<div ng-repeat="orariDites in cont.OrariJaves">
<ul ng-repeat="orari in orariDites">
<li>{{orari}}</li>
</ul>
</div>
</body>
</html>
JavaScript Code (Orari.js in this case)
var app = angular.module("OrariT", []);
app.controller('OrariController', function(){
this.OrariJaves = Ditet;
});
var Ditet ={
Hene: ['Ekonomi','Histori', 'TIK', 'Letersi','Biologji', 'Fizike'],
Marte: ['Letersi','Anglisht', 'Matematike Gjer.', 'Gjermanisht','Gjermanisht', 'Kimi', 'Fizike'],
};
I have a attached a Plunker which you can find here. The problem is also viewable here too.
Your inner ng-repeat should be using track by $index, because while iterating through the second element of array, it has Gjermanisht value which comes twice. Which produces angular ngRepeat dupes error.
HTML
<div ng-repeat="orariDites in cont.OrariJaves">
<ul ng-repeat="orari in orariDites track by $index">
<li>{{orari}}</li>
</ul>
</div>
Opening the console after running the plunker shows that it doesn't like that you have duplicate strings in the array
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: orari in orariDites, Duplicate key: string:Gjermanisht, Duplicate value: Gjermanisht
http://errors.angularjs.org/1.4.8/ngRepeat/dupes?p0=orari%20in%20orariDites&p1=string%3AGjermanisht&p2=Gjermanisht
If I remove the second "Gjermanisht" from the Ditet.Marte array it appears to work as one would expect.

How can I use Angular to create interactive widgets from vanilla (non-Angular) HTML?

I'm building an Angular application: a training engine which will present online courses to the user.
Each course is basically a series of "slides" - HTML partials which the user can navigate through in sequence. Each slide can include zero or more interactive widgets of varying types: simple quizzes, hands-on exercises, etc.
My goal is for the courses to consist of pure HTML/CSS, so that less technical folks can build courses without having to get their hands dirty with JS or Angular. That's fine as long as courses only contain static HTML. But it gets tricky when I want to add the interactive widgets to a course.
For example, a sample course "slide" might look like this:
<p>Here's some static content introducing the quiz.</p>
<div class="quiz">
<ol class="questions" data-passing-score="50">
<li>
<p>What was Abraham Lincoln's last name?</p>
<ul class="answers">
<li>Smith</li>
<li>Johnson</li>
<li class="correct">Lincoln</li>
<li>Liebowitz</li>
</ul>
</li>
<li>
<p>What were George Washington's false teeth made of?</p>
<ul class="answers">
<li>Particle board</li>
<li class="correct">Wood</li>
<li>The bones of his enemies</li>
<li>Advanced space-age polymers</li>
</ul>
</li>
</ol>
</div>
<p>Here's some static content that appears after the quiz.</p>
...and, when this HTML file gets loaded (presumably via $http.get()), my application would notice that it contains a div with the class quiz, and would set up the needed interactivity: tweaking the structure of the markup (e.g., adding radio buttons and a submit button), perhaps hiding and showing elements (so the user would only see one question at a time), scoring the quiz on submission, etc.
This is a pretty common pattern in jQuery-land. Of course, we're not in jQuery-land.
If I'm thinking about this correctly, there are two problems I would need to solve to make this work.
Problem 1: First, I would need to get the quiz data out of the raw HTML, and into a JavaScript object. For example, I might parse the HTML above into a structure like this:
var quiz = {
passing_score: 50,
questions: [
{
ask: "What was Abraham Lincoln's last name?",
answers: [
{ text: "Smith", correct: false },
{ text: "Johnson", correct: false },
{ text: "Lincoln", correct: true },
{ text: "Liebowitz", correct: false }
]
},
...
]
};
I guess I'd want to convert the loaded HTML into a DOM tree (just in memory, not appended to the document), and then explore it (using jQuery or jqLite) to find the data I'm interested in.
Is that a sensible approach? Are there other approaches I might want to explore?
Problem 2: Second, I would need to replace div.quiz in the loaded HTML with the contents of a quiz template, like this:
<form ng-controller="QuizController as quizCtrl" ng-submit="quizCtrl.submit()">
<ol>
<li ng-repeat="question in quizCtrl.questions">
<p ng-bind-html="question.ask"></p>
<ul>
<li ng-repeat="answer in question.answers">
<label>
<input type="radio" ng-attr-name="{{ 'q' + $parent.$index }}" ng-model="question.selected_answer" ng-value="answer">
<span ng-bind-html="answer.text"></span>
</label>
</li>
</ul>
</li>
</ol>
<button type="submit">Score Quiz</button>
</form>
...and bind that div to QuizController.
How do I dynamically bind a particular DOM node to a particular controller? How do I get the quiz object (which I constructed in the previous step) into the controller's scope?
Is there a standard-ish solution to this problem in Angular-land? Or is this entire approach just totally bananas?
Hope this makes sense. Thanks for any guidance you can provide!
The answer, as #dandavis suggested in the comments above, is custom directives.
For my quiz example, I could create a custom directive that defines a custom element. (Alternately, I could use restrict: 'C' in my directive definition to match <div class="quiz">, or restrict: 'A' to match on <div quiz>.)
Then, I just would put all my setup-and-DOM-massaging logic in the directive's link function.
Finally, I would just need to specify to the course authors how they should mark up a quiz.
I haven't fully played this out in real code yet, but I'm pretty sure this is all accurate.

jquerymobile refiltering

I'm using jquerymobile. And on a list I have a filter. When a user types something in it filters great. However when I add more items to the list, then the filtering does not refresh.
Any idea how I can refresh the filtering? (re-filter the list?)
Thanks
Trigger a change event on the search input field like this after items are added to list.
$(".ui-input-search .ui-input-text").trigger("change");
A sample:
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script>
$("#btn").live("click",function(){
$("#list").append("<li><a href='#'>100</a></li><li><a href='#'>200</a></li><li><a href='#'>400</a></li><li><a href='#'>500</a></li><li><a href='#'>1000</a></li>");
$("#list").listview("refresh");
$("#page").trigger("create");
$(".ui-input-search .ui-input-text").trigger("change");
});
</script>
<script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
</head>
<body>
<div id="page" data-role="page">
<div data-role="header">
<h1>Test Page</h1>
</div>
<div data-role="content">
<ul id="list" data-role="listview" data-inset="true" data-filter="true">
<li>1</li>
<li>12</li>
<li>41</li>
<li>8</li>
<li>91</li>
<li>65</li>
</ul>
<a data-role="button" id="btn">Add more</a>
</div>
</div><!-- /page -->
</body>
</html>
Demo - http://pastehtml.com/view/bnl7lpe3o.html
Let me know if that helps.
Update for jQuery Mobile 1.4
The filter behavior has been separated out into a filterable widget. You can update your listview content and rerun the filter in one swipe:
$( ".listselector" ).filterable( "refresh" );
A nice benefit of this is that the list items are filtered immediately. You do not experience the visual glitch of seeing the items for a split second before they get filtered, as you would when manually triggering the input control.
See http://api.jquerymobile.com/filterable/#method-refresh
The code above would no longer work, because jQuery Mobile keeps track of the last text entered into the filter, and does not filter if the input has not changed. If you did want to trigger the input control, you would need the following hack to first clear the last entered input:
$(".ui-input-search input").attr('data-lastval', '').trigger("change");
But, please use the filterable function going forward.
I think the problem should be resolved with refresh method, but I am not sure:
$('#mylist').listview('refresh');
After updating the list view, adding the below code will refresh the contents so that it becomes visible :
$("#list").listview("refresh");
Besides using the previously mentioned listview refresh, take note that using jQuery's .show() on a listitem will cause display:block; to be added to the element's CSS. Because it takes precedence over jQM's ui-screen-hidden class, it interferes with the searchfilter's ability to hide items when they don't match.
So if you are adding items to the list by way of .show()/.hide() combos, use .css('display','') instead of .show().
My general order for adding new hidden items:
// 1. Clear display:none and display:block, if necessary, from the listitem
$(yourLI).css('display','');
// 2. Apply jQM formatting, such as corners and other CSS, to the entire listview
$(yourListView).listview('refresh');
// 3. Make sure the searchfilter runs on the new items without user intervention
$(".ui-input-search .ui-input-text").trigger("change");

handlebars.js just stopped working?

I have an interesting problem. Ive had a handlebars.js template thats been working for a week and just stopped. I was hoping someone might have an idea as to why.
Heres the template
<script id="banners-template" type="text/x-handlebars-template">
<div class="banner-container" >
{{#banners}}
<ul class="banner" >
<li><div class="checkbox"></div></li>
<li>{{publisher_status}}</li>
<li>Test Link</li>
<li><img class="banner" src="{{imageurl}}"/></li>
<li>{{description}}</li>
<li>{{width}}x{{height}}</li>
</ul>
{{/banners}}
</div>
</script>
Heres the code that works with this.
var bannersRawTemplate = $("#banners-template").html();
var bannersTemplate = Handlebars.compile(bannersRawTemplate);
data = '{"banners":[{"type":"banner","width":125}]}';
alert(bannersTemplate(data));
I realize that type is not accessed in the template above but it shouldnt matter. I should still get the code inside of the "banners" array loop displayed once. This is not the case. The only part of the template that displays is . Its like its not seeing the banners array inside the JSON.
Any ideas?
Thanks in advance.
Is there a particular reason you don't pass in the actual JSON, and instead pass a string? From my experience with Mustache, and a cursory review of the Handlebars.js documentation, you should be passing:
JSON.parse('{"banners":[{"type":"banner","width":125}]}');
And, assuming the code you've shown is "really real", why not just:
{"banners":[{"type":"banner","width":125}]}
This is, of course, assuming that the issue really isn't that you've got a list with just one element.

Categories

Resources