Vue.js weird thing with window property - javascript

When I init my vue.js 2.0 app I make window.User object.
That's all going well. Then in some components I'm using it in my
templates:
v-if="this.User.role == 2"
So in some component's this is working and in some not -_- (Cannot read property 'role' of undefined). How is that even possible? In the component's where this is not working I've tried this:
created() {
alert(window.User.role);
}
And the proper result shows up! But in the template it's not working. What could be going on here. Very very frustrating thing.
In my bootstrap.js:
window.User = Laravel.user;
I register all my components the same way:
Vue.component('corporations', require('./components/corporation/Corporations.vue'));

If User is acting like a global variable that you want access to in all components and you want it to be reactive, you can add it to the root Vue's data and then refer to it everywhere else as this.$root.User.
You can also use a mixin to declare a computed that makes it available on all components that use the mixin. Like this:
var mixin = {
computed: {
User: function () { return window.User; }
}
}
The mixin is probably a better route to go.

Related

Sharing an object from global vuejs

I would like to share a common url between different components in vuejs
So in my main.js
Vue.prototype.$apiurl = '127.0.0.1:8000/api'
Now in my component i would like to call
console.log("apiurl is", this.$apiurl );
The above doesnt work. How do access global variables in vuejs
Having a dictionary for keeping constants seems more appropriate to me in this case, something like this:
APP_CONSTS = {
API_URL: "127.0.0.1:8000/api"
}
Then accessing it whenever you need, like APP_CONSTS.API_URL.
In some situations you may need to share data between components, in these cases state management comes into play. But it may be an overkill for your case.

Can I force angular to give error if a component doesn't exist?

If I have a template in a component that references non-existant components, angular 1.6 seems perfectly happy to render it as nothing at all. For example I have a route currently that looks like:
when('/something',{
title: 'Something',
template: "<mycomponent></mycomponent>",
})
If I forget to register mycomponent on my application, this route renders nothing. Is there some mode I can use in angular that will cause a harder error in a case like that? Or at least print something to the console when it occurs?
To be perfectly clear, I have this issue with both top level components referenced by the router, as well as other child components that those reference recursively.
No, there is no option for that. By the way "non rendered" components are a benefit IMO, because you could override this slot later.
A short example:
when('/something',{
title: 'Something',
template: "<slot><mycomponent></mycomponent></slot>",
})
assume you want to override the ui-part of mycomponent, just define a component for "slot"
There was a routeProvider.otherwise before. Not sure if it’s still supported. I’m on a phone so limited. Let me know how it goes.
UI Router supports components for routes, this means that it is capable of triggering an error if a component doesn't exist.
AngularJS currently doesn't offer component router. This means that route template is compiled as any other template with $compile, and unknown selectors are just ignored.
It is possible to patch ngRoute ngView directive to additionally check if a route that is expected to route to a component actually has it compiled.
// the whole list can be retrieved from commonly available sources
var htmlTags = ['p', 'div', ...];
app.directive('ngView', function () {
return function($scope, $element, $attrs) {
if (!DEBUG_MODE)
return;
if ($element.children().length !== 1)
return;
var routeComponent = angular.element($element.children()[0]);
var routeComponentTagName = routeComponent.prop('tagName').toLowerCase();
if (htmlTags.indexOf(routeComponentTagName) >= 0)
return;
var routeComponentName = $attrs.$normalize(routeComponentTagName);
if (!routeComponent.controller(routeComponentName)) {
console.warn(routeComponentTagName + ' element is non-standard yet not a component');
}
}
});
AngularJS already has $normalize() which is primarily intended for attributes and strips some specific prefixes, but as long as the components names don't have x or data prefix, it can work as generic kebab to camel case transformer.
There may be other ways to detect if a component was compiled on child element. The code above will trigger false negative if there already is parent routeComponentName component, because controller() travels up the hierarchy.
And the proper way to handle this problem is to provide test coverage for the application. Router-related cases should likely be handled in integration or e2e tests.

Vue components with partial reusability

