Hi I have a categories route and then product route. Each product has many images.
I want to show this images in carousel. the first product i click i get carousel with images but when i click on second product no carousel is displayed
MyApp.ShowCarouselComponent = Ember.Component.extend({
content: [],
templateName: 'show-carousel',
classNames: ['carousel', 'slide'],
init: function () {
this._super.apply(this, arguments);
// disable the data api from boostrap
$(document).off('.carousel.data-api');
// at least one item must have the active class, so we set the first here, and the class will be added by class binding
//var cdata = this.get('controller').get('carouselData');
var obj = this.get('content').get('firstObject');
Ember.set(obj, 'isActive', true);
console.log('this is what obj is ');
console.log(obj);
},
previousSlide: function () {
this.$().carousel('prev');
},
nextSlide: function () {
this.$().carousel('next');
},
didInsertElement: function () {
this.$().carousel();
},
willDestroyElement: function () {
this.$('.carousel').remove();
this._super();
},
indicatorsView: Ember.CollectionView.extend({
tagName: 'ol',
classNames: ['carousel-indicators'],
contentBinding: 'parentView.content',
itemViewClass: Ember.View.extend({
click: function () {
var $elem = this.get("parentView.parentView").$();
$elem.carousel(this.get("contentIndex"));
},
template: Ember.Handlebars.compile(''),
classNameBindings: ['content.isActive:active']
})
}),
itemsView: Ember.CollectionView.extend({
classNames: ['carousel-inner'],
contentBinding: 'parentView.content',
itemViewClass: Ember.View.extend({
classNames: ['item'],
classNameBindings: ['content.isActive:active'],
template: Ember.Handlebars.compile('\
{{view.content}}\
<img {{bind-attr src="view.content.product_url"}} alt="dfdds"/>\
<div class="carousel-caption">\
<h4>{{view.content}}</h4>\
<p>{{view.content.image_description}}</p>\
</div>')
})
})
});
show-carousel component
{{view view.indicatorsView}}
{{view view.itemsView}}
<a class="left carousel-control" {{action previousSlide target="view"}}>‹</a>
<a class="right carousel-control" {{action nextSlide target="view"}}>›</a>
router.js
this.resource('categories', {
path: '/'
}, function () {
this.resource('category', {
path: '/:category_id'
}, function () {
this.resource('product', {
path: '/:product_id'
});
});
});
In case of Ember views and Ember Components case accessing dom in init() method is a bad idea because it might happen that element that you are trying to access is not yet inserted into dom. so putting code from init method into didInsertElement() might solve your problem.
Related
In the IndexRoute of my Ember hello world app, I start a setInterval function that I wish to allow the end user to turn off (with clearInterval) by clicking a dom element in the template, which triggers an action in the IndexController. So, the setIntervalId is set in the IndexRoute, and I need to pass it to clearInterval in the IndexController, but the way I have it below, the setIntervalId is undefined. I also tried to use App.IndexRoute.setIntervalId to no avail.
How would I accomplish this?
(function() {
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_ACTIVE_GENERATION: true
});
App.IndexRoute = Ember.Route.extend({
setIntervalId: 0,
model: function() {
this.setIntervalId = setInterval(this.someInterval, 5000)
},
someInterval: function(){
var datasource = 'http://hackernews/blahblah';
return new Ember.$.ajax({url: datasource, dataType: "json", type: 'GET'}).then(function(data){
return data;
})
},
});
App.IndexController = Ember.ObjectController.extend({
actions: {
clearTimeout: function(){
console.log('clearing interval', this.setIntervalId); //undefined
clearInterval(this.setIntervalId);
}
}
})
})();
template
<script type="text/x-handlebars" data-template-name="index">>
<h1>Hi Babe</hi>
{{ outlet }}
<label {{action "clearTimeout" on="click"}}>clear timeout</label>
</script>
To set the model, you need to return the value in the route’s model function:
model: function() {
return this.setIntervalId = setInterval(this.someInterval, 5000)
}
To access the model in the controller, you need to use this.get('model').
actions: {
clearTimeout: function(){
console.log('clearing interval', this.get('model');
clearInterval(this.get('model'));
}
}
My current approach works, but it relies on jQuery rather than targeting the element directly. This feels less than ideal. Is there a standard way of doing this in ember?
App.facetGroup = Em.View.extend({
templateName: "facet-group",
actions: {
showList: function(e) {
var id = '#' + this.get('elementId');
$(id).children('.facets-list').slideToggle(100)
}
}
});
The facet-group template:
<h3 {{action showList target="view"}} class="facet-group-heading">{{view.displayName}}</h3>
// Facet lists are hidden by default
<ul class="facets-list">
{{#each view.facets }}
{{view this}}
{{/each}}
</ul>
Wouldn't you be better of by creating a ContainerView with two child views: [h3 ..] and [ul ..]? Also you can target the view's jquery element by using: this.$(), instead of this.get('elementId')
Edit:
Something like this should work:
App.FacetGroupView = Ember.ContainerView.create({
childViews: ['header', 'list'],
header: Ember.View.create(
tagName: 'h3',
// templateName: 'facet-group/header' or
template: Ember.Handlebars.compile('
{{view.displayName}}
'),
classNames: ['facet-group-heading'],
click: function() {
// Access the list view element
this.list.$().slideToggle(100);
}
)
list: Ember.View.create(
tagName: 'ul',
classNames: ['facets-list'],
// templateName: 'facet-group/list' or
template: Ember.Handlebars.compile('
{{#each view.facets}}
{{view this}}
{{/each}}
'),
didInsertElement: function() {
// Hide facet list by default
this.$().hide();
}
)
});
You can also target the sibbling like
actions: {
showList: function() {
this.$('.facets-list').slideToggle(100)
}
}
Demo Fiddle
I have a ContainerView that swaps other views in and out. I use another ContainerView as content. Trying to swap in the nested ContainerView, after I swapped it out, results in the error: Uncaught Error: assertion failed: calling set on destroyed object.
Here's the fiddle: http://jsfiddle.net/hekevintran/bFSKD/
To make the error happen, click "Other Form" and then click "First Form".
I think the error is because views that are removed from ContainerViews are destroyed and the child views of the nested ContainerView are not recreated. What's the right way to fix this example?
Templates:
<script type="text/x-handlebars" data-template-name="box">
<div>
{{#each forms}}
<button {{action "selectForm" this }}>{{this.name}}</button>
{{/each}}
{{view container}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="form">
<form>
{{#each fields}}
<div>
{{this.label}}: {{view this.widget}}
</div>
{{/each}}
</form>
</script>
JavaScript:
App = Ember.Application.create({});
App.BoxController = Ember.Object.extend({
initialForm: null,
currentForm: null,
init: function () {
var form = this.get('initialForm');
this.set('currentForm', form);
this.get('container').set('currentView', form.get('view').create());
},
forms: [],
container: function () {
return Ember.ContainerView.create({
boxController: this,
controllerBinding: 'boxController.currentForm'
})
}.property(),
selectForm: function (form) {
this.set('currentForm', form);
this.get('container').set('currentView', form.get('view').create());
}
});
App.Field = Ember.Object.extend({
value: null,
widgetBaseClass: Ember.TextField,
widget: function () {
return this.get('widgetBaseClass').extend({
field: this,
valueBinding: 'field.value'
});
}.property('widgetBaseClass')
});
App.RangeField = App.Field.extend({
widget: function () {
var field = this;
return Ember.ContainerView.extend({
childViews: [field.get('select1').create(), field.get('select2').create()]
});
}.property('select1', 'select2'),
fromValue: null,
toValue: null,
value: function () {
return [this.get('fromValue.value'), this.get('toValue.value')];
}.property('fromValue', 'toValue'),
choices: [
'1',
'2',
'3',
'4'
],
remainingChoices: function () {
var fromValue = this.get('fromValue');
if (fromValue) {
var choices = this.get('choices');
var index = choices.indexOf(fromValue);
return choices.slice(index + 1);
}
return [];
}.property('fromValue', 'choices'),
select1: function () {
return Ember.Select.extend({
field: this,
valueBinding: 'field.fromValue',
contentBinding: 'field.choices'
});
}.property(),
select2: function () {
return Ember.Select.extend({
field: this,
valueBinding: 'field.toValue',
contentBinding: 'field.remainingChoices',
contentHasChangedOnce: false,
contentChanged: function () {
// Set the initial value only once
if (! this.get('contentHasChangedOnce')) {
this.set('contentHasChangedOnce', true);
this.set('value', this.get('content')[0]);
}
// Reset the value if the chosen value is no longer
// available
if (! this.get('content').contains(this.get('value'))) {
this.set('value', this.get('content')[0]);
}
}.observes('content')
});
}.property()
});
App.Form = Ember.Object.extend({
fieldNames: [],
fields: function () {
var that = this;
var out = [];
_.each(this.get('fieldNames'), function (fieldName) {
out.pushObject(that.get(fieldName));
});
return out;
}.property('fieldNames')
});
aForm = App.Form.create({
name: 'First Form',
fieldNames: [
'a',
'b'
],
a: App.Field.create({label: 'A'}),
b: App.RangeField.create({label: 'B'}),
view: Ember.View.extend({
templateName: 'form'
})
});
var boxController = App.BoxController.create({
initialForm: aForm,
forms: [
aForm,
Ember.Object.create({
name: 'Other Form',
view: Ember.View.extend({
template: Ember.Handlebars.compile('Foobar')
})
})
]
});
var boxView = Ember.View.create({
templateName: 'box',
controller: boxController
});
boxView.append();
The issue is that you are instantiating select1 and select2 when you create the class extending ContainerView inside the widget method of App.RangeField.
Change this code:
App.RangeField = App.Field.extend({
widget: function () {
var field = this;
return Ember.ContainerView.extend({
childViews: [field.get('select1').create(), field.get('select2').create()]
});
}.property('select1', 'select2'),
...
}
to this:
App.RangeField = App.Field.extend({
widget: function () {
var field = this;
return Ember.ContainerView.extend({
init: function() {
this.set('childViews', [field.get('select1').create(), field.get('select2').create()]);
this._super();
}
});
}.property('select1', 'select2'),
...
}
Now you create new child views each time you instantiate widget instead of re-using the same two views which were destroyed the first time you removed them from the DOM.
Given a view with a context like { id: 1, form_id: 5}, I want to create an {{action}} link to the form using the form_id.
My view code looks like:
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
And the action in my router looks like:
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
I get an error that reads:
Uncaught Error: assertion failed: You must specify a target state for event 'showForm' in order to link to it in the current state 'root.index'.
I'm guessing that the problem is with the way I'm setting up the context for transitionTo, but I haven't been able to figure out the correct solution.
Here is the full code to reproduce the problem:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
MyApp = Ember.Application.create({
autoinit: false
});
MyApp.router = Ember.Router.create({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
// Throws error:
// You must specify a target state for event 'showForm' in
// order to link to it in the current state 'root.index'
//
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
// Won't work because form deserialize finds id, not form_id
//showForm: Em.Route.transitionTo('root.form'),
// This won't work either
// showForm: Em.Route.transitionTo('root.form', { id: this.form_id }),
connectOutlets: function( router, context ){
var group = Em.Object.create({ id:1, form_id: 5 });
router.get( 'applicationController' ).connectOutlet( 'group', group );
}
}),
form: Ember.Route.extend({
route: '/form/:id',
serialize: function( router, context ){
return { id: context.id }
},
deserialize: function( router, context ){
var form = Em.Object.create({ id: 5, name: 'my form' });
return MyApp.Form.find( context.id );
},
connectOutlets: function( router, context ){
// left out for fiddle example
}
})
})
});
MyApp.ApplicationController = Ember.Controller.extend({});
MyApp.GroupController = Em.ObjectController.extend({});
MyApp.GroupView = Em.View.extend({ templateName: 'group' });
MyApp.initialize(MyApp.router);
And the cooresponding fiddle:
http://jsfiddle.net/jefflab/LJGCz/
I was able to come up with an ugly solution to this problem using a computed property as the context of my action. The key snippets are:
<script type="text/x-handlebars" data-template-name="group">
<a {{action showForm view.formHash href=true}}>go to form</a>
</script>
MyApp.GroupView = Em.View.extend({
templateName: 'group',
formHash: function() {
return { id: this.get('controller').get('form_id') };
}.property('form_id')
});
And the working fiddle is here:
http://jsfiddle.net/jefflab/pGv8u/
However, after talking to Tom Dale, it is clear that the "right way" to solve to solve this problem is by using materialized objects instead of id values. If you are using Ember data, this is a great use case for the "sideloading" belongsTo feature.
This Emberjs router refuses to work with jsfiddle Jquery onDomReady and returns the error ; Uncaught Error: assertion failed: Could not find state for path: "root".
However, when i change the jsfiddle jquery settings to onLoad, the page loads but the router still seems unrecognized and any attempt to do a manually transition of the router fails with the message error: Cannot call method 'transitionTo' of undefined. But if i click one of the action helpers in the view that points or links to a route, it throws the error.
Any suggestions on how to resolve this will be greatly appreciated.
App = Ember.Application.create({
ready: function(){
App.router.transitionTo('root');
}
});
App.stateFlag = Ember.Mixin.create({
stateFlag: function(name) {
var state = App.get('router.currentState.name');
while (state.name === name) {
state = state.get('parentState');
console.log('state');
//return true;
}
}.property('App.router.currentState')
});
App.ApplicationController = Em.Controller.extend();
App.ApplicationView = Ember.View.extend({
templateName: 'application'
});
App.HomeController = Em.ObjectController.extend();
App.HomeView = Em.View.extend({
templateName: 'home'
});
App.LoginController = Em.ObjectController.extend();
App.LoginView = Ember.View.extend({
tagName: 'form',
templateName: 'logging',
});
App.SectionController = Em.ObjectController.extend(App.stateFlag, {
stateFlag: 'sectionA',
stateFlag: 'sectionB'
});
App.SectionView = Ember.View.extend({
templateName: "sub_section_b_view"
});
App.SectionA = Em.ObjectController.extend();
App.SectionAView = Ember.View.extend({
templateName: "section A"
});
App.SectionB = Em.ObjectController.extend();
App.SectionBView = Ember.View.extend({
templateName: "section B"
});
App.Router = Ember.Router.extend({
enableLogging: true,
location: 'hash',
root: Ember.Route.extend({
anotherWay: Ember.Router.transitionTo('root.logon.index'),
showLogin: function(router, event) {
router.transitionTo('root.logon.index');
},
doHome: function(router, event) {
router.transitionTo('home');
},
doSections: function(router, event) {
router.transitionTo('section.index');
},
home: Ember.Route.extend({
route: '/',
connectOutlets: function(router, event) {
router.get('applicationController').connectOutlet('home');
}
}),
logon: Ember.Route.extend({
route: '/login',
enter: function(router) {
console.log("The login sub-state was entered.");
},
connectOutlets: function(router, context) {
router.get('applicationController').connectOutlet('mine', 'login');
router.get('applicationController').connectOutlet('section');
},
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
router.get('loginController').connectOutlet('loga', 'login');
}
})
}),
section: Ember.Route.extend({
route: '/section',
connectOutlets: function(router, event) {
router.get('applicationController').connectOutlet('section');
},
index: Ember.Route.extend({
route: "/"
}),
doSectionA: function(router, event) { router.transitionTo('section.sectionA');
},
sectionA: Ember.Route.extend({
route: 'section A',
connectOutlets: function(router, context) {
router.get('sectionController').connectOutlet('sectionA');
}
}),
doSectionB: function(router, event) { router.transitionTo('section.sectionB');
},
sectionB: Ember.Router.extend({
route:'section B',
connectOutlets: function(router, context) {
router.get('sectionController').connectOutlet('sectionB');
}
})
})
})
});
The handlebar templates
<script type="text/x-handlebars" data-template-name="application">
<h1>Hi samu</h1>
{{outlet loga}}
{{outlet }}
<a href="#" {{action showLogin }}> go to root.logon.index state</a>
<br>
<a href="#" {{action anotherWay}} >it works to go to root longon index using this</a>
</script>
<br>
<script type='text/x-handlebars' data-template-name='home'>
</script>
<br>
<script type="text/x-handlebars" data-template-name="logging">
{{view Ember.TextField placeholder="what" class="userInput" }}
{{outlet loga}}
<br>
<b> Inserted from Login controller and view </b>
</script>
<script type="text/x-handlebars" data-template-name="sub_section_b_view">
<b>Inserted from the subsection controller and view </b>
</script>
<script type='x-handlebars' data-template-name='section A' >
</script>
<script type='x-handlebars' data-template-name='section B' >
</script>
After tinkering about, i go everything working. The named outlet works and the nested sub-route works. Here is the working jsfiddle. In that jsfiddle, if you click, 'go to root.logon.index state' if will render the form being provided by the named outlet called {{outlet loga}}.
If you click the link called testing sections, it will render the section view which displays two link to two sub-sections, click on any of them renders their content. Also i tried to break each of the routes in the Router into many classes or standalone classes and then creating new routes tat extending those classes from inside the Router, to simulate splitting Emberjs Router across many files to reduce the Router size in real life situations and it worked
Here is the whole code.
Handlebars template
<script type="text/x-handlebars" data-template-name="application">
<h1>Hi Laps</h1>
{{outlet loga}}
{{outlet }}
<a href="#" {{action showLogin }}> go to root.logon.index state</a>
<br>
<a href='#' {{action doSection}}> testing sections</a>
</script>
<br>
<script type='text/x-handlebars' data-template-name='home'>
</script>
<br>
<script type="text/x-handlebars" data-template-name="logging">
{{view Ember.TextField placeholder="what" class="userInput" }}
{{outlet loga}}
<br>
<b> Inserted from Login controller and view </b>
</script>
<script type="text/x-handlebars" data-template-name="sub_section_b_view">
<b>Inserted from the subsection controller and view </b>
<a href='#' {{action doSectionA}}><p>Sub section yea</p></a>
<br>
<a href='#' {{action doSectionB}}> our B part yep</a>
{{outlet}}
</script>
<script type='text/x-handlebars' data-template-name='sectionA' >
<br>
<font color="red">my section a is working</font>
</script>
Javascript/Emberjs bit
App = Ember.Application.create({
ready: function(){
//App.router.transitionTo('root.home');
}
});
App.stateFlag = Ember.Mixin.create({
stateFlag: function(name) {
var state = App.get('router.currentState.name');
while (state.name === name) {
state = state.get('parentState');
console.log('state');
//return true;
}
}.property('App.router.currentState')
});
App.ApplicationController = Em.Controller.extend();
App.ApplicationView = Ember.View.extend({
templateName: 'application'
});
App.HomeController = Em.ObjectController.extend();
App.HomeView = Em.View.extend({
templateName: 'home'
});
App.LoginController = Em.ObjectController.extend();
App.LoginView = Ember.View.extend({
tagName: 'form',
templateName: 'logging',
/* class name does not work */
className: ['userInput']
});
App.SectionController = Em.ObjectController.extend(App.stateFlag, {
stateFlag: 'sectionB'
});
App.SectionView = Ember.View.extend({
templateName: "sub_section_b_view"
});
App.SectionAController = Em.ObjectController.extend();
App.SectionAView = Ember.View.extend({
templateName: "sectionA"
});
App.SectionBController = Em.ObjectController.extend();
App.SectionBView = Ember.View.extend({
templateName: "sectionB"
});
App.SectionARoute = Ember.Route.extend({
connectOutlets: function(router, context) {
router.get('sectionController').connectOutlet({viewClass: App.SectionAView});
}
});
App.SectionBRoute = Ember.Route.extend({
connectOutlets: function(router, context) {
router.get('sectionController').connectOutlet({viewClass: App.SectionBView});
}
});
App.Router = Ember.Router.extend({
enableLogging: true,
location: 'hash',
root: Ember.Route.extend({
anotherWay: Ember.Router.transitionTo('root.logon.index'),
doSection: Ember.Router.transitionTo('root.section.index'),
showLogin: function(router, event) {
router.transitionTo('root.logon.index');
},
doHome: function(router, event) {
router.transitionTo('home');
},
doSections: function(router, event) {
router.transitionTo('section.index');
},
home: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('home');
}
}),
logon: Ember.Route.extend({
route: '/login',
enter: function(router) {
console.log("The login sub-state was entered.");
},
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router, context) {
router.get('applicationController').connectOutlet('loga', 'login');
}
})
}),
section: Ember.Route.extend({
route: '/section',
doSectionA: Ember.Router.transitionTo('section.sectionA'),
doSectionB: Ember.Router.transitionTo('root.section.sectionB'),
connectOutlets: function(router, event) {
router.get('applicationController').connectOutlet('section');
},
index: Ember.Route.extend({
route: '/'
}),
sectionA: App.SectionARoute.extend({
route: '/sectionA'
}),
sectionB: App.SectionBRoute.extend({
route: '/sectionB'
})
})
})
});