Angular.js $http $window.location and global variables - http

I'm trying to update a global variable within an $http request, redirect to a new page and then print that variable (on error). I'm not sure if i'm doing this correctly, but when I try to print my variable ($rootscope.adHocLevel2) it remains empty.
In js file
$scope.searchStep2 = function(option) {
$http.post($scope.url, { "option" : option}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
$rootScope.response = data;
})
.
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
$rootScope.response = 'Request failed';
$rootScope.routeAd = {"route": "../adHoc/adhoc.html"};
$rootScope.addHocLevel2 = {"suggestedCategory": "Car","suggestedLocLabel": "world","suggestedShadowURL": "http://www.toyota.com/auris/hybrid"};
$window.location=$rootScope.routeAd.route;
});
};
In Html I'm trying to print
<input type="text" ng-model="optLocation" value={{addHocLevel2}} />
Thanks ahead

Based on the above code, you have $window.location = $rootScope.routeAd.route which points to "../adHoc/adhoc.html". This will reload the app and therefore you lose addHocLevel2 value.
You will need to redirect to its hash route path (e.g. #/adhoc) - as per your routeProvider.when("/adhoc") config, rather than ../adHoc/adhoc.html.
Also I think the best way when moving within the angular's one page app is to use $location.path(yourRoutePath) rather than $window.location unless you want to change location entirely (new URL somewhere).
e.g
You have 2 routes setup:
app.config(function($routeProvider){
$routeProvider
.when('/main', { templateUrl:'/main/main.html',controller:'MainCtrl'})
.when('/adhoc', { templateUrl: '/adHoc/adhoc.html',controller:'AdHocCtrl});
}
Then say you want to redirect from /main to /adhoc, it goes something like this:
app.controller('MainCtrl',function($rootScope,$scope,$location,$http){
$http.post(...).error(function(data,status){
$rootScope.addHocLevel2 = {...};
$location.path("/adhoc"); // this will 'redirect' you to adHoc/adhoc.html page
}
})
Update: I updated your plunker: http://embed.plnkr.co/01lfzd4Q4bFoSPAB4WIF/
This is how I would do it - I'm sure there are another approaches. I use index.html as the template and have main.html and adhoc.html as the 2 other pages that you can switch to based on the URL.

Related

Trigger a button click from a URL

We need to scrape VEEC Website for the total number once a week.
As an example, for the week of 17/10/2016 - 23/10/2016 the URL returns the number Total 167,356 when the search button is clicked. We want this number to be stored in our database.
I'm using coldfusion to generate the weekly dates as params and have been passing them like the above URL. But I'm unable to find a query param so that the "Search" button click event is triggered.
I've tried like this & this but nothing seems to be working.
Any pointers?
It seems like for every form submission, a CRSF token is added, which prevents malicious activity. To make matters worse for you, the CRSF token is changed for each form submission, not just for each user, which makes it virtually impossible to circumvent.
When I make a CFHTTP POST request to this form, I get HTML FileContent back, but there is no DB data within the results table cell placeholders. It seems to me that the form owner allows form submission from an HTTP request, but if the CRSF token cannot be validated, no DB data is returned.
It maybe worth asking the website owner, if there is any kind of REST API, that you can hook into...
If you want to use a headless browser PhantomJS (https://en.wikipedia.org/wiki/PhantomJS) for this, here is a script that will save the total to a text file.
At command prompt, after you install PhantomJS, run phantomjs.exe main.js.
main.js
"use strict";
var firstLoad = true;
var url = 'https://www.veet.vic.gov.au/Public/PublicRegister/Search.aspx?CreatedFrom=17%2F10%2F2016&CreatedTo=23%2F10%2F2016';
var page = require("webpage").create();
page.viewportSize = {
width: 1280,
height: 800
};
page.onCallback = function (result) {
var fs = require('fs');
fs.write('veet.txt', result, 'w');
};
page.onLoadStarted = function () {
console.log("page.onLoadStarted, firstLoad", firstLoad);
};
page.onLoadFinished = function () {
console.log("page.onLoadFinished, firstLoad", firstLoad);
if (firstLoad) {
firstLoad = false;
page.evaluate(function () {
var event = document.createEvent("MouseEvents");
event.initEvent("click", true, true);
document.querySelectorAll(".dx-vam")[3].dispatchEvent(event);
});
} else {
page.evaluate(function () {
var element = document.querySelectorAll('.dxgv')[130];
window.callPhantom(element.textContent);
});
setTimeout(function () {
page.render('veet.png');
phantom.exit();
}, 3000);
}
};
page.open(url);
The script is not perfect, you can work on it if you're interested, but as is it will save the total to a file veet.txt and also save a screenshot veet.png.

Is there a way to update the URL in Flow Router without a refresh/redirect?

Is there a way to update a part of the URL reactively without using FlowRouter.go() while using React and react-layout?
I want to change the value in the document that is used to get the document from the DB. For example, if I have a route like ~/users/:username and update the username field in the document, I then have to user FlowRouter.go('profile', {data}) to direct the user to that new URL. The "old" route is gone.
Below is the working version I have, but there are two issues:
I have to use FlowRouter.go(), which is actually a full page refresh (and going back would be a 404).
I still get errors in the console because for a brief moment the reactive data for the component is actually wrong.
Relevant parts of the component are like this:
...
mixins: [ReactMeteorData],
getMeteorData() {
let data = {};
let users = Meteor.subscribe('user', {this.props.username});
if (user.ready())
data.user = user;
return data;
}
...
updateName(username) {
Users.update({_id:this.data.user._id}, {$set:{username}}, null, (e,r) => {
if (!e)
FlowRouter.go('profile', {username});
});
},
...
The route is like this:
FlowRouter.route('/users/:username', {
name: 'profile',
action(params) {
ReactLayout.render(Main, {content: <UserProfile {...params} />});
}
});
The errors I get in the console are:
Exception from Tracker recompute function:
and
TypeError: Cannot read property '_id' of undefined

Meteor client side collection needs to have all data populated before anything else

I'm trying to use a client side collection as a site configuration system. I insert documents representing my different pages, and the iron-router and navigation tabs all use them to determine what pages they are and what templates are represented by them. Each page uses a {{> contentTemplate}} inclusion helper to load it's relevant template.
It all works great, when the data has all loaded. When I restart the app on certain pages, the data hasn't loaded yet, and I receive the Exception from Deps recompute function: Error: Expected null or template in return value from inclusion function, found: undefined error.
Here's my javascript:
StoriesArray = [
{ category: 'teaching', contentTemplate: 'teachingHome', title: 'Teaching Home'},
...
];
Stories = new Meteor.Collection(null);
StoriesArray.forEach(function (story, index) {
story._id = index + '';
Stories.insert(story);
});
// in main.js
Template.teachingPost.contentTemplate = function() {
console.log(this);
console.log(this.contentTemplate);
return Template[this.contentTemplate];
};
// in router.js
this.route('teaching', {
layoutTemplate: 'teachingPost',
data: function() { return Stories.findOne({contentTemplate: 'teachingHome', category: 'teaching'}); }
});
The console logs in the contentTemplate helper above log twice, the first time as this:
Object {} main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:45
undefined main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:46
and the second time as this:
Object {category: "teaching", contentTemplate: "teachingHome", title: "Teaching Home"} main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:45
teachingHome main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:46
so the router is simply trying to load this data too early.
I've tried putting the StoriesArray loading process into different files all over my app, including lib, and even tried putting it into Meteor.startup, but it's always the same result.
The normal iron-router waitOn/subscription pattern doesn't really apply here, since this is a client side collection built with null, that has no server representation. I don't want this to have server representation, because this is static content that there's no need to go to my server for.
How do I ensure this information is done before continuing?
Untested, but per Iron Router's docs on waitOn:
Returning a subscription handle, or anything with a ready method from the waitOn function will add the handle to a wait list.
Also in general it's better to use find with data, rather than findOne, as find will return an empty cursor when the collection is empty as opposed to findOne returning undefined. So try this:
// in router.js
this.route('teaching', {
layoutTemplate: 'teachingPost',
data: function() {
return Stories.find({contentTemplate: 'teachingHome', category: 'teaching'});
},
waitOn: function() {
var handle = {};
handle.ready = function() {
if (Stories.find().count() !== 0)
return true;
else
return false;
}
return handle;
}
});
And adjust your Template.teachingPost.contentTemplate function to work with a cursor rather than an object.

Meteor.autorun() not working on client when insert occures

i have been knocking my head for 2 days now in that .
am creating a search engine, am creating queries dynamically using Meteor Framwork, the queries are working fine and when i search i can rebind the UI (Table in My Case) with the dynamic data query output.
however if an insert/update/delete operation occures the data object
and the UI (html Table) is not updating.
which means that the template is not re-rendered when the data object changes.
Template.search.rendered = function () {
Meteor.autorun(function() {
alarmsData = Alarms.find(getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val())).fetch()
console.log("rendered")
//alarmsData = Alarms.find({},{sort: {timestamp: "desc"} }).fetch();
searchControls(alarmsData)
getConsole(alarmsData, ".console")
$('#badge').html(alarmsData.length)
})
}
the get console function is just reading the array from teh search and creating an html table (this is working fine)
as for the begining i am creating a simple query as the default for my search. and then am changing this query whenever user changes the search criteria. i can notice that only the first instance of teh data object is kept and tracked for changes, so if the second search criteria resides within the first one, it's updating the UI, if not nothing happenes
i have used Meteor.autorun(function(){}) function however i traced it's execution with console.log and i can see it's no excuting when i insert data in the database for the same collection.
One, I believe you are trying to use Deps.autorun. Also, there is nothing in your autorun that seems to be dependent on a reactive source. Since alarmsData is taking a snapshot of data it won't care when Alarms has data changing.
Second, I would probably approach this with a redirect. I would compile my data, and redirect to the same page, allowing the server to handle the querying for me. This easily allows you to jump to this page from anywhere else with a prefilled query in the parameters (because the route would then handle it) and also gives a visual change to the navigation bar when a search has happened (just like every other search engine). You would do something like this on a button click:
var query = {},
path;
query.text = encodeURIComponent($('#searchTxt').val()),
query.start = encodeURIComponent($('#startTimeTxt').val()),
query.end = encodeURIComponent($('#endTimeTxt').val()),
// redirect to current path
path = Router.routes[Router.current().route.name].path({}, {
query: query
});
Router.go( path );
In your router you would just pass the query into your server and route as a data object (assuming you are using iron-router):
this.route( "search", {
path: "/search",
waitOn: function() {
return [
Meteor.subscribe( "searchAlarms", _.omit( this.params, "hash" ) ),
]
},
data: function () {
return { "query": _.omit( this.params, "hash" ) };
}
});
This will not only give you the query data that was used for the search (in your template) but the server can now handle the search for you! Your Alarms data now holds all of the documents needed to display to the user and you no longer need to subscribe to all your Alarms. This is also great because it is automatically reactive. So if a new Alarm matches your query filter it will automatically be passed down to the client and displayed to the user without needing to setup any extra dependencies/autoruns.
Note though, that if you are subscribing to Alarms elsewhere you will still need to do filtering client-side.
What a strange meteor code…
The "rendered" code method code is called once you will be rendering the search template
getSearchSelector($('#searchTxt').val() is not reactive, my advise is to use the session variable to put your search criteria inside and use this same session to inject the find parameters inside.
Are you looking for displaying all the alarms Data ?
function getAlarms()
{
var text = Session.get("text");
var from = Session.get("start");
var to = Session.get("end");
var filter = getSearchSelector(text, from, to);
return Alarms.find(filter);
}
Template.search.alarms = function () {
return getAlarms();
}
Template.search.alarmsCount = function () {
return getAlarms().count();
}
Template.search.events({
'keypress input[name=text]' : function(e,o)
{
var val = $("input[name= text]").val()
Session.set("text", val);
},
'keypress input[name=start]' : function(e,o)
{
var val = $("input[name=start]").val()
Session.set("start", val);
},
'keypress input[name=end]' : function(e,o)
{
var val = $("input[name=end]").val()
Session.set("end", val);
}
});
// And your template will look something like:
<template name="search">
Search alarms
<input type="text" name="text" placeholder="Enter your text here…"/>
<input type="text" name="start" placeholder="start time"/>
<input type="text" name="end" placeholder="end time/>
There is {{alarmsCount}} alarms(s);
{{#each alarms}}
Alarm object: {{.}}
{{/each}}
</template>
I Guess its Solved it by using Session.set & get, and automatically subscribing to the Serevr and send the dynamic Query.
Check the below Code
Template.zConsole.rendered = function () {
Session.set("obj", getSearchSelector($('#searchTxt').val(), $('#startTimeTxt').val(), $('#endTimeTxt').val()))
Deps.autorun(function (){
Meteor.subscribe("dynamicAlarms", Session.get("obj"))
console.log("Count from AutoRun ==> " + Alarms.find(Session.get("obj")).count())
})
}
on the server
Meteor.publish('dynamicAlarms',function (searchObj) {
return Alarms.find(searchObj)
})
& it works perfect with less code.

In Meteor, how to update one db field when another field changes?

General question: in Meteor, what's the best way to implement business logic that triggers whenever a model is updated -- e.g., for updating dependent fields or validations or...
Specific example: I'd like to add a "slug" field to Lists collection in the Meteor todos example. The slug needs to automatically update whenever a list's name is changed.
Here's what I've got... I'm observing every change to a list to see if its slug needs to be created/updated. This is in a shared models.js (runs server and client-side, to get the benefits of latency compensation):
// Lists -- {name: String}
Lists = new Meteor.Collection("lists");
var listsObserver = Lists.find().observe({
added: updateSlug,
changed: updateSlug
});
function updateSlug(doc, idx) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
Lists.update(doc._id, {$set: {slug: slug}});
}
}
(And as in the original todos example, server/publish.js publishes all of Lists.find() as "lists", and client/todos.js subscribes to that collection.)
The code above seems to work, but somehow doesn't look quite right to me. Questions:
Is observing the Lists collection like this a reasonable approach? It seems like
it could be inefficient -- any change to a Lists document will trigger this code.
Should I be doing a different (simulated) update client-side, or is it OK to let
this same Mongo/Minimongo update run on both?
Do I need to call listsObserver.stop() at some point to dispose the observer?
And if so, when?
(I'm just getting started with Meteor, so perhaps my biases from other environments are leaking through. The implied meta-question here is, am I even thinking about this problem in the right way?)
I would suggest using the Collection-Hooks package. It extends the collection operations with before and after hooks. This is better than having a lot of collection Observes or ObserveChanges, especially on the server where the overhead for collection observes can get very large.
This works on both the client and the server. If you implement it on the client you will get the benefit of updating the local collection (latency compensation) and the change will be pushed to the server so no need to do it again.
You also get the benefit of only doing one MongoDB operation instead of two or more like you would with observes or observeChanges.
You might use it like so:
var beforeInsertSlug = function(userId, doc) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
doc.slug = slug;
}
};
var beforeUpdateSlug = function(userId, doc, fieldNames, modifier, options){
if(modifier && modifier.$set && modifier.$set.doc && _.isString(modifier.$set.doc.name)){
var slug = (modifier.$set.doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + modifier.$set.doc.name + "' to " + slug);
modifier.$set.doc.slug = slug;
}
}
};
Lists.before.insert(beforeInsertSlug);
Lists.before.update(beforeUpdateSlug);
You can find the package here: https://atmospherejs.com/matb33/collection-hooks
I did a similar thing in server code. Basically put this code in Meteor.methods(), along with any other checks and updates you want making to the Lists Collection.
Although the code below looks a bit messy, and certainly hard to understand with the line starting with var slug:
Meteor.methods({
myupdate: function (doc) {
var slug = (doc.name || '').replace(/\W+/g, '-').toLowerCase();
if (slug !== doc.slug) {
console.log("Updating slug for '" + doc.name + "' to " + slug);
Lists.update(doc._id, {$set: {slug: slug}});
}
}
});
One way to implement this is to define a custom template function and trigger it in the template that is changing. For example:
In client.js
Template.myTemplate.custom_function_to_update = function() {
// do my update code. i.e. MyCollections.Update(...);
}
In the html file with the template
<template name="myTemplate">
<!-- Normal template code -->
{{ custom_function_to_update }}
</template>
and every time the template "myTemplate" updates, it will call your method.

Resources