Updating Ember.js data from external event without global controller - javascript

I have started learning Ember.js and have been trying to make a page with some data that updates when I get an event (pushed by web sockets).
To make it simple I made an example where I have a list of nodes and when I get a new node I want to call addNode on the controller to add the node. The UI should then update.
The problem I have is that the only way I managed to do this is by having a Global Controller, and then using that in my template instead of my model for the template.
I would like to link up the controller to the route, and have a method on the controller add data when the event arrives - not by having some global list or something.
is this possible? And if so how?
I have included my sample so you can change it and show me how its done.
js:
x = {title : 'test'};
App = Ember.Application.create();
App.Router.map(function() {
this.resource("system");
});
App.NodesController = Ember.ArrayController.create({
content: [],
addNode: function(nodeInfo){
this.pushObject(nodeInfo);
}
});
html:
<script type="text/x-handlebars" id="system">
{{#each App.NodesController}}
{{title}}
{{/each}}
</script>
Thanks, Jason

This is a little shady, but I'm not sure if the ember guys have provided another method of getting the controller externally yet.
jsbin

You've to put needs in your system controller.
App.SystemController - Ember.Controller.extend({
needs:['nodes']
});
and in your system template,
<script type="text/x-handlebars" id="system">
{{#each controllers.nodes}}
{{title}}
{{/each}}
</script>

Related

Ember generated action or jquery onclick

I have an ember application which works fine. But user's interaction does some DOM insertion like below...
$(.test).append(<a {{action "getData"}}>Get Data</a>);
The problem is that Ember seems do not recognize that an action "getData" has been added to the DOM. Nothing is happening when I click the element. Any thoughts on this?
Another way I am trying to do is:
//create the element
$(.test).append(<a id="idnum">Get Data</a>);
//make click listener
$('#idnum').click(function(){
console.log("get data");
}
my question is where should i place the code inside the component so the it can listen on the click event. Thanks.
You should do it in Ember way. Try handlebars {{#if}} helper to render an element dynamically.
{{#if canGetData}}
<a {{action "getData"}}>Get Data</a>
{{/if}}
Here you can set the value of the canGetData to true in the controller based on the users action.
The first example can't work because ember does not analythe the Handlebars elements in the DOM, but rather parses your Handlebars template with HTMLBars, which is a full HTML parser, and then renders it manually by inserting elements, not text into the DOM.
However the second example is the way to go if you have to rely on external code that does manual DOM manipulation. And it does work. Checkout this twiddle.
This does work:
this.$('.target').append('<a id="idnum">Get Data</a>');
this.$('#idnum').on('click', () => {
alert('clicked');
});
Just make sure that the DOM is ready. So do it in the didInsertElement hook or after the user clicked a button or so.
Like Lux suggested avoid DOM manipulation. I prefer the following approach,
if it is dynamic then you can consider wrapping DOM element as a new component and use component helper.
find sample twiddle
In application.js
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
linksArray:[ Ember.Object.create({value:'Text to display',routename:'home'}),
Ember.Object.create({value:'Text to display2',routename:'home'})],
actions:{
addItem(){
this.get('linksArray').pushObject(Ember.Object.create({value:'AddedDynamically',routename:'home'}));
}
}
});
in Application.hbs
<h1>Welcome to {{appName}}</h1>
<br>
{{#each linksArray as |item|}}
{{component 'link-to' item.value item.route }}
{{/each}}
<button {{action 'addItem'}}>Add Item</button>
<br>
{{outlet}}
<br>
<br>

How to use a template with textboxes loaded with data passed from a calling template in ember.js

I'm trying to make a addstudent template which can be rendered with data if clicked on editstudent option from another template.
I don't want to make different templates for addStudentDetails and EditstudentDetails. how to create a single template for this both functions.
can anyone help me with this?
I see a couple ways of doing this.
Handlebar template, using conditionals.
Then in your controller you can control whether you want to edit or add.
The ember guide has a good example of this for controlling state.
Or you can use Routes and Resources. Possibly a Parent Resource called Student and then routes called edit and add. But you said you didn't want to use different templates so I'm assuming you'll probably want to use #1.
//inside your controller
App.StudentController = Ember.ObjectController.extend({
editStudent: true // or false
});
<!-- handlebars template possibly called studentTemplate -->
{{#if editStudent}}
<!-- code for editStudent -->
{{else}}
<!-- code for addStudent -->
{{/if}}
Hope that answers your question.

Implementing an accordian-type view in Ember.js

In a toy application, I have a 'posts' template that shows all of the post titles. When you click each title, instead of heading to a 'show' view I want to expand down the rest of the contents of that post, directly inline.
I've considered having the postRoute reuse the postsRoute and set a flag that would then be checked against in the handlebars template to splat out the rest of the post content.
What would be a preferred 'Ember-ish' approach that would let a resource's singular view be rendered inline with its index view in the correct location?
I would suggest to define an itemController on PostsController which can take actions for individual post objects.
Then, in your template define the action (e.g. toggleBody) that toggles a property on the itemController. You can use this property to show or to hide the body of each post:
App.PostsController = Ember.ArrayController.extend
itemController: 'post'
App.PostController = Ember.ObjectController.extend
showBody: no
actions:
toggleBody: ->
#toggleProperty('showBody')
return false
<script type="text/x-handlebars" data-template-name="posts">
<ul>
{{#each}}
<li>{{title}} <span {{action toggleBody}} class='label'>Toggle</span>
{{#if showBody}}
<div>{{body}}</div>
{{/if}}
</li>
{{/each}}
</ul>
</script>
See also this jsFiddle for a working demo.

Ember.js - build a link dynamically using input values

Using ember.js I have an input:
{{input id="my_input" name="my_input" type="text"}}
Then I want to create a link, using linkTo, but want the value of the input to be part of the href. Like this:
{{#linkTo 'my_resource' my_input}}the link{{/linkTo}}
I have the resource defined like this:
App.Router.map(function() {
this.resource("my_resource", {path: ":my_input"});
});
Is that possible?
Thanks.
Definitely:
http://emberjs.jsbin.com/eFAGixo/1/edit
App.Router.map(function() {
this.resource('search', {path:'search/:search_text'});
});
App.SearchRoute = Ember.Route.extend({
model: function(params){
// at this point params.search_text will have
// what you searched for
// since you are sending a string, it will hit the model
// hook, had you sent an object it would have skipped this
// hook and just used the object as your model
return { searchInThisRoute: params.search_text};
}
});
<script type="text/x-handlebars" data-template-name="application">
<h2>Welcome to Ember.js</h2>
{{input value=searchText}}
{{#link-to 'search' searchText}} Go To Search{{/link-to}}
{{outlet}}
</script>
PS: This actually seems like it would make more sense as a button and using Ember Actions, but this works just as dandy.

How Do I Properly Make a Meteor Template Reactive?

My app displays a collection of items and I'd like to add an item drilldown view.
I've only been able to get half of the solution working; I find the appropriate document and use that document to render the template:
var item = Items.findOne('my_id');
$('#main').html(
Meteor.render(function () {
return Templates.item(item)
}));
This renders the individual item successfully and the appropriate events are bound.
Here's the rub, the template isn't reactive! If I change its data using the associated event handlers or from the console, the template isn't updated. However, a page refresh will reveal the updated data.
I'm a Meteor noobie, so it's probably something very simple. Any help would be greatly appreciated.
It seems to me that you aren't using the templates in they way they were really intended to be.
A meteor app starts with the main html markup which can only exist once in your app..
<head>
<title>My New Fancy App</title>
</head>
<body>
{{>templateName}}
</body>
Then you add a template..
<template name="templateName">
{{#each items}}
template or relevant html goes here..
{{/each}}
</template>
Now you need a template helper to give you data for your {{#each items}} block helper..
Template.templateName.helpers({
items: function(){ return Items.find({}) }
});
All this gets defined on the client side..
Then you'll need a collection and the collection should be defined on both the client and server.
Items = new Meteor.Collection('items');
This should now work as long as you have records in your collection.
Since you wish to only wish to render a single document you can change the helper and template just slightly..
first the helper becomes:
Template.templateName.helpers({
item: function(){ return Items.findOne() }
});
Then the template can reference the values of the returned document through document, so we change our template to:
<template name="templateName">
{{item.propertyName}}
</template>

Categories