I am using angular-strap's modal and have built my own template. My issue is that my template has 2 buttons, however, I the text and functionality of these buttons will change. As a result, I would like to pass on that data to the modal before I opened it.
In the documentation it says
// Pre-fetch an external template populated with a custom scope
var myOtherModal = $modal({scope: $scope, template: 'modal/docs/modal.tpl.demo.html'});
However, i haven't been able to get it working. Note that I would only like some values passed to the modal scope, not my entire parent $scope (which is what the example seems to do)
It's not as straightforward as in ui-bootstrap but it looks more flexible:
// creates new isolated scope
var myNewScope = $scope.$new(true);
// then you can data to your new scope
myNewScope.users = ["User1", "User2", "User3"];
var myOtherModal = $modal({scope: myNewScope, template: 'modal/docs/modal.tpl.demo.html'});
Related
To retrieve prefixed ID of a control we can use view's method CreateID. However, there's no such a method in class sap.ui.core.Core. So how do I know viewID in the line
sap.ui.getCore().byId(viewID)
The Core is common to all the applications you are loading into your HTML document. An specific view of an specific application can have a different ID depending on how many different views you already loaded. The CreateID method makes sense to create an ID prefixed by the relevant View ID, but the view IDs are not prefixed but just autogenerated.
So, to get the ID of the current view, just do this.getView() in its controller and get the ID from the returned object.
If you want to know the ID of a different view to the one you are visualizing/loading, then you need to navigate through all the views in your DOM and design your own way to identify it.
However I don't recommend you to modify a View from a controller of another view. Each View should be modified by the logic in its controller only, and no by other controllers, to maintain the principles of the MVC architecture
EDIT:
How to access your component:
If you bind the context of your app execution with the Push.js script, then use this.getOwnerComponent().
If you don't, then you need to access your Component from the core with sap.ui.getCore().getComponent('componentID')
Now the problem is how to know the componentID. Well, I see two options here, give an ID to your component, or map the autogenerated component IDs in a Core variable.
How to retrieve a specific Component:
Option 1: Define a Component ID
Whenever you initialize it, usually in the index.html file, you can do it like this:
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.component({
id: "myComponentID",
async: true,
name: "myNamespace", // The namespace you defined in the bootstrap tag and points to the folder where the Component.js file is stored
manifestFirst: true
}).then(function(oNewComponent){
new sap.m.Shell({
app: new sap.ui.core.ComponentContainer({
component: oNewComponent,
height : "100%"
})
}).placeAt("content");
})
});
</script>
With this option you can retrieve your Component as sap.ui.getCore().getComponent('myComponentID') and of course whatever model registered in it with sap.ui.getCore().getComponent('myComponentID').getModel('modelName')
Option 2: Map the autogenerated components IDs with your identifiers in a Core variable
I don't like to change the Components ID, and there are some times when you have no chance to do it. So in the past I did it like in the following snippet (probably not the best/elegant solution out there, but it works good)
Component.js
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* #public
* #override
*/
init: function() {
//Map Component ID in a Core variable
if(!sap.ui.getCore()._data_oLoadedComponents){
sap.ui.getCore()._data_oLoadedComponents = {};
}
sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId = this.getId(); // myCustomComponentId is the ID you want to use for this specific app
// END: Map Component ID in a Core Variable
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// set the device model
this.setModel(models.createDeviceModel(), "device");
}
So now you can access any of your components getting the autogenerated ID from your mapping variable like this:
sap.ui.getCore().getComponent(sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId)
and also access the model like this:
sap.ui.getCore().getComponent(sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId).getModel('modelName')
Within the onBeforeRendering() function of a view how should I determine if a specific node is present in the model and modify the model to include the node if not? This question is about lazy loading of data to the model for data that will be infrequently accessed by users but would have a performance penalty if loaded with initial model data.
My use case has a list page leading to a detail page for whatever item in the list the use clicks. On the detail page is a set of tabs which expose sub-details related to the selected detail. Specifically a long text description of a the brief for a task.
The tab control leads via manifest.json style routing to display a view in the tabs content area.
This is my current working code which is within the onBeforeRendering event of the view controller:
onBeforeRendering: function(oEvent){
var sPath = this.getView().getBindingContext("Projects").getPath(); // something like /task/6
console.log('Path='+sPath)
var oModel = this.getView().getModel("Projects");
var oTask = oModel.getProperty(sPath + "/brief");
if (oTask) { // looks like /brief exists so must already have loaded the brief
// nothing to do
console.log('Use existing data')
}
else { // /brief not yet present so we need to load it up
console.log('Load new data')
oModel.setProperty(sPath + "/brief", "This is the brief...") // replace with loaddata() from server, use attachRequestCompleted to call function to modify model.
}}
Question - is this the correct approach?
Edit: Based on discussion in this question I modified my code to use an event that fires per display of the view. onBeforeRendering turned out to run without much apparent predictability - which I am sure it has but in any case I wanted a one-per-display event. Also, I fleshed out the code further but retained the basic structure and it appears to do what I wanted.
This is a valid approach. But you should think aboute following use case: What happens if the data you loaded have been changed at the backend? The JSONModel does not give you any support here as it acts dumb data store only.
Say I wrote a blog app in Sails.js.
On every page in this app, there is a sidebar widget called "Recent Posts", where it lists the titles of the 5 most recent posts and clicking on them takes you to the post in question.
Because this sidebar widget is present on every page, it should be in layout.ejs. But, here we have a conflict - dynamic content is only supposed to be pulled from the database in the controller action for rendering a specific view.
This dynamic content isn't for a specific view, it's for the whole site (via layout.ejs).
By the conventions that I understand, I'd have to get that dynamic content data for the sidebar widget in every controller action that renders a view (otherwise I would get an undefined error when I attempt to call that local in my layout.ejs file).
Things I've tried / considered:
Load that dynamic content in every controller action that renders a view (this solution is very bad) and calling that dynamic content in layout.ejs as if it were a local for the specific view. This works fine, but goes against D.R.Y. principles and quite frankly is a pain in the ass to have to run the same query to the database in every controller action.
As per another similar stackoverflow question, create a new config (E.G. config/globals.js), load my dynamic content from my database into that config file as a variable, and then calling sails.config.globals.[variable_name] in my layout.ejs file. This also worked, since apparently config variables are available everywhere in the application -- but it 's a hacky solution that I'm not a fan of (the content I'm loading is simply the titles and slugs of 5 recent posts, not a "global config option", as the solution implies).
Run the query to get the dynamic content inside the .EJS file directly between some <% %> tags. I'm not sure if this would work, but even if it did, it goes against the separation of concerns MVC principle and I'd like to avoid doing this if at all possible (if it even works).
As per a lengthy IRC discussion # http://webchat.freenode.net/?channels=sailsjs, it was suggested to create a policy and map that policy to all my controllers. In that policy, query the database for the 5 most recent posts, and set them to the req.recentposts. The problem with this solution is that, while the recent posts data will be passed to every controller, I still have to pass that req.recentposts data to my view -- making it so I still have to modify every single res.view({}) in every action. I don't have to have the database query in every action, which is good, but I still have to add a line of code to every action that renders a view... this isn't D.R.Y. and I'm looking for a better solution.
So, what is the proper solution, without needing to load that dynamic content in every controller action (a solution that adheres to D.R.Y. is what I'm lookng for), to get some dynamic content available to my layout.ejs file?
In folder /config you should create a file express.js and add something like that:
module.exports.express = {
customMiddleware: function(app){
app.use(function(req, res, next){
// or whatever query you need
Posts.find().limit(5).exec(function(err, posts){
res.locals.recentPosts = posts;
// remember about next()
next();
});
});
}
}
Then just make some simple loop in your view:
<% for(var i=0; i<recentPosts.length; i++) { %>
<% recentPosts[i].title %>
<% } %>
Here are some links to proper places in documentation:
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Configuration.md#express
and
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Response.md#reslocals
I found out another way to do this. What I did was to create a service that could render .ejs files to plain html by simply taking advantage of the ejs library already in sails. This service could either be invoked by the controller, or even passed as a function in the locals, and executed from within the .ejs. The service called TopNavBarService would look like:
var ejs = require('ejs');
exports.render = function() {
/* database finds goes here */
var userInfo = {
'username' : 'Kallehopp',
'real_name' : 'Kalle Hoppson'
};
var html = null;
ejs.renderFile('./views/topNavBar.ejs', {'locals':userInfo}, function(err, result) { html = result; });
return html;
}
In the constroller it could look like:
module.exports = {
testAction: function (req, res) {
return res.view('testView', {
renderNavbar: TopNavBarService.render // service function as a local!
});
}
};
This way you can create your customized ejs-helper that could even take arguments (although not shown here). When invoked, the helper could access the database and render a part of the html.
<div>
<%- renderNavbar() %>
</div>
I am trying to be as lazy loading as possible,
But, I am puzzle on how to start, here is my "sequenced comprehension":
Objective: Create a contacts page with contacts existing on the server
Step1.0: To use the router: the <div id="contacts"> must exists to trigger a rule, so I stepped back to (Step0.9),
Step0.9: Created this div in the body. Fine, the router find the #contacts, Oh, but this is a view, ok, stepped back to (Step0.8).
Step0.8: Erase the div created in the body and replace it by a view instead:
contactsView = Backbone.View.extend
tagName: 'div',
id: 'contacts'
To be lazy loading, this view should only be created when the #contact is trigger in my router table, but I just removed it from by body, it does exist anymore, I am back to Step1.0 ???
Some tutorials found, shows global variable settings... Please, how the general scenario using a router, a view, their models, and collection should proceed (no code is necessary for an answer, just one line for each steps) ?
I know there can be multiples ways, but what is the most common backbone step strategy to create elements.
Thanks in advance.
I'm not 100% sure I understood you correctly. If I didn't please let me know in the comments.
There seems to be some confusion in your question regarding the usage of Backbone.Router in general. When the router maps a route to URL fragment #contacts, that has nothing to do with a DOM element with the id #contacts. The hash sign simply happens to be the identifier for an URL fragment and id CSS selector, but that's where the similarity ends.
Typically my router looks something like this:
var AppRouter = Backbone.Router.extend({
routes: {
contacts: "contactList"
},
contactList: function() {
var contacts = new ContactCollection();
var view = new ContactListView({collection:contacts});
view.render().$el.appendTo("#contacts");
}
});
Notice that the #contacts element doesn't need to be called that. You can call it #pony, or you can render the view directly to the document body if you want.
So in these terms the workflow is:
Router gets hit
Collection is initialized
View is rendered
Usual way i do is
Have the div#contacts loaded within body
Router maps the #contacts to the method showContacts in the router
showContacts creates the view, attaches it to the desired div
var view = new contactsView();
$('#contacts').empty().append(view.el);
view.render();
You need not define the id in the definition of contactsView
I have a controller's method which renders a template.
This works fine to render the template within my .gsp view.
I am also using the mail-plugin, and I would like to used the same controller's function to render the template by email, hence populating some email with it.
I know how to do that from a .gsp view via Ajax request but do not know any way to do that from within a controller or a service.
The idea would be to use my controller's action more like a function, take the rendered teplate and populate my email with it.
Also, my controller's action needs to have some 'params' properties to work properly.
Any suggestion most welcome.
Regards,
You can use the render tag( http://grails.org/doc/latest/ref/Tags/render.html ) can be used to return a string.
I would move whatever logic you have in your controller that is reusable into a service, and then use this to return a model, then you can simply call this via:
def model = myService.method( ... )
def emailContent = g.render( template: 'mytemplate', model: model)