New todo created but not displayed - todos app with EmberJS without ember-data - javascript

I am getting started with EmberJS, followed the Screencast. I am trying to do the TODOs app without ember-data.
Following is my HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Ember.js • TodoMVC</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars" data-template-name="todos">
<section id="todoapp">
<header id="header">
<h1>todos</h1>
{{input type="text" id="new-todo" placeholder="What needs to be done?" value=newTitle action="createTodo"}}
</header>
<section id="main">
<ul id="todo-list">
{{#each}}
<li {{bind-attr class="isCompleted:completed"}}>
<input type="checkbox" class="toggle">
<label>{{title}}</label><button class="destroy"></button>
</li>
{{/each}}
</ul>
<input type="checkbox" id="toggle-all">
</section>
<footer id="footer">
<span id="todo-count">
<strong>2</strong> todos left
</span>
<ul id="filters">
<li>
All
</li>
<li>
Active
</li>
<li>
Completed
</li>
</ul>
<button id="clear-completed">
Clear completed (1)
</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
</footer>
</script>
<script type="text/javascript" src="js/jquery-1.11.0.js"></script>
<script type="text/javascript" src="js/bootstrap.js"></script>
<script type="text/javascript" src="js/handlebars-v1.3.0.js"></script>
</script><script type="text/javascript" src="js/ember.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
The javascript - all in one place.
window.Todos = Ember.Application.create();
Todos.Router.map(function () {
this.resource('todos', { path: '/' });
});
Todos.TodosController = Ember.ArrayController.extend({
actions:{
createTodo: function(){
var title = this.get('newTitle');
if(!title.trim()){return;}
todos.push({
id: todos[todos.length-1].id +1,
title: title,
isCompleted: false
});
this.set('newTitle','');
}
}
});
Todos.TodosRoute = Ember.Route.extend({
model: function(){
return todos;
}
});
var todos=[{
id: 1,
title: 'Learn Ember.js',
isCompleted: true
},
{
id: 2,
title: '...',
isCompleted: false
},
{
id: 3,
title: 'Profit!',
isCompleted: false
}];
The problem is when I try to add a new todo entry, it is not shown in the UI as added, but when I type todos on the console.
I see the new entry had been added. Why is it that the model is changed but nothing is shown in the UI ?
I also notice that the new entry in the todos array does not have methods like getTitle, setTitle, get isCompleted, set isCompleted that other objects have - which makes me think I am definitely not missing something here.
It seems like I need to get hold of model for TodosRoute and add to it, if that is what I need to do.
How can I do that ?

You have to use todos.pushObject() instead of simple array.push().
Short explanation: "Push the object onto the end of the array. Works just like push() but it is KVO-compliant." http://emberjs.com/api/classes/Ember.MutableArray.html#method_pushObject
Somewhat longer explanation: While Ember extends JS objects like array by default to make them better citizen in Emberland, it doesn't override existing methods like push(). This is why you need to use a separate method (pushObject) - it's Ember's solution to make data binding work on JS objects like an array.
Almost the same goes for plain JS objects: Ember has Ember.Object, which has some methods of it's own, which are not needed when all you need is a classic JS object. That doesn't mean you must use Ember.Object every time, in this case Ember is smart enough to set up data binding for you, based on the properties you access from your Handlebars template.

Related

Use Shopify/draggable with Alpine.js x-for directive

I'm trying to make the sortable plugin from Shopify working with Alpine.js but when I drag and drop the items, it generate the error in the console
"Alpine Error: 'ReferenceError: framework is not defined'
Expression: 'framework'
Element: "<li x-text="framework" tabindex="0" style="" class="draggable-source--is-dragging">springs</li>
Here's a reproducible example
https://codepen.io/cbaconnier/pen/bGgxyWE?editors=1011
<html>
<head>
</head>
<body>
<div x-data="{frameworks: ['laravel', 'rails', 'django', 'springs']}">
<ul>
<template x-for="framework in frameworks" :key="framework">
<li x-text="framework"></li>
</template>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.8.2/dist/alpine.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#shopify/draggable#1.0.0-beta.12/lib/sortable.js"></script>
<script>
const sortable = new Sortable.default(document.querySelectorAll('ul'), {
draggable: 'li'
});
</script>
</body>
</html>
I know there's some homemade dragable/sortable that have been done with Alpine.js but since I'm already using it with Livewire on this project and some other, it would be nice to also make it works.
Even though I would like to keep Shopify/draggable I have resigned to use SortableJS/Sortable that seems to works quit well.
<html>
<head>
</head>
<body>
<div
x-data="{frameworks: ['laravel', 'rails', 'django', 'springs']}"
x-init="Sortable.create($refs.items)"
>
<ul x-ref="items">
<template x-for="framework in frameworks" :key="framework">
<li x-text="framework"></li>
</template>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.8.2/dist/alpine.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.13.0/Sortable.min.js"></script>
</body>
</html>

Generate and render a Flask template when Bootstrap tab is changed

I have a Flask setup serving a template. The data for this can be nicely split across a few different "sets" which can all be rendered with the same template. I have inserted Bootstrap tabs which I would like to handle displaying the data for each separate set. The data my be non-existent or incomplete initially, but will be filled in over time. The hope is that when the user clicks a new Bootstrap tab, it triggers a request for Flask to return the most recent data pertaining to whichever tab was selected.
I have boiled it down to the small example below. I can successfully generate the Flask request when the tab is changed, but the result isn't triggering a new rendering of the template.
Flask route:
#app.route('/test_route', methods=['GET'])
def get_simple():
set_number_str = request.args.get('set', "mydata-1", type=str)
set_number = int(set_number_str.split('-')[-1])
ready = bool(random.getrandbits(1))
if ready:
# get_data_for_set(set_number)
return render_template('simple_template.html',valid=1, data_set=set_number, data_points=[1,2,3])
else:
return render_template('simple_template.html', valid=0, data_set=set_number, data_points=[0, 0, 0])
Html:
<html>
<head>
<link href="{{url_for('static', filename='js/bootstrap-3.3.7-dist/css/bootstrap.min.css')}}" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="{{url_for('static', filename='js/jquery-3.3.1.min.js')}}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/bootstrap-3.3.7-dist/js/bootstrap.min.js')}}"></script>
</head>
<body>
<div class="my-tabs">
<ul class="nav nav-tabs" id="set-tabs">
<li class="active" id="data-1">
Set 1
</li>
<li class="" id="data-2">
Set 2
</li>
</ul>
<div id="my-tab-content" class="tab-content">
<div class="tab-pane" id="mydata-1">
<p>Placeholder 1</p>
</div>
<div class="tab-pane" id="mydata-2">
<p>Placeholder 2</p>
</div>
</div>
</div>
<script type="text/javascript">
if ({{valid}} == 0) {
$('#mydata'+'{{data_set}}').html("Waiting on scenario to start...");
} else {
$('#mydata'+'{{data_set}}').html("Have some data: " + "{{data_points}}");
}
</script>
<script type="text/javascript">
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var set_id = $(e.target).attr("href");
$.ajax({
url: "/test_route",
type: "get",
data: {set: set_id},
success: function(response) {
$('#mydata-'+set_id).replaceWith(response);
},
error: function(xhr) {
console.log(xhr)
}
});
return false;
});
</script>
When I run, the only thing I see is the "Placeholder _" text.
Is this a limitation of Flask/Jinja2 templating or something I'm doing wrong? If it's the former, do you have any alternative approaches you would recommend? My key constraint is needing to re-retrieve the data from the server each time as it will change.

