full transition after updating query parameters in ember - ember-cli

I have the following route that run a report for specific date range:
export default Ember.Route.extend({
model: function(params){
return this.store.query('report' {
"report":"my_report",
"from":params.startDate,
"to":params.endDate
});
},
setupController: function(controller, model) {
this._super(controller, model);
// change format model and save it in rows
controller.set('model', rows);
}
});
Now, my controller is as follow:
export default Ember.Controller.extend({
queryParams:["startDate","endDate"],
actions:{
processReport:function(from,to){
this.transitionToRoute('reports',{
queryParams :{
"startDate":from,
"endDate":to,
"refreshModel": true
}
});
}
},
to:"",
from:""
});
The template is as follow:
From {{bootstrap-datepicker value=to}}
To {{bootstrap-datepicker value=from}}
<button {{action "processReport" from to}}>Process Report</button>
So, when I click the button the url changed and console show:
Attempting transition to reports ember.debug.js:52602
Transitioned into 'reports' ember.debug.js:27426
but the page remains same. How do I fully transition to the page?

You should add to your route the queryParams configuration to make a full transition
export default Ember.Route.extend({
queryParams: {
startDate: {
refreshModel: true
},
endDate: {
refreshModel: true
}
},
...
And on the controller you'll need to just update the values "startDate" and "endDate" in the custom action:
Export default Ember.Controller.extend({
queryParams:["startDate","endDate"],
actions:{
processReport:function(){
this.set('startDate', this.get('from'));
this.set('endDate', this.get('to'));
}
},
to:"",
from:""
});
This has been explained on the guides:
https://guides.emberjs.com/v2.11.0/routing/query-params/#toc_opting-into-a-full-transition

I figured it out on my own by reading the updated API on query-params in the following link:
https://guides.emberjs.com/v2.3.0/routing/query-params/

Related

Ag-Grid Link with link in the cell

I am building angular 4 app with ag-grid and I am having an issue with trying to figure out how to put a link in the cell. Can anybody help me with that issue?
Thanks
Please check this demo
cellRenderer: function(params) {
return ''+ params.value+''
}
In this demo, the cell value for the column 'city' is a hyperlink.
I struggled with this the other day and it was bit more complex than I first thought. I ended up with creating a renderer component to which I send in the link and that needed a bit on NgZone magic to work all the way. You can use it in your column definition like this:
cellRendererFramework: RouterLinkRendererComponent,
cellRendererParams: {
inRouterLink: '/yourlinkhere',
}
Component where inRouterLink is the link that you send in and params.value is the cell value. That means that you can route to your angular route that could look something like 'yourlink/:id'. You could also simplify this a bit if you don't want a more generic solution by not sending in the link and just hard coding the link in the template and not using the cellRendererParams.
import { Component, NgZone } from '#angular/core';
import { Router } from '#angular/router';
import { AgRendererComponent } from 'ag-grid-angular';
#Component({
template: '<a [routerLink]="[params.inRouterLink,params.value]" (click)="navigate(params.inRouterLink)">{{params.value}}</a>'
})
export class RouterLinkRendererComponent implements AgRendererComponent {
params: any;
constructor(
private ngZone: NgZone,
private router: Router) { }
agInit(params: any): void {
this.params = params;
}
refresh(params: any): boolean {
return false;
}
// This was needed to make the link work correctly
navigate(link) {
this.ngZone.run(() => {
this.router.navigate([link, this.params.value]);
});
}
}
And register it in
#NgModule({
imports: [
AgGridModule.withComponents([
RouterLinkRendererComponent,
])
],
})
UPDATE: I have written a blog post about this: https://medium.com/ag-grid/enhance-your-angular-grid-reports-with-formatted-values-and-links-34fa57ca2952
This is a bit dated, but it may help someone. The solution with typescript on Angular 5 is similar to what C.O.G has suggested.
In the component's typescript file, the column definition can contain a custom cell rendering function.
columnDefs = [
{headerName: 'Client', field: 'clientName' },
{headerName: 'Invoice Number', field: 'invoiceNumber',
cellRenderer: (invNum) =>
`<a href="/invoice/${invNum.value}" >${invNum.value}</a>` },
];
The lambda function is called while rendering the cell. The 'value' of the parameter that gets passed is what you can use to generate custom rendering.
Inspired by #Michael Karén
This is a improved version that is more flexible.
We can set what text to display in link
We can pass more than 2 routerLink parameters
Resolve routerLink according to data
Support target
Display text only if link is not applicable
And more if you wanted to add, just further edit this component
import { Component } from '#angular/core';
import { ICellRendererAngularComp } from 'ag-grid-angular';
export interface IRouterLinkRendererComponentOptions {
routerLinkParams?: any[];
linkDescription?: string;
textOnly?: string;
target?: string;
}
#Component({
template: `
<a *ngIf="params.textOnly == null; else textOnlyBlock"
[routerLink]="params.routerLinkParams"
[target]="params.target ? params.target : '_self'"
>
{{ params.linkDescription }}
</a>
<ng-template #textOnlyBlock>
{{ params.textOnly }}
</ng-template>
`
})
export class RouterLinkRendererComponent implements ICellRendererAngularComp {
params: IRouterLinkRendererComponentOptions;
agInit(params: any): void {
this.params = params.routerLinkRendererComponentOptions(params);
}
refresh(params: any): boolean {
return true;
}
}
So that we can dynamically resolve parameters and return text only if wanted in column definition by
{
...
cellRendererFramework: RouterLinkRendererComponent,
cellRendererParams: {
routerLinkRendererComponentOptions: (param): IRouterLinkRendererComponentOptions => {
if (param.data.dispatch_adjustment) {
return {
routerLinkParams: ['/adjustments', param.data.dispatch_adjustment.id, 'edit'],
linkDescription: '#' + param.data.dispatch_adjustment.id
};
} else {
return {
textOnly: '-'
};
}
}
},
...
},
Instead of using href in cellRenderer , it's better to use cellrenderer framework as route link works in it.
Another Disadvantage is if you use href then the entire angular application will reload again it changes the navigation state from imperative to popstate. The angular router works on the imperative state.
I had implemented something similar to Michael and Tom, with only [routerLink] and no (click) handler. But recently I started getting the dreaded warning:
Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?
After experimenting for awhile I found this post and added the navigate click handler function, which made the application start working again, however I found that the 'Navigation triggered outside Angular zone' message was still appearing in the logs.
So while the (click)="navigate()" call triggers the navigation inside the ngZone,the [routerLink] call is still being made, which bothered me. I really didn't want two attempts to navigate to happen - in case anything changed with a future API update.
I decided to replace the anchor tag with a span pseudoLink.
.pseudoLink {
color: blue;
text-decoration: underline;
cursor: pointer;
}
#Component({
template: '<span class="pseudoLink" (click)="navigate()">{{mytitle}}</span>'
})
navigate() {
this.ngZone.run(
() => {
console.log("LinkRendererComponent: navigate: (", this.mylink, ")");
this.router.navigate([this.mylink]);
}
);
}
this.mylink is defined in the agInit() method based on parameters passed in via cellRendererParams.
This works well for my main purpose which is to make the cell look like a link. Only thing I lost was the URL path popup in the browser status bar.
Hope this might help someone else.
Using a cell renderer is the correct solution but missing from the top answer is stopping the click event from reaching AgGrid:
cellRenderer: ({value}) => {
const a = document.createElement('a');
a.innerText = a.href = value;
a.target = '_blank';
// Prevent click from reaching AgGrid
a.addEventListener('click', event => { event.stopPropagation() });
return a;
}
If the click bubbles up to AgGrid it will cause row selection changes, etc if those are enabled.
I created a generic component that is usable for any link cell, uses no workarounds, and logs no warnings.
Usage
columnDefs = [
{
colId: 'My Column',
cellRendererFramework: AgGridLinkCellComponent,
cellRendererParams: {
// `text` and `link` both accept either an string expression (same as `field`) or a function that gets ICellRendererParams
text: 'title',
link: (params: ICellRendererParams) => `/my-path/${_.get(params, 'data.id')}`
}
}
]
Register the component in your AppModule:
imports: [
AgGridModule.withComponents([
AgGridLinkCellComponent
])
]
The component itself:
import * as _ from 'lodash';
import {Component} from '#angular/core';
import {AgRendererComponent} from 'ag-grid-angular';
import {ICellRendererParams} from 'ag-grid-community';
#Component({
selector: 'app-ag-grid-link-cell-component',
template: '<a [routerLink]="link">{{ text }}</a>',
})
export class AgGridLinkCellComponent implements AgRendererComponent {
link: string;
text: string;
constructor() {
}
agInit(params: ICellRendererParams): void {
this.refresh(params);
}
refresh(params: ICellRendererParams): boolean {
const dataParams = params.colDef.cellRendererParams;
this.link = _.isFunction(dataParams.link) ? dataParams.link(params) : _.get(params.data, dataParams.link);
this.text = _.isFunction(dataParams.text) ? dataParams.link(params) : _.get(params.data, dataParams.text);
return false;
}
}
We had this problem, and its not straightforward.
We ended up solving it in a different way as we use AdapTable on top of ag-Grid.
So we created an AdapTable Action Column and in the RenderFunction provided the link. That worked best for us as we didnt always want the Link to appear so we could use the ShouldRender function to decide whether or not we wanted to display link for each row.