I'm working on a large app with legacy code. I've run into this issue twice now and am realizing there must be a better solution than what I've done to solve it. The issue is as follows.
There are 3 separate pages where I need to add very similar Vue functionality. However, these pages have significantly different HTML/Blade templates. Therefore, it's like I have to pass in separate HTML/Blade templates to the component in addition to component props.
I can kind of accomplish this using Vue inline-templates, which takes care of the significantly different HTML/Blade template problem.
However, the remaining issue is that I have 3 .js Vue components, one for each page. This would be fine, except the Vue code in each file is very similar.
It's also possible that at some point I will need to add more unique Vue code to each component, and would like to keep that possibility open.
What I would like to do is find a way to reuse the Vue code that is very similar in each component.
I have tried thinking of a way to nest the same child component within each of these 3 separate components, but I don't see how that would be possible due to the differences in the HTML/Blade in each file.
Any suggestions would be most appreciated, as I feel like I'm duplicating too much Vue code!
Thanks to user thanksd for providing the solution in the comments above. Mixins indeed were the way to go for me. That way, instead of this:
Vue.component('first-component', {
template: // something unique
methods : {
functionNumber1: function () {
// do something
},
});
Vue.component('second-component', {
template: // something totally different
methods : {
functionNumber1: function () {
// do same something
},
}
});
I can essentially do this:
const myMixin = {
methods : {
functionNumber1: function () {
// do same something
},
}
Vue.component('first-component', {
template: // something unique
mixins: ['myMixin']
});
Vue.component('second-component', {
template: // something totally different
mixins: ['myMixin']
});

looking up in store from a component

I have a template that includes a component.
// pods/workgroup/template.hbs
...
{{workgroup/member-add
wgId=model.id
store=store
peekUser2Workgroup=peekUser2Workgroup
}}
...
Within that component I need to lookup if something is already present in the store.
//somewhere in components/workgroup/member-add/componsent.js
let alreadyInStore = this.store.peekRecord('user2workgroup',u2wId);
I made it work by injecting the store into the component (as above), which of course is bad practise.
So I tried making a property in my parent-controller that does the store lookup:
//in components/workgroup/member-add/componsent.js
let alreadyInStore = this.get('controller').peekUser2Workgroup(u2wId);
//in pods/workgroup/controller.js
peekUser2Workgroup: function(u2wId) {
console.log(this);
console.log(this.store);
return this.store.peekRecord('user2workgroup',u2wId);
}
This works fine as long as I pass the complete store into the compentent as above.
However, if I don't pass the store to the component it get's undefined, although never accessed from the component directly (the store is present in the controller alone).
Logging into console of this gives me surprisingly the component, not the controller, this.store is undefined.
So I've learned, that with this I don't access the controller itself when a function/parameter gets called from outside/a component.
The question is, how can I make the controller to reference to itself with this?
Or how can I access the store when calling a parameter from outside?
Do I really need to pass the controller itself to himself??
like so:
// in component
let alreadyInStore = this.get('controller').peekUser2Workgroup(this.get('controller'), u2wgId);
//in controller
peekUser2Workgroup: function(myself, u2wId) {
console.log(this);
console.log(this.store);
return myself.store.peekRecord('user2workgroup',u2wId);
}
That seems very odd to me, and looks like I'm shifting around even more than I did initially when simply injecting the store to the controller...
Ember: 2.0.1
Ember-Data: 2.0.0
Instead of passing the store into the component as a property, inject it using Ember.service like this:
store: Ember.service.inject()
Then instead of passing in the function, just pass in the id vale you're looking up:
{{workgroup/member-add
wgId=model.id
}}
Now in your component you can fetch the record:
workgroup: function(){
return this.get('store').peekRecord('user2workgroup', this.get('wgId'));
}.property()

insert component without calling handlebars helper or a controller with outlet

I want to insert a component into controller template without using the handlebars helper (component "component-name"... or component-name). Or through a controller in an outlet (or as long as the solution works for a component that wants to insert another component, then it's fine, I don't think outlets work in components).
In other words:
App.IndexController = Ember.Controller.extend({
actions: {
insertComponent: function() {
var component = this.container.lookup("component:my-inserted", { singleton: false });
component.set("layoutName", "components/my-inserted");
// to be like handlebars-inserted component, what do i do here?
}
}
});
You can use test with this: http://emberjs.jsbin.com/popozanare/4/edit?html,js,output
Why?
Thinking of a way of to have clean modal syntax, such as the "openModal" syntax described in the Ember Cookbook: http://guides.emberjs.com/v1.10.0/cookbook/user_interface_and_interaction/using_modal_dialogs/.
The problem is that the source context is lost, as the modal is within the ApplicationRoute. I want the same syntax when calling a modal, but keeping the hierarchy. You can keep the hierarchy using https://github.com/yapplabs/ember-modal-dialog, which requires a mapping of variables... which i don't like either (but will likely implement if I have no other choice).
TD;LR: Want to open modal within the controller/component (context) that called it without scaffolding in the controller/component that called it (mapping variables, etc).
Edit:
On second thought, using a container view might be cleaner than mapping variables, found in this solution: http://jsbin.com/hahohi/1/edit?html,js,output. Still needs scaffolding though. Thanks #user3568719.
That cookbook is a bit outdated, but if you are looking for a "clean" way to handling modals in your app I would suggest named outlets.
Add it to your application or auth template {{outlet "modal"}} and when you want to bring up the modal you can catch the action on the corresponding route and then render into that named outlet like so:
this.render('your-desired-modal-template', {
into: 'auth',
outlet: 'modal'
});
And when you want to dismiss it simply disconnectOutlet like so:
this.disconnectOutlet({
outlet: 'modal',
parentView: 'auth'
});
This is the way we've been going about it, I m open to suggestions/better methods.

Categories

Resources