I am trying to get the router.navigateBack function working correctly using Durandal 2.0.1.
I have a viewmodel that can create an entity that we'll call lead. After creating a new lead I want to replace the uri so that it has the edit lead route instead of the create lead route and also replace it in the history. I call the following function after save:
router.navigate('#lead/' + vm.lead().id(), { replace: true, trigger: false });
here is the route description in my config file
{
route: 'lead(/:leadId)',
moduleId: 'lead/lead'
}
Then afterwards when I call
router.navigateBack();
It navigates to the expected route but always causes a full page refresh. I am trying to avoid the full page refresh and simply navigate as usual.
In durandal 1.0 I used to just call
router.replaceLocation();
To accomplish this same task and it would work well. I'm wondering if I'm missing something here.
Try the following:
var leadId = vm.lead.peek().id.peek();
router.navigate('#lead/' + leadId, { replace: true, trigger: false });
I believe your observable has a dependency that's causing a complete recalculation. We can use Knockout's peek() to obtain a value without creating a dependency.
Related
I'm using commitMode="Immediate" and I'm trying to disable my save button when any input is invalid.
What is the recommended approach to achieve this?
I understand that I can just set a variable when using "manual" mode from my component, but I can't find any event that represents a change in validity of preferably the complete Raddataform (otherwise of a single property) when using Immediate validation.
You can do this by listening for validation events and then updating your model.
From this example, add the propertyValidated listener:
<RadDataForm #propertyValidated="onPropertyValidated" ...></RadDataForm>
Then change your state:
methods: {
onPropertyValidated({ object, propertyName, entityProperty }) {
this.$refs.button.enabled = !entityProperty.isValid;
}
}
You will probably want to keep track of all validations in this case, or you could use the complete dataform.hasValidationErrors().
This is the NS-Vue solution, but totally applicable in Angular.
Add a #propertyValidated="onValidateForm" event listener that triggers on each validation. Then you can use hasValidationErrors() on the form to see if the form is valid. The only trick is that is has to be wrapped in a setTimeout(), like so:
onValidateForm(event) {
setTimeout(() => {
this.validated = !event.object.hasValidationErrors();
console.log("form valid: " + this.validated);
}, 100);
}
For a complete solution, see this playground.
Given a root component in index.html (my-app) containing iron-pages, and an iron-ajax call inside one or more of those pages, what is the best way for an iron-ajax on-response function in a child component to tell my-app to change the route? I am using Polymer 2. I see examples relying on links in different components, and calls in the same component, but no iron-ajax from one component to its parent.
In my-app.html I have an app-location and app-route, and the iron-pages:
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"></app-route>
<app-toolbar>...
<iron-pages role="main" selected="[[routeData.page]]"
attr-for-selected="name" selected-attribute="visible"
fallback-selection="404">
<my-login name="" route="[[subroute]]"></my-login>
<my-todos name="my-todos" route="[[subroute]]"></my-todos>
...
<my-404-warning name="404"></my-404-warning>
</iron-pages>
The user first sees my-login. When the iron-ajax in my-login call completes, I want to replace my-login with my-todos. I’ve come up with two approaches so far (see below). Both work--changing the page and updating the URL--but is one necessarily better? Is there a cleaner approach I’ve not found?
The iron-ajax on-response handler in my-login
_handleLogin(e) {
if (e.detail.response) {
let loginInfo = e.detail.response;
if (loginInfo.o_error) {
console.log('login error: '+loginInfo.o_error);
// ...
} else {
this.dispatch('login', loginInfo); // to polymer-redux store
// UPDATE PATH, OPTION #1
var page = this.ownerDocument.body.children[0];
page.set('route.path', 'server-catalog');
// UPDATE PATH, OPTION #2
this.dispatchEvent(new CustomEvent('change-route', {
bubbles: true, composed: true, detail: "my-todos" }));
// UPDATE PATH, OPTION #3..n
???
}
} else {
console.log(e.detail);
}
}
Both options trigger the observer, _routePageChanged(routeData.page), and so the magic proceeds.
Option #1 is straightforward, but involves reaching directly into the parent, my-app.
Option #2 relies on two additions to my-app:
ready() {
super.ready();
// Custom elements polyfill safe way to indicate an element has been upgraded.
this.removeAttribute('unresolved');
// listen for custom events
this.addEventListener('change-route', (e)=>this._onChangeRoute(e));
}
_onChangeRoute(e) {
this.set('route.path', e.detail);
}
Option #2 feels better, but I’m wondering if it’s the cleanest I can do.
It's recommended to pass routeData into <my-login> via data binding, and set routeData.page = "my-todos" in your _handleLogin()
I found a third way (and one I think I'll use) in the app-location docs: "app-location fires a location-changed event on window when it updates the location". So,
window.history.pushState({}, null, '/new_path');
window.dispatchEvent(new CustomEvent('location-changed'));
My EmberJS app has a couple of actions (triggered by buttons) that require a view/DOM manipulation as well as setting a state in the controller, followed by a model update. The way I do this, it does not appeal to my programming aesthetics. It gets the job done, but it doesn't look good :(
Here is a gist of how I do things :
<button {{action 'whatever' target='view'}}></button>
App.MyView = Ember.View.extend({
actions:{
whatever:function(){
var ctrl = this.get('controller');
ctrl.set('property',value); // arbitrary example of setting a controller property through it's view
ctrl.controllerMethod(); // invoking a controller method through the view
**// do some DOM manipulation**
}
}
});
Naturally, I can wrap whatever controller related steps I am performing in the view in a controller method and invoke that method through the view, but IMO that's just equally ugly. The view shouldn't really be invoking a controller method like how I have done. Unfortunately, this specific action requires a DOM manipulation as well as setting some state and performing an action in the controller.
I am not sure what is the recommended way of performing this. Can anyone enlighten ?
I suggest you handle the action from the controller. I noticed you're setting a property. You can use that to signal something to the view and then do the DOM manipulation within the view.
App.MyView = Ember.View.extend({
function () {
**// do some DOM manipulation**
}.observe('controller.property');
});
The way I think about it is that UI 'actions' are mapped to a business event (e.g. addClient instead of click), then as a result of that something happens that could change properties of the model, controller. As a result of those changes the view might need to update directly, ideally through a binding, but sometimes is needed to modify the DOM manually.
as #LukeMelia said in his comment you should really handle the changes in your controller and update your view (if you need to?) via databinding.
so, you would just omitt the target="view" argument from your view helper and Ember will look for the proper action in the nearest controller, bubbling all the way up to the route, and so on.
a simple code snippet (with what you provided in your first post) would look like:
Handlebars Template:
<button {{action someAction}}>Fire!</button>
Ember.Controller:
App.MyController = Ember.ObjectController.extend({
myProperty: 'cool',
printMyCoolness: function () {
console.log("I'm using Ember.js!");
},
actions: {
someAction: function () {
this.set('myProperty', 'set on fire!');
this.printMyCoolness();
}
}
});
I am using x-editable for in-line editing inside my web app. I would like to pass additional parameters to server, which I would like to read from data- attributes on trigger element. Here is my editable element:
Value
I would like to pass data-param attribute, but I don't know how to access trigger element. I tried via $(this).data('param'), but I get null... My full editable code:
$.fn.editable.defaults.mode = 'inline';
$('.editable').editable({
params: { param: $(this).data('param') }
});
Calling $('.editable').data('param') doesn't come into account since I have many .editable elements present.
Thanks
I figured it out. I'm answering in case somebody needs to know:
$('.editable').editable({
params: function(params) {
// add additional params from data-attributes of trigger element
params.param1 = $(this).editable().data('param');
params.param2 = $(this).editable().data('nextparam');
return params;
}
)
I am using backbone.js and trying to stay strict to the model-view-controller structure as I learn it. I have an onclick function for a link in one of my views that I am not sure where to put. Is the best place to keep this in the render function of the view?
Thanks
More specifically, the onclick performs a facebook login and then adds the user to my database if they are not currently in it. Don't know if this changes anything.
Here is what I think I will go with:
var NewUserView = Backbone.View.extend({
el: $('#window'),
render: function(){
// Render
this.listeners();
},
listeners: function(){
// onclick and other listeners
}
});
From the Backbone documentation:
In Backbone, the View class can also be thought of as a kind of
controller, dispatching events that originate from the UI, with the
HTML template serving as the true view. We call it a View because it
represents a logical chunk of UI, responsible for the contents of a
single DOM element.
Here's the general way to handle events in Backbone:
var NewUserView = Backbone.View.extend({
el: $('#window'),
render: function() {
// Render
},
events: {
"click #facebookButton": "loginViaFacebook"
},
loginViaFacebook: {
// Perform facebook login and add user to database
}
});
Where do you want the link to appear? On View page right? So , you should keep it in the same view on which you want the link to appear.
But , if you are building an architecture rather than just a web application, then you should put the onclick function in some different file where you will keep all these function and then import them in the view as required or keeping them in separate files and bundling them for import on view page.
Please make a file and write all the functions in that file and include that file in the your view file and use the onClick in the anchor tag. Please let me know if this make sense.