AngularJS simple app webpage stops rendering code

I'm trying to go through an AngularJS tutorial on codeschool.com but as I work through it and refresh my browser to see my progress, at some point in the tutorial the browser always ends up loading a blank webpage and won't load the code anymore. I've attempted the tutorial from the start twice and it happened both times at different points.
I must keep doing something wrong in my code. Has anyone else had this issue or know how to fix it?
I've seen a couple similar questions on here but the answers haven't worked.
Here's the HTML:
<!DOCTYPE html>
<html ng-app='store'>
<head>
<html lang="en-US">
<link rel="stylesheet/css" type="text/css" href="bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<div ng-repeat="product in store.products">
<h1> {{store.product.name}} </h1>
<h2> {{store.product.price}} </h2>
<p> {{store.product.description}} </p>
<button>Add to Cart</button>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
And here's the JS file:
(function (){
const app = angular.module('store', [ ]);
app.controller("StoreController", function(){
this.product = gems;
});
const gems = [
{
name: "Dodecahedron",
price: 2.95,
description: 'Many sided gem. Would look great in a ring.',
},
{
name: "Pentagonal Gem",
price: 5.95,
description: 'A gem shaped like a pentagon. More expensive and
more shiny',
}
];
})();
Two things: you have a mismatched variable name between your controller and your view and then inside your ng-repeat you are including your controller alias instead of accessing each of the repeated elements directly.
First change this.product = gems; in your controller to this.products = gems; and then change your ng-repeat div to this:
<div ng-repeat="product in store.products">
<h1> {{product.name}} </h1>
<h2> {{product.price}} </h2>
<p> {{product.description}} </p>
<button>Add to Cart</button>
</div>