Refresh custom control in sapui5 when model change

I've a custom control which have multiple properties inserted in Detail View page. I've binded data with these properties. Scenario is I've two pages one is list view and then detail view. I've to navBack from detail page and select diff product from main page.Detail view page show diff products detail according to selected product. everything works fine. but problem is that my custom control doesn't update values and other page have updated values.
<custom:product topic="" subTopic="{product>name}" id="productDetial"></custom:product>
I've used one methond this.getView().byId("productDetail").rerender(); but it doesn't update my Inner HTML of control.
the control code. might be some typos error.as I've changed some variables name and remove unwanted code. the purpose is to show the methods which I've used and how I did
sap.ui.define([
"sap/m/Label",
"sap/m/Button",
"sap/m/CustomTile"
], function(Label, Button, CustomTile) {
"use strict";
jQuery.sap.declare("testProduct.control.product");
return CustomTile.extend("testProduct.control.product", {
metadata: { // the Control API
properties: {
name: {
type: "string",
defaultValue: "--"
},
subTopic: {
type: "string",
defaultValue: "--"
}
}
},
init: function() {
},
rerender: function(oRM, oControl) {
},
renderer: function(oRM, oControl) {
oRM.write('<div class=" sapMTile customTileCourseDetail">');
oRM.write('<div class="leftTileYourScore">');
if (oControl.getSubTopic() !== "" && oControl.getSubTopic() !== undefined) {
oRM.writeEscaped(oControl.getSubTopic());
} else {
oRM.write(" ");
}
oRM.write('</div>');
oRM.write('</div>
}
});
});
Yo just need to add a setter function in you control. When the binding is refreshed/changes, UI5 will trigger a setter method specific to the property. So in you case for the property subTopic it expects a method setSubTopic. This method should define you own logic to update said property in the UI layer according to your needs.
Here is part of the code you need to add, you will also have to tweak the initial rendering logic a bit.
renderer: function (oRM, oControl) {
//oRM.write('<div class=" sapMTile customTileCourseDetail">');
oRM.write("<div")
oRM.writeControlData(oControl);
oRM.addClass("sapMTile customTileCourseDetail");
oRM.writeClasses();
oRM.write(">");
oRM.write('<div class="leftTileYourScore">');
if (oControl.getSubTopic() !== "" && oControl.getSubTopic() !== undefined) {
oRM.writeEscaped(oControl.getSubTopic());
} else {
oRM.write(" ");
}
oRM.write('</div>');
oRM.write('</div>');
},
setSubTopic: function(sText){
this.setProperty("subTopic", sText, true);
$("#"+this.sId+" .leftTileYourScore").html(sText);
}

react-datepicker with no initial value

I started using the react-datepicker component.
https://github.com/Hacker0x01/react-datepicker
I got the example running and now I want to adapt it that there is no initial value.
The example code is the following:
<script type="text/javascript">
var container = document.querySelector('#datepicker-container');
var exampleComponent = React.createClass({
displayName: 'exampleComponent',
getInitialState: function() {
return {
start_date: moment(),
};
},
handleStartDateChange: function(date) {
this.setState({
start_date: date
});
},
render: function() {
return React.DOM.div({}, [
DatePicker({
key: 'example1',
selected: this.state.start_date,
onChange: this.handleStartDateChange
}),
]);
}
})
React.renderComponent(exampleComponent(), container);
</script>
I tried to use selected: none or even leave selected out but then I get the following error:
TypeError: newProps.date is null
value: newProps.date.format(this.props.dateFormat)
I also looked into the source code, but didn't find any possibility to start with an empty date.
Thanks in advance for your help!
You should be able to achieve what you want by setting start_date: null in your getInitialState,
Alternatively, if you use example 3 from react-datepicker, you can define a function like this:
handleResetDate: function() {
this.setState({
new_date: null
});
},
, and call it whenever you want the date to reset.
Hope this helps!
Note: I'm using react-datepicker version 4.10
Setting start_date or selected to null didn't work for me, instead I simply changed the value for selected to "", as in selected={""}. That worked for me.

EmberJS: Observer Not Being Triggered on Computed Property

I am building a handelbars helper that renders a checkbox group. My goal is to display a checkbox group with something like this and get two-way binding on selectedOptions:
{{form-checkboxGroup options=allOptions selectedOptions=selectedOptions}}
I've used this pattern successfully with other form components and it's a big win. I'm able to render my allOptions and selectedOptions values as a checkbox group, but it's the two-way binding that's tripping me up. Any idea what I'm missing?
By the way, I'm using ember-cli, but that doesn't affect anything relating to this issue.
Here's my setup:
Handlebars Helper: helpers/form-checkbox-group.js
The sole purpose of this file is to link the Handelbars expression {{form-checkboxGroup}} to the view and template below.
import FormCheckboxGroupView from 'my-app/views/util/form/form-checkbox-group';
export default Ember.Handlebars.makeBoundHelper(function( options ) {
return Ember.Handlebars.helpers.view.call(this, FormCheckboxGroupView, options);
});
CheckboxGroup Handlebars Template: templates/util/form/form-checkbox-group.hbs
...
{{#each user in view.combinedOptions}}
{{input type="checkbox" name="view.fieldName" checked=user.checked }} {{user.name}}
{{/each}}
...
CheckboxGroup View: views/util/form/form-checkbox-group.js
...
export default FormCheckboxGroupView = Ember.View.extend( FormFieldMixin, {
templateName: 'util/form/form-checkbox-group',
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('view.combinedOptions.#each.checked'),
// combines the "options" and "selected options" into a single array of "combinedOptions" explicitly indicating what's checked
combinedOptions: function() {
...
// sample result of combinedOptions:
// { name: "Johnny Five", id: "12", checked: true }
return combinedOptions;
}.property('view.options', 'view.selectedOptions')
});
And finally, to actually use my Handlebars helper, here's the consuming page's template and corresponding controller:
Consuming Page: templates/my-page.hbs
{{form-checkboxGroup options=allUsersArray selectedOptions=selectedUsersArray fieldName="selectedProvidersArray" }}
Backing Controller for Consuming Page: controllers/my-page.js
export default MyPageController = Ember.Controller.extend( FormMixin, {
allUsersArray: [
{ name: 'Bill Huxtable', id: 'billy' },
{ name: 'Samantha Jones', id: 'jones' },
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
selectedUsersArray: [
{ name: 'Tony Pepperoni', id: 'tonyp' },
{ name: 'Ridonk Youliss', id: 'silly' }
],
...
});
So, all of this successfully renders the checkbox group nicely, but my efforts to capture the fact that a checkbox has been newly selected by using observes("view.combinedOptions.#each.checked') is not working.
Any idea on how I can this up for two-way binding? Thanks in advance for assistance!
No jsbin so I'm flying blind, but try this:
selectedOptions: function() {
console.log("When triggered this could update view.selectedOptions");
}.observes('combinedOptions.#each.checked')
view.property is how you access view from template. You don't need that from the view itself (unless you have view property on your view).

How to manage newly created objects without "saving" before transitioning to a new route it in emberjs?

I have an issue where I have a resource with a new route. When I transition to that new route I create a new object. On the form I have button to cancel, which removes that object. However, if I click a link on my navigation, say going back to the resource index, that object is there with whatever I put in the form. What's the best way of managing creating objects then moving away from the form?
My routes:
App.Router.map(function() {
this.resource('recipes', function() {
this.route('new');
this.route('show', { path: '/:recipe_id' });
});
this.resource('styles');
});
App.RecipesNewRoute = Ember.Route.extend({
model: function() {
return App.Recipe.createRecord({
title: '',
description: '',
instructions: ''
});
},
setupController: function(controller, model) {
controller.set('styles', App.Style.find());
controller.set('content', model);
}
});
My controller for the new route:
App.RecipesNewController = Ember.ObjectController.extend({
create: function() {
this.content.validate()
if(this.content.get('isValid')) {
this.transitionToRoute('recipes.show', this.content);
}
},
cancel: function() {
this.content.deleteRecord();
this.transitionToRoute('recipes.index');
},
buttonTitle: 'Add Recipe'
});
I'm using version 1.0.0.rc.1
Thanks!
Any code that you place in the deactivate method of your route will get executed every time you leave that route. The following code will delete the new model if the user hasn't explicitly saved it.
App.RecipesNewRoute = Ember.Route.extend({
// ...
deactivate: function() {
var controller = this.controllerFor('recipes.new');
var content = controller.get('content');
if (content && content.get('isNew') && !content.get('isSaving'))
content.deleteRecord();
},
// ...
});
As an added bonus, you now don't need to explicitly delete the record when the user presses the cancel button.