According to this, http://ember-addons.github.io/bootstrap-for-ember/#/show_components/popover, shows the documentation on how this is supposed to work...quoted below may be the bit I'm not understanding (it's a little confusing because this all mentions tooltips and not popovers but I'm making a leap that since it's all in the same documentation that it's all related...)
(controllers)dashboard.js
import Ember from 'ember';
export default Bootstrap.TooltipBoxController=Ember.Controller.extend({
hoverPop: Ember.Object.create({
title: "I'm a title!",
content: "And i'm a content!",
trigger: "hover",
placement: "right",
sticky: true
})
});
(templates)dashboard.hbs
<div class="pageheader">
{{bs-bind-popover hoverPop}} ?
</div>
application.js inside this file i have:
import Ember from 'ember';
export default Ember.Route.extend({
renderTemplate: function() {
// Render default outlet
this.render();
// render extra outlets
var controller = this.controllerFor('tooltip-box');
this.render("bs-tooltip-box", {
outlet: "bs-tooltip-box",
controller: controller,
into: "application" // important when using at root level
});
},
....
Ember console:
Uncaught TypeError: undefined is not a function (refers to this line export default Bootstrap.TooltipBoxController=Ember.Controller.extend({
Any ideas? Has anyone solved this problem?
Related
Is it possible to update a property on all instances of a component?
If I have 10 instances of the component below on a page, I would like to set the currentTrack property to false on all of them. Is this possible? Can it be done from inside one of the components?
import Ember from 'ember';
export default Ember.Component.extend({
currentTrack: true,
});
I'm using Ember 2.12
You can use Ember.Evented for this use case.
Here, there is a simple twiddle for it.
template.hbs
{{your-component-name currentTrack=currentTrack}}
{{your-component-name currentTrack=currentTrack}}
{{your-component-name currentTrack=currentTrack}}
// btn for disabling
<a href="#" class="btn" onclick={{action 'makeAllcurrentTracksFalse'}}>To false</a>
controller.js
currentTrack: true,
actions: {
makeAllcurrentTracksFalse() {this.set('currentTrack', false)}
}
or in your-component-name.js - you can use the same action as above and it will be applied to all components
How about you create entries for what ever thing your're trying to achieve.
const SongEntry = Ember.Object.extend({
});
To create an entry you would call (probably add a song to playlist?)
songs: [],
addNewSongToList: function(songName) {
const newEntry = MyMusicEntry.create({
isCurrent: false,
title: songName
});
this.get('songs').pushObject(newEntry);
},
activateNewSong: function(newSongToActivate) {
this.get('songs').forEach(s => s.set('isCurrent', false);
newSongToActivate.set('isCurrent', true)
}
Template would look like this
{{each songs as |song|}}
{{my-song-component songEntry=song changeSong=(action "activateNewSong")}}
{{/each}}
//my-song-component.js
<div class="song-layout" {{action "changeSong" song}}>
I started an adventure with Ember a few weeks ago.
I have solid progress thanks to docs and example around the internet.
Sadly I hit a solid wall with this one as have almost copy-pasted models out of which most work and one and only one does not.
The error that I see in Inspector is:
Encountered a resource object with type "series", but no model was found for model name "series" (resolved model name using 'my-app#serializer:-json-api:.modelNameFromPayloadKey("series"))
Error while processing route: serie.index data is null...
I'm using mirage fixtures with success
// mirage/fixtures/files.js
export default [
{duration:'1',filename:'1.mkv',size:'1',id:'1',url:'dl/1.mkv'},
{duration:'2',filename:'2.mkv',size:'2',id:'2',url:'dl/2.mkv'}
];
// mirage/fixtures/series.js
export default [
{type:'show',title:'ser1',summary:'123',id:'11'},
{type:'show',title:'ser2',summary:'234',id:'12'}
];
Both use the same model for mirage
// mirage/model/file.js
// mirage/model/serie.js
import { Model } from 'ember-cli-mirage';
export default Model.extend({
});
I load fixtures this way:
// mirage/scenarios/default.js
export default function(server) {
server.loadFixtures();
}
And serializer is set on mirage this way:
// mirage/serializers/application.js
import { JSONAPISerializer } from 'ember-cli-mirage';
export default JSONAPISerializer.extend({
});
the only thing that I added to the config is
// added to mirage/config.js
this.namespace = 'api';
this.get('/series');
this.get('/series/:id');
this.get('/files');
this.get('/files/:id');
There is nothing more to do with mirage so let's move onto ember.
// app/adapters/application.js
application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api'
});
Both use the same component
// app/components/file-view.js
// app/components/serie-view.js
import Ember from 'ember';
export default Ember.Component.extend({
});
Models are defined this way:
// app/models/file.js
import DS from 'ember-data';
export default DS.Model.extend({
filename: DS.attr(),
url: DS.attr(),
art: DS.attr()
});
// app/models/serie.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr(),
type: DS.attr(),
summary: DS.attr()
});
// app/router.js
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('serie', function() {
this.route('show');
});
this.route('file', function() {
this.route('show');
});
});
export default Router;
Routes are almost identical
// app/routes/file.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('file');
}
});
// app/routes/serie.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('serie');
}
});
Same goes for the templates
// app/templates/file.hbs
<h2>Files</h2>
{{#each model as |fileUnit|}}
{{file-view file=fileUnit}}
{{/each}}
{{outlet}}
// app/templates/serie.hbs
<h2>Series</h2>
{{#each model as |serieUnit|}}
{{serie-view serie=serieUnit}}
{{/each}}
{{outlet}}
And last are the component templates:
// app/templates/components/file-view.js
<div>
<img src="cover.jpg" width=200 hight=200 alt="">
<h3>{{file.filename}} id: {{file.id}}</h3>
</div>
// app/templates/components/serie-view.js
<div>
<h3> {{serie.title}} id: {{serie.id}}</h3>
Summary: {{serie.summary}}
</div>
And as http://localhost:4200/file works fin the http://localhost:4200/serie throw an error
I tried to tackle this by removing the unnecessary code and models so that I could narrow down the problem but ended up having two models that are very similar with almost copy-pasted functionality yet only one working.
I really have no idea what is this about anymore.
Your error gives you a hint
Encountered a resource object with type "series", but no model was found for model name "series" (resolved model name using 'my-app#serializer:-json-api:.modelNameFromPayloadKey("series"))
The problem is that ember knows how to switch between files and file but not between series and serie because the word series is irregular (both singular and plural) so serie is not its proper singular form.
Override modelNameFromPayloadKey method in your serializer to return the proper model name for the key 'series':
export default DS.JSONAPISerializer.extend({
modelNameFromPayloadKey(key) {
// if payload model name is 'series', use 'serie'
if (key === 'series') {
return 'serie';
}
// otherwise do the default thing
return this._super(...arguments);
}
});
This official guide describes how you can bind a boolean property to disabled attribute of a HTML element. Yet it talks about a controller.
I have a button, that when clicked transitions the route (sorry it has to be a button and cannot be a link-to):
/templates/trails.hbs
<button type="button" class="btn btn-primary" disabled={{isEditing}}
onclick={{route-action 'addNew'}}>Add New</button>
(route-action is a helper that allows me to use closure actions in routes)
/routes/trails.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
addNew() {
this.transitionTo('trails.new');
}
}
});
So, after the button is clicked, the route is changed to 'trails.new'
/routes/trails/new.js
import Ember from 'ember';
export default Ember.Route.extend({
isEditing: true,
});
This property appears to be ignored and is not bound as I had expected it would be. I also tried adding a controller:
/controllers/trails/new.js
import Ember from 'ember';
export default Ember.Controller.extend({
isEditing: true,
});
So how does the official guide suggest something that seems to not work? What piece of ember magic am I missing here?
Your template is templates/trails.hbs but you set isEditing in a subroute controller controllers/trails/new.js
You need to have controllers/trails.js and deinfe isEditing in it.
So in routes/trails.js implement this :
actions: {
willTransition: function(transition) {
if(transtions.targetName === 'trails.new'){
this.controller.set('isEditing', true);
}
else{
this.controller.set('isEditing', false);
}
}
}
After some digging around I discovered that what I was trying to do is not the right way to go about this at all. I would have to add a controller/trails.js and put the property 'isEditing' in that.
So I refactored this into a component: add-new-button. This is a far more 'ember' way.
First, I need an initializer (thanks to this question):
app/initializers/router.js
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
(this injects the router into the component, so I can watch it for changes and also 'grab' the currentRoute)
My code refactored into the component:
app/components/add-new-button.js
import Ember from 'ember';
export default Ember.Component.extend({
isEditing: function() {
let currentRoute = this.get('router.currentRouteName');
return ~currentRoute.indexOf('new');
}.property('router.currentRouteName')
});
templates/components/add-new-button.hbs
<button type="button" class="btn btn-primary" disabled={{isEditing}}
onclick={{route-action 'addNew'}}>Add New</button>
templates/trails.hbs
{{add-new-button}}
The beauty of this is now I can use this button on my other top level templates to trigger route changes to the new route for each resource (and disable the button on arrival at the new route).
NOTE
return ~currentRoute.indexOf('new');
is doing a substring check on the route, if it finds 'new' returns true, otherwise returns false. See this.
In ES6 it can be replaced with (so I have!):
return currentRoute.includes('new);
I'm trying to test a link-to href property in an ember component. with ember 2.0
but when I render the component with renter hbs it renders this:
<div id=\"23\" class=\"ember-view\">
<p><!----></p>
<div class=\"participantes\">
<a id=\"ember282\" class=\"ember-view\">
<span>rnt-ayl-bld-js-jvr-frd-edw</span>
</a>
</div>
and the href property is not rendered,
I read that is something related to router but I'm not sure how to include the router in the test I tried something like:
moduleForComponent('conversation-item', 'Integration | Component | conversation item', {
integration: true,
setup(){
const router = this.lookup('router:main');
router.startRouting(true);
}
});
but the lookup function is not present
TypeError: 'undefined' is not a function (evaluating
'container.lookup('router:main')')
You might want to take a look at this question Ember Component Integration Tests: `link-to` href empty
Although it is 1.13 it might help you. Unfortunately I'm using ember-mocha and am still having problems.
Essentially, you're really close, you just need to use the container to look up the router.
// tests/helpers/setup-router.js
export default function({ container }) {
const router = container.lookup('router:main');
router.startRouting(true);
}
// tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import setupRouter from '../../helpers/setup-router';
moduleForComponent('my-component', 'Integration | Component | my component', {
integration: true,
setup() {
setupRouter(this);
}
});
I have two simple ember components; a list component and a list-item component. Data gets passed as an array to the list where it runs an each loop and makes a list-item component for each item in the array.
I'd like to, within the list-item component, take the data being passed to it from its parent list component and overwrite it. Eventually I would like to do more than just overwrite it and use it as a parameter in a function to return a new, parsed, value.
For the sake of example, lets say that this is a list of tweets.
Here is my code.
ROUTER.JS
import Ember from 'ember';
import config from './config/environment';
var Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('tweets');
});
export default Router;
TEMPLATES/TWEETS.HBS
{{tweet-list tweets=model}}
ROUTES/TWEETS.JS
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return[{
tweetText: "this is an example #request tweet text1 #user"
},{
tweetText: "tweet of the #text2 how #cool"
}, {
tweetText: "tweet toot took text3"
}];
}
});
COMPONENTS/TWEET-LIST/COMPONENT.JS
import Ember from 'ember';
export default Ember.Component.extend({
});
COMPONENTS/TWEET-LIST/TEMPLATE.HBS
<ul>
{{#each tweets as |tweet|}}
<li>{{tweet-item tweet=tweet}}</li>
{{/each}}
</ul>
COMPONENTS/TWEET-ITEM/COMPONENT.JS
import Ember from 'ember';
export default Ember.Component.extend({
// model(){
// return "over written value here"
// }
});
COMPONENTS/TWEET-ITEM/TEMPLATE.HBS
{{tweet.tweetText}} - <!-- {{overwritten value here}} -->
I believe I have to do the overwriting in the COMPONENTS/TWEET-ITEM/COMPONENT.JS file ? How do I go about overwriting or, even better, returning a new value based off of the data passed down from the parent component?
Use different component properties for given and overwritten tweets. For example:
// components/tweet-item/component.js
import Ember from 'ember';
export default Ember.Component.extend({
// given tweet
tweet: null,
// overwritten tweet
parsedTweet: Ember.computed('tweet', function() {
return {
tweetText: this.get('tweet').tweetText + ' #overwritten'
};
}),
// you may also modify given tweet here
// but the better approach to send action up
// in favor of data-down-action-up principe
actions: {
publish: function(tweet) {
this.sendAction('publish', tweet);
}
}
});
// components/tweet-item/template.hbs
tweetText: {{parsedTweet.tweetText}}
<button {{action 'publish' tweet}}> Publish </button>
// components/tweet-list/component.js
actions: {
publish: function(tweet) {
// your logic
}
}
// components/tweet-list/template.hbs
<ul>
{{#each tweets as |tweet|}}
<li>{{tweet-item tweet=tweet publish='publish'}}</li>
{{/each}}
</ul>