Handlebars.js Partial Sub-Template generation

I am having issue with the Partials/Sub Template generation in the Handlebars.js.
I have used the registerPartials Method properly, but still it is giving some sort of issue in rendering. If I remove the partial template it would render the content properly.
Below is the code that I am using:
<!DOCTYPE html>
<html>
<head>
<title>Handlebars.js example</title>
</head>
<body>
<div id="placeholder">This will get replaced by handlebars.js</div>
<script type="text/javascript" src="handlebars.js"></script>
<script id="myTemplate" type="x-handlebars-template">
{{#each allShoes}}
<li>
<span> {{name}} - </span> price: {{price}}
{{> description}}
</li>
{{/each}}
</script>
<script id="shoe-description" type="x-handlebars-template">
<ul>
<li>{{color}}</li>
<li>{{size}}</li>
</ul>
</script>
<script type="text/javascript">
var source = document.getElementById("myTemplate").innerHTML;
var template = Handlebars.compile(source);
// Register the Partial
//Handlebars.registerPartial("description", $("#shoe-description").html());
var shoesData = {
allShoes:[
{name:"Nike", price:199.00,color:"black", size:10},
{name:"Loafers", price:59.00, color:"blue", size:9},
{name:"Wing Tip", price:259.00, color:"brown", size:11}
]
};
Handlebars.registerPartial("description", $("#shoe-description").html());
document.getElementById("placeholder").innerHTML = template(shoesData);
</script>
</body>
</html>
Is there any issue with the registerPartial?
Any help is appreciated.
Thanks,
Ankit Tanna
I'd guess that your rendering issues are a side effect of asking the browser to render invalid HTML. Your HTML ends up with, more or less, this structure:
<div>
<li>
<ul>...</ul>
</li>
...
</div>
But an <li> must have a <ul>, <ol>, or <menu> as its parent. I quote the specification:
Permitted parent elements
ul, ol, menu
So having a <div> as the parent of an <li> is invalid and the browser may rewrite your not-quite-HTML to make it valid HTML. That correction could be making a mess of your inner lists. Fix your HTML and try again:
<script id="myTemplate" type="x-handlebars-template">
<ul>
{{#each allShoes}}
...
{{/each}}
</ul>
</script>
Demo: http://jsfiddle.net/ambiguous/R23Ak/

knockout example template example not working

In the following code why does the jquery template not render? I thought that the template was build in? thank you
<script id="friendsTemplate" type="text/html">
<ul>
{{each(index,friend) friends}}
<li>${ friend.name }</li>
{{/each}}
</ul>
</script>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/knockout-2.2.1.js"></script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>details</h1>
<p>first name: <input data-bind="value: firstName" /></p>
<p>last name: <input data-bind="value: lastName" /></p>
<p>full name: <span data-bind ="text: fullName"></span></p>
<h2>friends</h2>
<div data-bind="template: 'friendsTemplate'"></div>
<script id="friendsTemplate" type="text/html">
<ul>
{{each(index,friend) friends}}
<li>${ friend.name }</li>
{{/each}}
</ul>
</script>
</div>
</form>
</body>
</html>
<script type ="text/javascript">
function friend(name) {
return {
name: ko.observable(name)
};
}
var viewModel ={
firstName: ko.observable("bert"),
lastName: ko.observable("smith"),
friends:ko.observableArray([new friend('steve'),new friend('annie')])
};
viewModel.fullName = ko.dependentObservable(function () {
return this.firstName() + " " + this.lastName();
},viewModel);
ko.applyBindings(viewModel);
</script>
The jQuery.tmpl support is built in however you need to reference jQuery and the jQuery.tmpl to make it work as stated in the documentation under: Note 6: Using jQuery.tmpl, an external string-based template engine:
By default, Knockout comes with support for jquery.tmpl. To use it,
you need to reference the following libraries, in this order:
<!-- First jQuery --> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<!-- Then jQuery.tmpl --> <script src="jquery.tmpl.js"></script>
<!-- Then Knockout --> <script src="knockout-x.y.z.js"></script>
If you reference all the dependencies your code should work fine: Demo JSFiddle
You need to pass a data object to your template.
data-bind="template: { name: 'friendsTemplate', data: $data }"
Look at http://knockoutjs.com/documentation/template-binding.html for details.
Is there a reason why you are using a jQuery Template? The following is more in alignment with "typical" Knockout usage. Also you should only use external templates if there is reuse. This code could easily be inlined.
<script id="friendsTemplate" type="text/html">
<ul data-bind="friends">
<li data-bind="text: name"></li>
</ul>
</script>

Categories

Resources